ofxgo/request.go

162 lines
4.6 KiB
Go
Raw Normal View History

2017-03-17 21:35:26 -04:00
package ofxgo
import (
"bytes"
"errors"
2017-04-17 10:54:20 -04:00
"fmt"
"github.com/aclindsa/go/src/encoding/xml"
"time"
2017-03-17 21:35:26 -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 {
URL string
2017-04-17 10:54:20 -04:00
Version ofxVersion // OFX version, overwritten in Client.Request()
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>
Prof []Message //<PROFMSGSETV1>
Image []Message //<IMAGEMSGSETV1>
2017-03-17 21:35:26 -04:00
indent bool // Whether to indent the marshaled XML
}
func marshalMessageSet(e *xml.Encoder, requests []Message, set messageType) error {
2017-03-17 21:35:26 -04:00
if len(requests) > 0 {
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 {
if request.Type() != set {
return errors.New("Expected " + set.String() + " message , found " + request.Type().String())
}
2017-03-17 21:35:26 -04:00
if ok, err := request.Valid(); !ok {
return err
}
if err := e.Encode(request); err != nil {
return err
}
}
if err := e.EncodeToken(messageSetElement.End()); err != nil {
return err
}
}
return nil
}
// SetClientFields overwrites the fields in this Request object controlled by
// the Client
func (oq *Request) SetClientFields(c *Client) {
oq.Signon.DtClient.Time = time.Now()
// Overwrite fields that the client controls
oq.Version = c.OfxVersion()
2017-04-12 21:40:42 -04:00
oq.Signon.AppID = c.ID()
oq.Signon.AppVer = c.Version()
oq.indent = c.IndentRequests()
}
// 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
switch oq.Version {
2017-04-17 10:54:20 -04:00
case OfxVersion102, OfxVersion103, OfxVersion151, OfxVersion160:
2017-03-17 21:35:26 -04:00
b.WriteString(`OFXHEADER:100
DATA:OFXSGML
2017-04-17 10:54:20 -04:00
VERSION:` + oq.Version.String() + `
2017-03-17 21:35:26 -04:00
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
`)
2017-04-17 10:54:20 -04:00
case OfxVersion200, OfxVersion201, OfxVersion202, OfxVersion203, OfxVersion210, OfxVersion211, OfxVersion220:
2017-03-17 21:35:26 -04:00
b.WriteString(`<?xml version="1.0" encoding="UTF-8" standalone="no"?>` + "\n")
2017-04-17 10:54:20 -04:00
b.WriteString(`<?OFX OFXHEADER="200" VERSION="` + oq.Version.String() + `" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?>` + "\n")
2017-03-17 21:35:26 -04:00
default:
2017-04-17 10:54:20 -04:00
return nil, fmt.Errorf("%d is not a valid OFX version string", oq.Version)
2017-03-17 21:35:26 -04:00
}
encoder := xml.NewEncoder(&b)
if oq.indent {
encoder.Indent("", " ")
}
ofxElement := xml.StartElement{Name: xml.Name{Local: "OFX"}}
if err := encoder.EncodeToken(ofxElement); err != nil {
return nil, err
}
if ok, err := oq.Signon.Valid(); !ok {
return nil, err
}
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
}
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},
{oq.Prof, ProfRq},
{oq.Image, ImageRq},
}
for _, set := range messageSets {
if err := marshalMessageSet(encoder, set.Messages, set.Type); err != nil {
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
}