mirror of
https://github.com/aclindsa/ofxgo.git
synced 2024-10-30 05:20:05 -04:00
Reorganization
This commit is contained in:
parent
add6036729
commit
8158868432
@ -5,15 +5,6 @@ import (
|
|||||||
"github.com/golang/go/src/encoding/xml"
|
"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 {
|
type StatementRequest struct {
|
||||||
XMLName xml.Name `xml:"STMTTRNRQ"`
|
XMLName xml.Name `xml:"STMTTRNRQ"`
|
||||||
TrnUID UID `xml:"TRNUID"`
|
TrnUID UID `xml:"TRNUID"`
|
||||||
|
27
common.go
Normal file
27
common.go
Normal 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
121
request.go
Normal 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
|
||||||
|
}
|
@ -9,125 +9,6 @@ import (
|
|||||||
"strings"
|
"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 {
|
type Response struct {
|
||||||
Version string // String for OFX header, defaults to 203
|
Version string // String for OFX header, defaults to 203
|
||||||
Signon SignonResponse //<SIGNONMSGSETV1>
|
Signon SignonResponse //<SIGNONMSGSETV1>
|
||||||
@ -208,22 +89,6 @@ func (or *Response) readSGMLHeaders(r *bufio.Reader) error {
|
|||||||
return nil
|
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 {
|
func (or *Response) readXMLHeaders(decoder *xml.Decoder) error {
|
||||||
var tok xml.Token
|
var tok xml.Token
|
||||||
tok, err := nextNonWhitespaceToken(decoder)
|
tok, err := nextNonWhitespaceToken(decoder)
|
@ -61,12 +61,6 @@ func (bai *BankAcctInfo) String() string {
|
|||||||
return fmt.Sprintf("%+v", *bai)
|
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 {
|
type CCAcctInfo struct {
|
||||||
XMLName xml.Name `xml:"CCACCTINFO"`
|
XMLName xml.Name `xml:"CCACCTINFO"`
|
||||||
CCAcctFrom CCAcct `xml:"CCACCTFROM"`
|
CCAcctFrom CCAcct `xml:"CCACCTFROM"`
|
||||||
|
25
util.go
Normal file
25
util.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user