Reorganization

This commit is contained in:
Aaron Lindsay 2017-03-17 21:35:26 -04:00
parent add6036729
commit 8158868432
6 changed files with 173 additions and 150 deletions

View File

@ -5,15 +5,6 @@ import (
"github.com/golang/go/src/encoding/xml"
)
type BankAcct struct {
XMLName xml.Name // BANKACCTTO or BANKACCTFROM
BankId String `xml:"BANKID"`
BranchId String `xml:"BRANCHID,omitempty"` // Unused in USA
AcctId String `xml:"ACCTID"`
AcctType String `xml:"ACCTTYPE"` // One of CHECKING, SAVINGS, MONEYMRKT, CREDITLINE, CD
AcctKey String `xml:"ACCTKEY,omitempty"` // Unused in USA
}
type StatementRequest struct {
XMLName xml.Name `xml:"STMTTRNRQ"`
TrnUID UID `xml:"TRNUID"`

27
common.go Normal file
View File

@ -0,0 +1,27 @@
package ofxgo
import (
"github.com/golang/go/src/encoding/xml"
)
// Represents a top-level OFX message set (i.e. BANKMSGSRSV1)
type Message interface {
Name() string // The name of the OFX element this set represents
Valid() (bool, error) // Called before a Message is marshaled and after
// it's unmarshaled to ensure the request or response is valid
}
type BankAcct struct {
XMLName xml.Name // BANKACCTTO or BANKACCTFROM
BankId String `xml:"BANKID"`
BranchId String `xml:"BRANCHID,omitempty"` // Unused in USA
AcctId String `xml:"ACCTID"`
AcctType String `xml:"ACCTTYPE"` // One of CHECKING, SAVINGS, MONEYMRKT, CREDITLINE, CD
AcctKey String `xml:"ACCTKEY,omitempty"` // Unused in USA
}
type CCAcct struct {
XMLName xml.Name // CCACCTTO or CCACCTFROM
AcctId String `xml:"ACCTID"`
AcctKey String `xml:"ACCTKEY,omitempty"` // Unused in USA
}

121
request.go Normal file
View File

@ -0,0 +1,121 @@
package ofxgo
import (
"bytes"
"errors"
"github.com/golang/go/src/encoding/xml"
)
type Request struct {
URL string
Version string // OFX version string, overwritten in Client.Request()
Signon SignonRequest //<SIGNONMSGSETV1>
Signup []Message //<SIGNUPMSGSETV1>
Banking []Message //<BANKMSGSETV1>
//<CREDITCARDMSGSETV1>
//<LOANMSGSETV1>
//<INVSTMTMSGSETV1>
//<INTERXFERMSGSETV1>
//<WIREXFERMSGSETV1>
//<BILLPAYMSGSETV1>
//<EMAILMSGSETV1>
//<SECLISTMSGSETV1>
//<PRESDIRMSGSETV1>
//<PRESDLVMSGSETV1>
Profile []Message //<PROFMSGSETV1>
//<IMAGEMSGSETV1>
indent bool // Whether to indent the marshaled XML
}
func marshalMessageSet(e *xml.Encoder, requests []Message, setname string) error {
if len(requests) > 0 {
messageSetElement := xml.StartElement{Name: xml.Name{Local: setname}}
if err := e.EncodeToken(messageSetElement); err != nil {
return err
}
for _, request := range requests {
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
}
func (oq *Request) Marshal() (*bytes.Buffer, error) {
var b bytes.Buffer
// Write the header appropriate to our version
switch oq.Version {
case "102", "103", "151", "160":
b.WriteString(`OFXHEADER:100
DATA:OFXSGML
VERSION:` + oq.Version + `
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
`)
case "200", "201", "202", "203", "210", "211", "220":
b.WriteString(`<?xml version="1.0" encoding="UTF-8" standalone="no"?>` + "\n")
b.WriteString(`<?OFX OFXHEADER="200" VERSION="` + oq.Version + `" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?>` + "\n")
default:
return nil, errors.New(oq.Version + " is not a valid OFX version string")
}
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: "SIGNONMSGSRQV1"}}
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
}
if err := marshalMessageSet(encoder, oq.Signup, "SIGNUPMSGSRQV1"); err != nil {
return nil, err
}
if err := marshalMessageSet(encoder, oq.Banking, "BANKMSGSRQV1"); err != nil {
return nil, err
}
if err := marshalMessageSet(encoder, oq.Profile, "PROFMSGSRQV1"); err != nil {
return nil, err
}
if err := encoder.EncodeToken(ofxElement.End()); err != nil {
return nil, err
}
if err := encoder.Flush(); err != nil {
return nil, err
}
return &b, nil
}

View File

@ -9,125 +9,6 @@ import (
"strings"
)
type Message interface {
Name() string
Valid() (bool, error)
}
type Request struct {
URL string
Version string // OFX version string, overwritten in Client.Request()
Signon SignonRequest //<SIGNONMSGSETV1>
Signup []Message //<SIGNUPMSGSETV1>
Banking []Message //<BANKMSGSETV1>
//<CREDITCARDMSGSETV1>
//<LOANMSGSETV1>
//<INVSTMTMSGSETV1>
//<INTERXFERMSGSETV1>
//<WIREXFERMSGSETV1>
//<BILLPAYMSGSETV1>
//<EMAILMSGSETV1>
//<SECLISTMSGSETV1>
//<PRESDIRMSGSETV1>
//<PRESDLVMSGSETV1>
Profile []Message //<PROFMSGSETV1>
//<IMAGEMSGSETV1>
indent bool // Whether to indent the marshaled XML
}
func marshalMessageSet(e *xml.Encoder, requests []Message, setname string) error {
if len(requests) > 0 {
messageSetElement := xml.StartElement{Name: xml.Name{Local: setname}}
if err := e.EncodeToken(messageSetElement); err != nil {
return err
}
for _, request := range requests {
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
}
func (oq *Request) Marshal() (*bytes.Buffer, error) {
var b bytes.Buffer
// Write the header appropriate to our version
switch oq.Version {
case "102", "103", "151", "160":
b.WriteString(`OFXHEADER:100
DATA:OFXSGML
VERSION:` + oq.Version + `
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
`)
case "200", "201", "202", "203", "210", "211", "220":
b.WriteString(`<?xml version="1.0" encoding="UTF-8" standalone="no"?>` + "\n")
b.WriteString(`<?OFX OFXHEADER="200" VERSION="` + oq.Version + `" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?>` + "\n")
default:
return nil, errors.New(oq.Version + " is not a valid OFX version string")
}
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: "SIGNONMSGSRQV1"}}
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
}
if err := marshalMessageSet(encoder, oq.Signup, "SIGNUPMSGSRQV1"); err != nil {
return nil, err
}
if err := marshalMessageSet(encoder, oq.Banking, "BANKMSGSRQV1"); err != nil {
return nil, err
}
if err := marshalMessageSet(encoder, oq.Profile, "PROFMSGSRQV1"); err != nil {
return nil, err
}
if err := encoder.EncodeToken(ofxElement.End()); err != nil {
return nil, err
}
if err := encoder.Flush(); err != nil {
return nil, err
}
return &b, nil
}
type Response struct {
Version string // String for OFX header, defaults to 203
Signon SignonResponse //<SIGNONMSGSETV1>
@ -208,22 +89,6 @@ func (or *Response) readSGMLHeaders(r *bufio.Reader) error {
return nil
}
func nextNonWhitespaceToken(decoder *xml.Decoder) (xml.Token, error) {
for {
tok, err := decoder.Token()
if err != nil {
return nil, err
} else if chars, ok := tok.(xml.CharData); ok {
strippedBytes := bytes.TrimSpace(chars)
if len(strippedBytes) != 0 {
return tok, nil
}
} else {
return tok, nil
}
}
}
func (or *Response) readXMLHeaders(decoder *xml.Decoder) error {
var tok xml.Token
tok, err := nextNonWhitespaceToken(decoder)

View File

@ -61,12 +61,6 @@ func (bai *BankAcctInfo) String() string {
return fmt.Sprintf("%+v", *bai)
}
type CCAcct struct {
XMLName xml.Name // CCACCTTO or CCACCTFROM
AcctId String `xml:"ACCTID"`
AcctKey String `xml:"ACCTKEY,omitempty"` // Unused in USA
}
type CCAcctInfo struct {
XMLName xml.Name `xml:"CCACCTINFO"`
CCAcctFrom CCAcct `xml:"CCACCTFROM"`

25
util.go Normal file
View File

@ -0,0 +1,25 @@
package ofxgo
import (
"bytes"
"github.com/golang/go/src/encoding/xml"
)
// Returns the next available Token from the xml.Decoder that is not CharData
// made up entirely of whitespace. This is useful to skip whitespace when
// manually unmarshaling XML.
func nextNonWhitespaceToken(decoder *xml.Decoder) (xml.Token, error) {
for {
tok, err := decoder.Token()
if err != nil {
return nil, err
} else if chars, ok := tok.(xml.CharData); ok {
strippedBytes := bytes.TrimSpace(chars)
if len(strippedBytes) != 0 {
return tok, nil
}
} else {
return tok, nil
}
}
}