From f185d78d29bd8c0e06c4dc3a466f681c1662c0d6 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Fri, 31 Mar 2017 11:54:43 -0400 Subject: [PATCH] Generalize response parsing code This removes the many decodeXXXMessageSet() functions and replaces them with a large map and a single generic decodeMessageSet() function. Also change Responses to satisfy the Message interface as pointer types (instead of the raw types), add the full set of top-level message sets (though most of them still lack any message-parsing ability), adjust the message set names to more closely mirror their OFX names, and fixup tests and the command-line client to match the above changes. --- banking.go | 33 +------- banking_test.go | 4 +- cmd/ofx/bankdownload.go | 2 +- cmd/ofx/banktransactions.go | 6 +- cmd/ofx/ccdownload.go | 2 +- cmd/ofx/cctransactions.go | 6 +- cmd/ofx/get_accounts.go | 2 +- cmd/ofx/invdownload.go | 2 +- cmd/ofx/invtransactions.go | 6 +- creditcards.go | 33 +------- investments.go | 32 +------- profile.go | 32 +------- request.go | 72 +++++++++-------- response.go | 157 +++++++++++++++++++++--------------- response_test.go | 2 +- securities.go | 44 ++-------- signup.go | 35 +------- signup_test.go | 2 +- 18 files changed, 171 insertions(+), 301 deletions(-) diff --git a/banking.go b/banking.go index fa9f453..6ef0deb 100644 --- a/banking.go +++ b/banking.go @@ -1,7 +1,6 @@ package ofxgo import ( - "errors" "github.com/aclindsa/go/src/encoding/xml" ) @@ -150,41 +149,15 @@ type StatementResponse struct { MktgInfo String `xml:"STMTRS>MKTGINFO,omitempty"` // Marketing information } -func (sr StatementResponse) Name() string { +func (sr *StatementResponse) Name() string { return "STMTTRNRS" } -func (sr StatementResponse) Valid() (bool, error) { +func (sr *StatementResponse) Valid() (bool, error) { //TODO implement return true, nil } -func (sr StatementResponse) Type() messageType { +func (sr *StatementResponse) Type() messageType { return BankRs } - -func decodeBankingMessageSet(d *xml.Decoder, start xml.StartElement) ([]Message, error) { - var msgs []Message - for { - tok, err := nextNonWhitespaceToken(d) - if err != nil { - return nil, err - } else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local { - // If we found the end of our starting element, we're done parsing - return msgs, nil - } else if startElement, ok := tok.(xml.StartElement); ok { - switch startElement.Name.Local { - case "STMTTRNRS": - var info StatementResponse - if err := d.DecodeElement(&info, &startElement); err != nil { - return nil, err - } - msgs = append(msgs, Message(info)) - default: - return nil, errors.New("Unsupported banking response tag: " + startElement.Name.Local) - } - } else { - return nil, errors.New("Didn't find an opening element") - } - } -} diff --git a/banking_test.go b/banking_test.go index c21aa84..13260de 100644 --- a/banking_test.go +++ b/banking_test.go @@ -64,7 +64,7 @@ func TestMarshalBankStatementRequest(t *testing.T) { }, Include: true, } - request.Banking = append(request.Banking, &statementRequest) + request.Bank = append(request.Bank, &statementRequest) request.SetClientFields(&client) // Overwrite the DtClient value set by SetClientFields to time.Now() @@ -203,7 +203,7 @@ func TestUnmarshalBankStatementResponse(t *testing.T) { AvailBalAmt: (*ofxgo.Amount)(&availbalamt), AvailDtAsOf: &availdtasof, } - expected.Banking = append(expected.Banking, statementResponse) + expected.Bank = append(expected.Bank, &statementResponse) response, err := ofxgo.ParseResponse(responseReader) if err != nil { diff --git a/cmd/ofx/bankdownload.go b/cmd/ofx/bankdownload.go index f5b6237..bc80f2b 100644 --- a/cmd/ofx/bankdownload.go +++ b/cmd/ofx/bankdownload.go @@ -55,7 +55,7 @@ func download() { }, Include: true, } - query.Banking = append(query.Banking, &statementRequest) + query.Bank = append(query.Bank, &statementRequest) response, err := client.RequestNoParse(query) if err != nil { diff --git a/cmd/ofx/banktransactions.go b/cmd/ofx/banktransactions.go index e78fbe9..a342458 100644 --- a/cmd/ofx/banktransactions.go +++ b/cmd/ofx/banktransactions.go @@ -40,7 +40,7 @@ func bankTransactions() { }, Include: true, } - query.Banking = append(query.Banking, &statementRequest) + query.Bank = append(query.Bank, &statementRequest) response, err := client.Request(query) if err != nil { @@ -54,12 +54,12 @@ func bankTransactions() { os.Exit(1) } - if len(response.Banking) < 1 { + if len(response.Bank) < 1 { fmt.Println("No banking messages received") return } - if stmt, ok := response.Banking[0].(ofxgo.StatementResponse); ok { + if stmt, ok := response.Bank[0].(*ofxgo.StatementResponse); ok { fmt.Printf("Balance: %s %s (as of %s)\n", stmt.BalAmt, stmt.CurDef, stmt.DtAsOf) fmt.Println("Transactions:") for _, tran := range stmt.BankTranList.Transactions { diff --git a/cmd/ofx/ccdownload.go b/cmd/ofx/ccdownload.go index baa6660..dbbcfac 100644 --- a/cmd/ofx/ccdownload.go +++ b/cmd/ofx/ccdownload.go @@ -49,7 +49,7 @@ func ccDownload() { }, Include: true, } - query.CreditCards = append(query.CreditCards, &statementRequest) + query.CreditCard = append(query.CreditCard, &statementRequest) response, err := client.RequestNoParse(query) diff --git a/cmd/ofx/cctransactions.go b/cmd/ofx/cctransactions.go index f889e84..8f1e30d 100644 --- a/cmd/ofx/cctransactions.go +++ b/cmd/ofx/cctransactions.go @@ -36,7 +36,7 @@ func ccTransactions() { }, Include: true, } - query.CreditCards = append(query.CreditCards, &statementRequest) + query.CreditCard = append(query.CreditCard, &statementRequest) response, err := client.Request(query) if err != nil { @@ -50,12 +50,12 @@ func ccTransactions() { os.Exit(1) } - if len(response.CreditCards) < 1 { + if len(response.CreditCard) < 1 { fmt.Println("No banking messages received") return } - if stmt, ok := response.CreditCards[0].(ofxgo.CCStatementResponse); ok { + if stmt, ok := response.CreditCard[0].(*ofxgo.CCStatementResponse); ok { fmt.Printf("Balance: %s %s (as of %s)\n", stmt.BalAmt, stmt.CurDef, stmt.DtAsOf) fmt.Println("Transactions:") for _, tran := range stmt.BankTranList.Transactions { diff --git a/cmd/ofx/get_accounts.go b/cmd/ofx/get_accounts.go index 34bb796..86f0c58 100644 --- a/cmd/ofx/get_accounts.go +++ b/cmd/ofx/get_accounts.go @@ -54,7 +54,7 @@ func getAccounts() { fmt.Printf("\nFound the following accounts:\n\n") - if acctinfo, ok := response.Signup[0].(ofxgo.AcctInfoResponse); ok { + if acctinfo, ok := response.Signup[0].(*ofxgo.AcctInfoResponse); ok { for _, acct := range acctinfo.AcctInfo { if acct.BankAcctInfo != nil { fmt.Printf("Bank Account:\n\tBankId: \"%s\"\n\tAcctId: \"%s\"\n\tAcctType: %s\n", acct.BankAcctInfo.BankAcctFrom.BankId, acct.BankAcctInfo.BankAcctFrom.AcctId, acct.BankAcctInfo.BankAcctFrom.AcctType) diff --git a/cmd/ofx/invdownload.go b/cmd/ofx/invdownload.go index 1f1945a..31dab66 100644 --- a/cmd/ofx/invdownload.go +++ b/cmd/ofx/invdownload.go @@ -58,7 +58,7 @@ func invDownload() { Include401K: true, Include401KBal: true, } - query.Investments = append(query.Investments, &statementRequest) + query.InvStmt = append(query.InvStmt, &statementRequest) response, err := client.RequestNoParse(query) diff --git a/cmd/ofx/invtransactions.go b/cmd/ofx/invtransactions.go index 2aa7b3f..ffbdc74 100644 --- a/cmd/ofx/invtransactions.go +++ b/cmd/ofx/invtransactions.go @@ -44,7 +44,7 @@ func invTransactions() { Include401K: true, Include401KBal: true, } - query.Investments = append(query.Investments, &statementRequest) + query.InvStmt = append(query.InvStmt, &statementRequest) response, err := client.Request(query) if err != nil { @@ -59,12 +59,12 @@ func invTransactions() { os.Exit(1) } - if len(response.Investments) < 1 { + if len(response.InvStmt) < 1 { fmt.Println("No investment messages received") return } - if stmt, ok := response.Investments[0].(ofxgo.InvStatementResponse); ok { + if stmt, ok := response.InvStmt[0].(*ofxgo.InvStatementResponse); ok { availCash := big.Rat(stmt.InvBal.AvailCash) if availCash.IsInt() && availCash.Num().Int64() != 0 { fmt.Printf("Balance: %s %s (as of %s)\n", stmt.InvBal.AvailCash, stmt.CurDef, stmt.DtAsOf) diff --git a/creditcards.go b/creditcards.go index fb0cf62..92295c9 100644 --- a/creditcards.go +++ b/creditcards.go @@ -1,7 +1,6 @@ package ofxgo import ( - "errors" "github.com/aclindsa/go/src/encoding/xml" ) @@ -57,41 +56,15 @@ type CCStatementResponse struct { MktgInfo String `xml:"CCSTMTRS>MKTGINFO,omitempty"` // Marketing information } -func (sr CCStatementResponse) Name() string { +func (sr *CCStatementResponse) Name() string { return "CCSTMTTRNRS" } -func (sr CCStatementResponse) Valid() (bool, error) { +func (sr *CCStatementResponse) Valid() (bool, error) { //TODO implement return true, nil } -func (sr CCStatementResponse) Type() messageType { +func (sr *CCStatementResponse) Type() messageType { return CreditCardRs } - -func decodeCCMessageSet(d *xml.Decoder, start xml.StartElement) ([]Message, error) { - var msgs []Message - for { - tok, err := nextNonWhitespaceToken(d) - if err != nil { - return nil, err - } else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local { - // If we found the end of our starting element, we're done parsing - return msgs, nil - } else if startElement, ok := tok.(xml.StartElement); ok { - switch startElement.Name.Local { - case "CCSTMTTRNRS": - var info CCStatementResponse - if err := d.DecodeElement(&info, &startElement); err != nil { - return nil, err - } - msgs = append(msgs, Message(info)) - default: - return nil, errors.New("Unsupported banking response tag: " + startElement.Name.Local) - } - } else { - return nil, errors.New("Didn't find an opening element") - } - } -} diff --git a/investments.go b/investments.go index ff0ffb3..1f30b41 100644 --- a/investments.go +++ b/investments.go @@ -811,41 +811,15 @@ type InvStatementResponse struct { Inv401KBal *Inv401KBal `xml:"INVSTMTRS>INV401KBAL,omitempty"` } -func (sr InvStatementResponse) Name() string { +func (sr *InvStatementResponse) Name() string { return "INVSTMTTRNRS" } -func (sr InvStatementResponse) Valid() (bool, error) { +func (sr *InvStatementResponse) Valid() (bool, error) { //TODO implement return true, nil } -func (sr InvStatementResponse) Type() messageType { +func (sr *InvStatementResponse) Type() messageType { return InvStmtRs } - -func decodeInvestmentsMessageSet(d *xml.Decoder, start xml.StartElement) ([]Message, error) { - var msgs []Message - for { - tok, err := nextNonWhitespaceToken(d) - if err != nil { - return nil, err - } else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local { - // If we found the end of our starting element, we're done parsing - return msgs, nil - } else if startElement, ok := tok.(xml.StartElement); ok { - switch startElement.Name.Local { - case "INVSTMTTRNRS": - var info InvStatementResponse - if err := d.DecodeElement(&info, &startElement); err != nil { - return nil, err - } - msgs = append(msgs, Message(info)) - default: - return nil, errors.New("Unsupported investments response tag: " + startElement.Name.Local) - } - } else { - return nil, errors.New("Didn't find an opening element") - } - } -} diff --git a/profile.go b/profile.go index 1a369ca..dd2ef94 100644 --- a/profile.go +++ b/profile.go @@ -125,41 +125,15 @@ type ProfileResponse struct { Email String `xml:"PROFRS>EMAIL,omitempty"` } -func (pr ProfileResponse) Name() string { +func (pr *ProfileResponse) Name() string { return "PROFTRNRS" } -func (pr ProfileResponse) Valid() (bool, error) { +func (pr *ProfileResponse) Valid() (bool, error) { //TODO implement return true, nil } -func (pr ProfileResponse) Type() messageType { +func (pr *ProfileResponse) Type() messageType { return ProfileRs } - -func decodeProfileMessageSet(d *xml.Decoder, start xml.StartElement) ([]Message, error) { - var msgs []Message - for { - tok, err := nextNonWhitespaceToken(d) - if err != nil { - return nil, err - } else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local { - // If we found the end of our starting element, we're done parsing - return msgs, nil - } else if startElement, ok := tok.(xml.StartElement); ok { - switch startElement.Name.Local { - case "PROFTRNRS": - var prof ProfileResponse - if err := d.DecodeElement(&prof, &startElement); err != nil { - return nil, err - } - msgs = append(msgs, Message(prof)) - default: - return nil, errors.New("Unsupported profile response tag: " + startElement.Name.Local) - } - } else { - return nil, errors.New("Didn't find an opening element") - } - } -} diff --git a/request.go b/request.go index d0a3c35..38d211e 100644 --- a/request.go +++ b/request.go @@ -8,23 +8,23 @@ import ( ) type Request struct { - URL string - Version string // OFX version string, overwritten in Client.Request() - Signon SignonRequest // - Signup []Message // - Banking []Message // - CreditCards []Message // - // - Investments []Message // - // - // - // - // - Securities []Message // - // - // - Profile []Message // - // + URL string + Version string // OFX version string, overwritten in Client.Request() + Signon SignonRequest // + Signup []Message // + Bank []Message // + CreditCard []Message // + Loan []Message // + InvStmt []Message // + InterXfer []Message // + WireXfer []Message // + Billpay []Message // + Email []Message // + SecList []Message // + PresDir []Message // + PresDlv []Message // + Profile []Message // + Image []Message // indent bool // Whether to indent the marshaled XML } @@ -118,23 +118,29 @@ NEWFILEUID:NONE return nil, err } - if err := marshalMessageSet(encoder, oq.Signup, SignupRq); 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.Profile, ProfileRq}, + {oq.Image, ImageRq}, } - if err := marshalMessageSet(encoder, oq.Banking, BankRq); err != nil { - return nil, err - } - if err := marshalMessageSet(encoder, oq.CreditCards, CreditCardRq); err != nil { - return nil, err - } - if err := marshalMessageSet(encoder, oq.Investments, InvStmtRq); err != nil { - return nil, err - } - if err := marshalMessageSet(encoder, oq.Securities, SecListRq); err != nil { - return nil, err - } - if err := marshalMessageSet(encoder, oq.Profile, ProfileRq); err != nil { - return nil, err + for _, set := range messageSets { + if err := marshalMessageSet(encoder, set.Messages, set.Type); err != nil { + return nil, err + } } if err := encoder.EncodeToken(ofxElement.End()); err != nil { diff --git a/response.go b/response.go index 0aa90d9..9916e19 100644 --- a/response.go +++ b/response.go @@ -6,26 +6,27 @@ import ( "errors" "github.com/aclindsa/go/src/encoding/xml" "io" + "reflect" "strings" ) type Response struct { - Version string // String for OFX header, defaults to 203 - Signon SignonResponse // - Signup []Message // - Banking []Message // - CreditCards []Message // - // - Investments []Message // - // - // - // - // - Securities []Message // - // - // - Profile []Message // - // + Version string // String for OFX header, defaults to 203 + Signon SignonResponse // + Signup []Message // + Bank []Message // + CreditCard []Message // + Loan []Message // + InvStmt []Message // + InterXfer []Message // + WireXfer []Message // + Billpay []Message // + Email []Message // + SecList []Message // + PresDir []Message // + PresDlv []Message // + Profile []Message // + Image []Message // } func (or *Response) readSGMLHeaders(r *bufio.Reader) error { @@ -180,6 +181,59 @@ func guessVersion(r *bufio.Reader) (bool, error) { } } +var responseTypes = map[string]map[string]reflect.Type{ + SignupRs.String(): map[string]reflect.Type{ + (&AcctInfoResponse{}).Name(): reflect.TypeOf(AcctInfoResponse{})}, + BankRs.String(): map[string]reflect.Type{ + (&StatementResponse{}).Name(): reflect.TypeOf(StatementResponse{})}, + CreditCardRs.String(): map[string]reflect.Type{ + (&CCStatementResponse{}).Name(): reflect.TypeOf(CCStatementResponse{})}, + LoanRs.String(): map[string]reflect.Type{}, + InvStmtRs.String(): map[string]reflect.Type{ + (&InvStatementResponse{}).Name(): reflect.TypeOf(InvStatementResponse{})}, + InterXferRs.String(): map[string]reflect.Type{}, + WireXferRs.String(): map[string]reflect.Type{}, + BillpayRs.String(): map[string]reflect.Type{}, + EmailRs.String(): map[string]reflect.Type{}, + SecListRs.String(): map[string]reflect.Type{ + (&SecListResponse{}).Name(): reflect.TypeOf(SecListResponse{}), + (&SecurityList{}).Name(): reflect.TypeOf(SecurityList{})}, + PresDirRs.String(): map[string]reflect.Type{}, + PresDlvRs.String(): map[string]reflect.Type{}, + ProfileRs.String(): map[string]reflect.Type{ + (&ProfileResponse{}).Name(): reflect.TypeOf(ProfileResponse{})}, + ImageRs.String(): map[string]reflect.Type{}, +} + +func decodeMessageSet(d *xml.Decoder, start xml.StartElement, msgs *[]Message) error { + setTypes, ok := responseTypes[start.Name.Local] + if !ok { + return errors.New("Invalid message set: " + start.Name.Local) + } + for { + tok, err := nextNonWhitespaceToken(d) + if err != nil { + return err + } else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local { + // If we found the end of our starting element, we're done parsing + return nil + } else if startElement, ok := tok.(xml.StartElement); ok { + responseType, ok := setTypes[startElement.Name.Local] + if !ok { + return errors.New("Unsupported response transaction for " + start.Name.Local + ": " + startElement.Name.Local) + } + response := reflect.New(responseType).Interface() + responseMessage := response.(Message) + if err := d.DecodeElement(responseMessage, &startElement); err != nil { + return err + } + *msgs = append(*msgs, responseMessage) + } else { + return errors.New("Didn't find an opening element") + } + } +} + // ParseResponse parses an OFX response in SGML or XML into a Response object // from the given io.Reader // @@ -248,6 +302,23 @@ func ParseResponse(reader io.Reader) (*Response, error) { return nil, err } + var messageSlices = map[string]*[]Message{ + SignupRs.String(): &or.Signup, + BankRs.String(): &or.Bank, + CreditCardRs.String(): &or.CreditCard, + LoanRs.String(): &or.Loan, + InvStmtRs.String(): &or.InvStmt, + InterXferRs.String(): &or.InterXfer, + WireXferRs.String(): &or.WireXfer, + BillpayRs.String(): &or.Billpay, + EmailRs.String(): &or.Email, + SecListRs.String(): &or.SecList, + PresDirRs.String(): &or.PresDir, + PresDlvRs.String(): &or.PresDlv, + ProfileRs.String(): &or.Profile, + ImageRs.String(): &or.Image, + } + for { tok, err = nextNonWhitespaceToken(decoder) if err != nil { @@ -255,54 +326,12 @@ func ParseResponse(reader io.Reader) (*Response, error) { } else if ofxEnd, ok := tok.(xml.EndElement); ok && ofxEnd.Name.Local == "OFX" { return &or, nil // found closing XML element, so we're done } else if start, ok := tok.(xml.StartElement); ok { - // TODO decode other types - switch start.Name.Local { - case "SIGNUPMSGSRSV1": - msgs, err := decodeSignupMessageSet(decoder, start) - if err != nil { - return nil, err - } - or.Signup = msgs - case "BANKMSGSRSV1": - msgs, err := decodeBankingMessageSet(decoder, start) - if err != nil { - return nil, err - } - or.Banking = msgs - case "CREDITCARDMSGSRSV1": - msgs, err := decodeCCMessageSet(decoder, start) - if err != nil { - return nil, err - } - or.CreditCards = msgs - //case "LOANMSGSRSV1": - case "INVSTMTMSGSRSV1": - msgs, err := decodeInvestmentsMessageSet(decoder, start) - if err != nil { - return nil, err - } - or.Investments = msgs - //case "INTERXFERMSGSRSV1": - //case "WIREXFERMSGSRSV1": - //case "BILLPAYMSGSRSV1": - //case "EMAILMSGSRSV1": - case "SECLISTMSGSRSV1": - msgs, err := decodeSecuritiesMessageSet(decoder, start) - if err != nil { - return nil, err - } - or.Securities = msgs - //case "PRESDIRMSGSRSV1": - //case "PRESDLVMSGSRSV1": - case "PROFMSGSRSV1": - msgs, err := decodeProfileMessageSet(decoder, start) - if err != nil { - return nil, err - } - or.Profile = msgs - //case "IMAGEMSGSRSV1": - default: - return nil, errors.New("Unsupported message set: " + start.Name.Local) + slice, ok := messageSlices[start.Name.Local] + if !ok { + return nil, errors.New("Invalid message set: " + start.Name.Local) + } + if err := decodeMessageSet(decoder, start, slice); err != nil { + return nil, err } } else { return nil, errors.New("Found unexpected token") diff --git a/response_test.go b/response_test.go index d7a5b64..b7a9a38 100644 --- a/response_test.go +++ b/response_test.go @@ -58,7 +58,7 @@ func checkEqual(t *testing.T, fieldName string, expected, actual reflect.Value) } if expected.Type() != actual.Type() { - t.Fatalf("%s: Expected %s type for %s, found %s\n", t.Name(), expected.Type().Name(), fieldName, actual.Type().Name()) + t.Fatalf("%s: Expected %s type for %s, found %s\n", t.Name(), expected.Type(), fieldName, actual.Type()) } equalMethod := equalMethodOf(expected) diff --git a/securities.go b/securities.go index 8981de2..bd85455 100644 --- a/securities.go +++ b/securities.go @@ -50,16 +50,16 @@ type SecListResponse struct { // SECLISTRS is always empty, so we don't parse it here. The actual securities list will be in a top-level element parallel to SECLISTTRNRS } -func (r SecListResponse) Name() string { +func (r *SecListResponse) Name() string { return "SECLISTTRNRS" } -func (r SecListResponse) Valid() (bool, error) { +func (r *SecListResponse) Valid() (bool, error) { // TODO implement return true, nil } -func (r SecListResponse) Type() messageType { +func (r *SecListResponse) Type() messageType { return SecListRs } @@ -175,16 +175,16 @@ type SecurityList struct { Securities []Security } -func (r SecurityList) Name() string { +func (r *SecurityList) Name() string { return "SECLIST" } -func (r SecurityList) Valid() (bool, error) { +func (r *SecurityList) Valid() (bool, error) { // TODO implement return true, nil } -func (r SecurityList) Type() messageType { +func (r *SecurityList) Type() messageType { return SecListRs } @@ -236,35 +236,3 @@ func (r *SecurityList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro } } } - -func decodeSecuritiesMessageSet(d *xml.Decoder, start xml.StartElement) ([]Message, error) { - var msgs []Message - for { - tok, err := nextNonWhitespaceToken(d) - if err != nil { - return nil, err - } else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local { - // If we found the end of our starting element, we're done parsing - return msgs, nil - } else if startElement, ok := tok.(xml.StartElement); ok { - switch startElement.Name.Local { - case "SECLISTTRNRS": - var info SecListResponse - if err := d.DecodeElement(&info, &startElement); err != nil { - return nil, err - } - msgs = append(msgs, Message(info)) - case "SECLIST": - var info SecurityList - if err := d.DecodeElement(&info, &startElement); err != nil { - return nil, err - } - msgs = append(msgs, Message(info)) - default: - return nil, errors.New("Unsupported securities response tag: " + startElement.Name.Local) - } - } else { - return nil, errors.New("Didn't find an opening element") - } - } -} diff --git a/signup.go b/signup.go index 7daecf9..dbec7cb 100644 --- a/signup.go +++ b/signup.go @@ -1,7 +1,6 @@ package ofxgo import ( - "errors" "fmt" "github.com/aclindsa/go/src/encoding/xml" ) @@ -120,41 +119,15 @@ type AcctInfoResponse struct { AcctInfo []AcctInfo `xml:"ACCTINFORS>ACCTINFO,omitempty"` } -func (air AcctInfoResponse) Name() string { - return "ACCTINFORS" +func (air *AcctInfoResponse) Name() string { + return "ACCTINFOTRNRS" } -func (air AcctInfoResponse) Valid() (bool, error) { +func (air *AcctInfoResponse) Valid() (bool, error) { //TODO implement return true, nil } -func (air AcctInfoResponse) Type() messageType { +func (air *AcctInfoResponse) Type() messageType { return SignupRs } - -func decodeSignupMessageSet(d *xml.Decoder, start xml.StartElement) ([]Message, error) { - var msgs []Message - for { - tok, err := nextNonWhitespaceToken(d) - if err != nil { - return nil, err - } else if end, ok := tok.(xml.EndElement); ok && end.Name.Local == start.Name.Local { - // If we found the end of our starting element, we're done parsing - return msgs, nil - } else if startElement, ok := tok.(xml.StartElement); ok { - switch startElement.Name.Local { - case "ACCTINFOTRNRS": - var info AcctInfoResponse - if err := d.DecodeElement(&info, &startElement); err != nil { - return nil, err - } - msgs = append(msgs, Message(info)) - default: - return nil, errors.New("Unsupported signup response tag: " + startElement.Name.Local) - } - } else { - return nil, errors.New("Didn't find an opening element") - } - } -} diff --git a/signup_test.go b/signup_test.go index 05e3aac..1497e81 100644 --- a/signup_test.go +++ b/signup_test.go @@ -150,7 +150,7 @@ func TestUnmarshalAcctInfoResponse(t *testing.T) { BankAcctInfo: &bankacctinfo, }}, } - expected.Signup = append(expected.Signup, acctInfoResponse) + expected.Signup = append(expected.Signup, &acctInfoResponse) response, err := ofxgo.ParseResponse(responseReader) if err != nil {