2017-03-17 21:35:26 -04:00
|
|
|
package ofxgo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
2017-10-02 09:21:56 -04:00
|
|
|
"github.com/aclindsa/xml"
|
2017-03-28 20:42:22 -04:00
|
|
|
"time"
|
2017-03-17 21:35:26 -04:00
|
|
|
)
|
|
|
|
|
2017-03-31 15:29:57 -04:00
|
|
|
// Request is the top-level object marshalled and sent to OFX servers. It is
|
|
|
|
// constructed by appending one or more request objects to the message set they
|
|
|
|
// correspond to (i.e. appending StatementRequest to Request.Bank to get a bank
|
|
|
|
// statemement). If a *Request object is appended to the wrong message set, an
|
|
|
|
// error will be returned when Marshal() is called on this Request.
|
2017-03-17 21:35:26 -04:00
|
|
|
type Request struct {
|
2017-03-31 11:54:43 -04:00
|
|
|
URL string
|
2017-04-17 10:54:20 -04:00
|
|
|
Version ofxVersion // OFX version, overwritten in Client.Request()
|
2017-03-31 11:54:43 -04:00
|
|
|
Signon SignonRequest //<SIGNONMSGSETV1>
|
|
|
|
Signup []Message //<SIGNUPMSGSETV1>
|
|
|
|
Bank []Message //<BANKMSGSETV1>
|
|
|
|
CreditCard []Message //<CREDITCARDMSGSETV1>
|
|
|
|
Loan []Message //<LOANMSGSETV1>
|
|
|
|
InvStmt []Message //<INVSTMTMSGSETV1>
|
|
|
|
InterXfer []Message //<INTERXFERMSGSETV1>
|
|
|
|
WireXfer []Message //<WIREXFERMSGSETV1>
|
|
|
|
Billpay []Message //<BILLPAYMSGSETV1>
|
|
|
|
Email []Message //<EMAILMSGSETV1>
|
|
|
|
SecList []Message //<SECLISTMSGSETV1>
|
|
|
|
PresDir []Message //<PRESDIRMSGSETV1>
|
|
|
|
PresDlv []Message //<PRESDLVMSGSETV1>
|
2017-04-03 19:50:16 -04:00
|
|
|
Prof []Message //<PROFMSGSETV1>
|
2017-03-31 11:54:43 -04:00
|
|
|
Image []Message //<IMAGEMSGSETV1>
|
2017-03-17 21:35:26 -04:00
|
|
|
|
2019-06-14 01:40:22 -04:00
|
|
|
indent bool // Whether to indent the marshaled XML
|
|
|
|
carriageReturn bool // Whether to user carriage returns in new lines for marshaled XML
|
2017-03-17 21:35:26 -04:00
|
|
|
}
|
|
|
|
|
2019-03-01 05:51:36 -05:00
|
|
|
func encodeMessageSet(e *xml.Encoder, requests []Message, set messageType, version ofxVersion) error {
|
2017-03-17 21:35:26 -04:00
|
|
|
if len(requests) > 0 {
|
2017-03-31 09:25:07 -04:00
|
|
|
messageSetElement := xml.StartElement{Name: xml.Name{Local: set.String()}}
|
2017-03-17 21:35:26 -04:00
|
|
|
if err := e.EncodeToken(messageSetElement); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, request := range requests {
|
2017-03-31 09:25:07 -04:00
|
|
|
if request.Type() != set {
|
|
|
|
return errors.New("Expected " + set.String() + " message , found " + request.Type().String())
|
|
|
|
}
|
2017-04-17 11:11:46 -04:00
|
|
|
if ok, err := request.Valid(version); !ok {
|
2017-03-17 21:35:26 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := e.Encode(request); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.EncodeToken(messageSetElement.End()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-04-13 10:18:07 -04:00
|
|
|
// SetClientFields overwrites the fields in this Request object controlled by
|
|
|
|
// the Client
|
2018-10-02 20:55:25 -04:00
|
|
|
func (oq *Request) SetClientFields(c Client) {
|
2017-04-04 05:45:19 -04:00
|
|
|
oq.Signon.DtClient.Time = time.Now()
|
2017-03-28 20:42:22 -04:00
|
|
|
|
|
|
|
// Overwrite fields that the client controls
|
|
|
|
oq.Version = c.OfxVersion()
|
2017-04-12 21:40:42 -04:00
|
|
|
oq.Signon.AppID = c.ID()
|
2017-03-28 20:42:22 -04:00
|
|
|
oq.Signon.AppVer = c.Version()
|
|
|
|
oq.indent = c.IndentRequests()
|
2019-06-14 01:40:22 -04:00
|
|
|
oq.carriageReturn = c.CarriageReturnNewLines()
|
2017-03-28 20:42:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Marshal this Request into its SGML/XML representation held in a bytes.Buffer
|
|
|
|
//
|
|
|
|
// If error is non-nil, this bytes.Buffer is ready to be sent to an OFX server
|
2017-03-17 21:35:26 -04:00
|
|
|
func (oq *Request) Marshal() (*bytes.Buffer, error) {
|
|
|
|
var b bytes.Buffer
|
|
|
|
|
|
|
|
// Write the header appropriate to our version
|
2019-06-14 01:40:22 -04:00
|
|
|
writeHeader(&b, oq.Version, oq.carriageReturn)
|
2017-03-17 21:35:26 -04:00
|
|
|
|
|
|
|
encoder := xml.NewEncoder(&b)
|
|
|
|
if oq.indent {
|
|
|
|
encoder.Indent("", " ")
|
|
|
|
}
|
2019-06-14 01:40:22 -04:00
|
|
|
if oq.carriageReturn {
|
|
|
|
encoder.CarriageReturn(true)
|
|
|
|
}
|
2019-06-14 21:20:08 -04:00
|
|
|
if oq.Version < OfxVersion200 {
|
|
|
|
// OFX 100 series versions should avoid element close tags for compatibility
|
|
|
|
encoder.SetDisableAutoClose(ofxLeafElements...)
|
|
|
|
}
|
2017-03-17 21:35:26 -04:00
|
|
|
|
|
|
|
ofxElement := xml.StartElement{Name: xml.Name{Local: "OFX"}}
|
|
|
|
|
|
|
|
if err := encoder.EncodeToken(ofxElement); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-04-17 11:11:46 -04:00
|
|
|
if ok, err := oq.Signon.Valid(oq.Version); !ok {
|
2017-03-17 21:35:26 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2017-03-31 09:25:07 -04:00
|
|
|
signonMsgSet := xml.StartElement{Name: xml.Name{Local: SignonRq.String()}}
|
2017-03-17 21:35:26 -04:00
|
|
|
if err := encoder.EncodeToken(signonMsgSet); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := encoder.Encode(&oq.Signon); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := encoder.EncodeToken(signonMsgSet.End()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-03-31 11:54:43 -04:00
|
|
|
messageSets := []struct {
|
|
|
|
Messages []Message
|
|
|
|
Type messageType
|
|
|
|
}{
|
|
|
|
{oq.Signup, SignupRq},
|
|
|
|
{oq.Bank, BankRq},
|
|
|
|
{oq.CreditCard, CreditCardRq},
|
|
|
|
{oq.Loan, LoanRq},
|
|
|
|
{oq.InvStmt, InvStmtRq},
|
|
|
|
{oq.InterXfer, InterXferRq},
|
|
|
|
{oq.WireXfer, WireXferRq},
|
|
|
|
{oq.Billpay, BillpayRq},
|
|
|
|
{oq.Email, EmailRq},
|
|
|
|
{oq.SecList, SecListRq},
|
|
|
|
{oq.PresDir, PresDirRq},
|
|
|
|
{oq.PresDlv, PresDlvRq},
|
2017-04-03 19:50:16 -04:00
|
|
|
{oq.Prof, ProfRq},
|
2017-03-31 11:54:43 -04:00
|
|
|
{oq.Image, ImageRq},
|
|
|
|
}
|
|
|
|
for _, set := range messageSets {
|
2019-03-01 05:51:36 -05:00
|
|
|
if err := encodeMessageSet(encoder, set.Messages, set.Type, oq.Version); err != nil {
|
2017-03-31 11:54:43 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2017-03-17 21:35:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := encoder.EncodeToken(ofxElement.End()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := encoder.Flush(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &b, nil
|
|
|
|
}
|