mirror of
https://github.com/aclindsa/ofxgo.git
synced 2024-11-21 19:20:05 -05:00
Make the OFX spec version an 'enum'
This commit is contained in:
parent
94f49640b4
commit
0eba6741f2
@ -45,7 +45,7 @@ func TestMarshalBankStatementRequest(t *testing.T) {
|
||||
var client = ofxgo.Client{
|
||||
AppID: "OFXGO",
|
||||
AppVer: "0001",
|
||||
SpecVersion: "203",
|
||||
SpecVersion: ofxgo.OfxVersion203,
|
||||
}
|
||||
|
||||
var request ofxgo.Request
|
||||
@ -119,7 +119,7 @@ NEWFILEUID:NONE
|
||||
var client = ofxgo.Client{
|
||||
AppID: "OFXGO",
|
||||
AppVer: "0001",
|
||||
SpecVersion: "103",
|
||||
SpecVersion: ofxgo.OfxVersion103,
|
||||
}
|
||||
|
||||
var request ofxgo.Request
|
||||
@ -213,7 +213,7 @@ func TestUnmarshalBankStatementResponse(t *testing.T) {
|
||||
</OFX>`)
|
||||
var expected ofxgo.Response
|
||||
|
||||
expected.Version = "203"
|
||||
expected.Version = ofxgo.OfxVersion203
|
||||
expected.Signon.Status.Code = 0
|
||||
expected.Signon.Status.Severity = "INFO"
|
||||
expected.Signon.DtServer = *ofxgo.NewDateGMT(2006, 1, 15, 11, 23, 03, 0)
|
||||
|
17
client.go
17
client.go
@ -15,9 +15,9 @@ import (
|
||||
type Client struct {
|
||||
// Request fields to overwrite with the client's values. If nonempty,
|
||||
// defaults are used
|
||||
SpecVersion string // VERSION in header
|
||||
AppID string // SONRQ>APPID
|
||||
AppVer string // SONRQ>APPVER
|
||||
SpecVersion ofxVersion // VERSION in header
|
||||
AppID string // SONRQ>APPID
|
||||
AppVer string // SONRQ>APPVER
|
||||
|
||||
// Don't insert newlines or indentation when marshalling to SGML/XML
|
||||
NoIndent bool
|
||||
@ -25,14 +25,13 @@ type Client struct {
|
||||
|
||||
var defaultClient Client
|
||||
|
||||
// OfxVersion returns a string representation of the OFX specification version
|
||||
// this Client will marshal Requests as. Defaults to "203" if the client's
|
||||
// SpecVersion field is empty.
|
||||
func (c *Client) OfxVersion() string {
|
||||
if len(c.SpecVersion) > 0 {
|
||||
// OfxVersion returns the OFX specification version this Client will marshal
|
||||
// Requests as. Defaults to "203" if the client's SpecVersion field is empty.
|
||||
func (c *Client) OfxVersion() ofxVersion {
|
||||
if c.SpecVersion.Valid() {
|
||||
return c.SpecVersion
|
||||
}
|
||||
return "203"
|
||||
return OfxVersion203
|
||||
}
|
||||
|
||||
// ID returns this Client's OFX AppID field, defaulting to "OFXGO" if
|
||||
|
@ -121,10 +121,15 @@ func detectSettings() {
|
||||
const anonymous = "anonymous00000000000000000000000"
|
||||
|
||||
func tryProfile(appID, appVer, version string, noindent bool) bool {
|
||||
ver, err := ofxgo.NewOfxVersion(version)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating new OfxVersion enum:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var client = ofxgo.Client{
|
||||
AppID: appID,
|
||||
AppVer: appVer,
|
||||
SpecVersion: version,
|
||||
SpecVersion: ver,
|
||||
NoIndent: noindent,
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aclindsa/ofxgo"
|
||||
"os"
|
||||
)
|
||||
|
||||
func newRequest() (*ofxgo.Client, *ofxgo.Request) {
|
||||
ver, err := ofxgo.NewOfxVersion(ofxVersion)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating new OfxVersion enum:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var client = ofxgo.Client{
|
||||
AppID: appID,
|
||||
AppVer: appVer,
|
||||
SpecVersion: ofxVersion,
|
||||
SpecVersion: ver,
|
||||
NoIndent: noIndentRequests,
|
||||
}
|
||||
|
||||
|
74
constants.go
74
constants.go
@ -13,6 +13,80 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ofxVersion uint
|
||||
|
||||
// OfxVersion* constants represent the OFX specification version in use
|
||||
const (
|
||||
OfxVersion102 ofxVersion = 1 + iota
|
||||
OfxVersion103
|
||||
OfxVersion151
|
||||
OfxVersion160
|
||||
OfxVersion200
|
||||
OfxVersion201
|
||||
OfxVersion202
|
||||
OfxVersion203
|
||||
OfxVersion210
|
||||
OfxVersion211
|
||||
OfxVersion220
|
||||
)
|
||||
|
||||
var ofxVersions = [...]string{"102", "103", "151", "160", "200", "201", "202", "203", "210", "211", "220"}
|
||||
|
||||
func (e ofxVersion) Valid() bool {
|
||||
// This check is mostly out of paranoia, ensuring e != 0 should be
|
||||
// sufficient
|
||||
return e >= OfxVersion102 && e <= OfxVersion220
|
||||
}
|
||||
|
||||
func (e ofxVersion) String() string {
|
||||
if e.Valid() {
|
||||
return ofxVersions[e-1]
|
||||
}
|
||||
return fmt.Sprintf("invalid ofxVersion (%d)", e)
|
||||
}
|
||||
|
||||
func (e *ofxVersion) FromString(in string) error {
|
||||
value := strings.TrimSpace(in)
|
||||
|
||||
for i, s := range ofxVersions {
|
||||
if s == value {
|
||||
*e = ofxVersion(i + 1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*e = 0
|
||||
return errors.New("Invalid OfxVersion: \"" + in + "\"")
|
||||
}
|
||||
|
||||
func (e *ofxVersion) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var value string
|
||||
err := d.DecodeElement(&value, &start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.FromString(value)
|
||||
}
|
||||
|
||||
func (e *ofxVersion) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
|
||||
if !e.Valid() {
|
||||
return nil
|
||||
}
|
||||
enc.EncodeElement(ofxVersions[*e-1], start)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewOfxVersion returns returns an 'enum' value of type ofxVersion given its
|
||||
// string representation
|
||||
func NewOfxVersion(s string) (ofxVersion, error) {
|
||||
var e ofxVersion
|
||||
err := e.FromString(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
type acctType uint
|
||||
|
||||
// AcctType* constants represent types of bank accounts
|
||||
|
@ -13,6 +13,51 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOfxVersion(t *testing.T) {
|
||||
e, err := ofxgo.NewOfxVersion("102")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error creating new OfxVersion from string \"102\"\n")
|
||||
}
|
||||
if !e.Valid() {
|
||||
t.Fatalf("OfxVersion unexpectedly invalid\n")
|
||||
}
|
||||
err = e.FromString("220")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error on OfxVersion.FromString(\"220\")\n")
|
||||
}
|
||||
if e.String() != "220" {
|
||||
t.Fatalf("OfxVersion.String() expected to be \"220\"\n")
|
||||
}
|
||||
|
||||
marshalHelper(t, "220", &e)
|
||||
|
||||
overwritten, err := ofxgo.NewOfxVersion("THISWILLNEVERBEAVALIDENUMSTRING")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error creating new OfxVersion from string \"THISWILLNEVERBEAVALIDENUMSTRING\"\n")
|
||||
}
|
||||
if overwritten.Valid() {
|
||||
t.Fatalf("OfxVersion created with string \"THISWILLNEVERBEAVALIDENUMSTRING\" should not be valid\n")
|
||||
}
|
||||
if !strings.Contains(strings.ToLower(overwritten.String()), "invalid") {
|
||||
t.Fatalf("OfxVersion created with string \"THISWILLNEVERBEAVALIDENUMSTRING\" should not return valid string from String()\n")
|
||||
}
|
||||
|
||||
b, err := xml.Marshal(&overwritten)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error on xml.Marshal(OfxVersion): %s\n", err)
|
||||
}
|
||||
if string(b) != "" {
|
||||
t.Fatalf("Expected empty string, got '%s'\n", string(b))
|
||||
}
|
||||
|
||||
unmarshalHelper(t, "220", &e, &overwritten)
|
||||
|
||||
err = xml.Unmarshal([]byte("<GARBAGE><!LALDK>"), &overwritten)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error unmarshalling garbage value\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcctType(t *testing.T) {
|
||||
e, err := ofxgo.NewAcctType("CHECKING")
|
||||
if err != nil {
|
||||
|
@ -44,7 +44,7 @@ func TestMarshalCCStatementRequest(t *testing.T) {
|
||||
var client = ofxgo.Client{
|
||||
AppID: "OFXGO",
|
||||
AppVer: "0001",
|
||||
SpecVersion: "203",
|
||||
SpecVersion: ofxgo.OfxVersion203,
|
||||
}
|
||||
|
||||
var request ofxgo.Request
|
||||
@ -86,7 +86,7 @@ NEWFILEUID:NONE
|
||||
EDT := time.FixedZone("EDT", -4*60*60)
|
||||
EST := time.FixedZone("EST", -5*60*60)
|
||||
|
||||
expected.Version = "102"
|
||||
expected.Version = ofxgo.OfxVersion102
|
||||
expected.Signon.Status.Code = 0
|
||||
expected.Signon.Status.Severity = "INFO"
|
||||
expected.Signon.Status.Message = "SUCCESS"
|
||||
|
@ -1,6 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
enums = {
|
||||
# OFX spec version
|
||||
"OfxVersion": (["102", "103", "151", "160", "200", "201", "202", "203", "210", "211", "220"], "the OFX specification version in use"),
|
||||
|
||||
# Bank/general
|
||||
"AcctType": (["Checking", "Savings", "MoneyMrkt", "CreditLine", "CD"], "types of bank accounts"),
|
||||
"TrnType": (["Credit", "Debit", "Int", "Div", "Fee", "SrvChg", "Dep", "ATM", "POS", "Xfer", "Check", "Payment", "Cash", "DirectDep", "DirectDebit", "RepeatPmt", "Hold", "Other"], "types of transactions. INT, ATM, and POS depend on the signage of the account."),
|
||||
|
@ -52,7 +52,7 @@ func TestMarshalInvStatementRequest(t *testing.T) {
|
||||
var client = ofxgo.Client{
|
||||
AppID: "MYAPP",
|
||||
AppVer: "1234",
|
||||
SpecVersion: "203",
|
||||
SpecVersion: ofxgo.OfxVersion203,
|
||||
}
|
||||
|
||||
var request ofxgo.Request
|
||||
@ -322,7 +322,7 @@ func TestUnmarshalInvStatementResponse(t *testing.T) {
|
||||
</OFX>`)
|
||||
var expected ofxgo.Response
|
||||
|
||||
expected.Version = "203"
|
||||
expected.Version = ofxgo.OfxVersion203
|
||||
expected.Signon.Status.Code = 0
|
||||
expected.Signon.Status.Severity = "INFO"
|
||||
expected.Signon.DtServer = *ofxgo.NewDateGMT(2017, 4, 1, 20, 12, 44, 0)
|
||||
@ -765,7 +765,7 @@ NEWFILEUID: NONE
|
||||
</OFX>`)
|
||||
var expected ofxgo.Response
|
||||
|
||||
expected.Version = "102"
|
||||
expected.Version = ofxgo.OfxVersion102
|
||||
expected.Signon.Status.Code = 0
|
||||
expected.Signon.Status.Severity = "INFO"
|
||||
expected.Signon.DtServer = *ofxgo.NewDateGMT(2017, 4, 3, 12, 0, 0, 0)
|
||||
|
@ -39,7 +39,7 @@ func TestMarshalProfileRequest(t *testing.T) {
|
||||
var client = ofxgo.Client{
|
||||
AppID: "OFXGO",
|
||||
AppVer: "0001",
|
||||
SpecVersion: "203",
|
||||
SpecVersion: ofxgo.OfxVersion203,
|
||||
}
|
||||
|
||||
var request ofxgo.Request
|
||||
@ -215,7 +215,7 @@ NEWFILEUID:NONE
|
||||
</OFX>`)
|
||||
var expected ofxgo.Response
|
||||
|
||||
expected.Version = "102"
|
||||
expected.Version = ofxgo.OfxVersion102
|
||||
expected.Signon.Status.Code = 0
|
||||
expected.Signon.Status.Severity = "INFO"
|
||||
expected.Signon.DtServer = *ofxgo.NewDateGMT(2017, 4, 3, 9, 34, 58, 0)
|
||||
|
13
request.go
13
request.go
@ -3,6 +3,7 @@ package ofxgo
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/aclindsa/go/src/encoding/xml"
|
||||
"time"
|
||||
)
|
||||
@ -14,7 +15,7 @@ import (
|
||||
// error will be returned when Marshal() is called on this Request.
|
||||
type Request struct {
|
||||
URL string
|
||||
Version string // OFX version string, overwritten in Client.Request()
|
||||
Version ofxVersion // OFX version, overwritten in Client.Request()
|
||||
Signon SignonRequest //<SIGNONMSGSETV1>
|
||||
Signup []Message //<SIGNUPMSGSETV1>
|
||||
Bank []Message //<BANKMSGSETV1>
|
||||
@ -80,10 +81,10 @@ func (oq *Request) Marshal() (*bytes.Buffer, error) {
|
||||
|
||||
// Write the header appropriate to our version
|
||||
switch oq.Version {
|
||||
case "102", "103", "151", "160":
|
||||
case OfxVersion102, OfxVersion103, OfxVersion151, OfxVersion160:
|
||||
b.WriteString(`OFXHEADER:100
|
||||
DATA:OFXSGML
|
||||
VERSION:` + oq.Version + `
|
||||
VERSION:` + oq.Version.String() + `
|
||||
SECURITY:NONE
|
||||
ENCODING:USASCII
|
||||
CHARSET:1252
|
||||
@ -92,11 +93,11 @@ OLDFILEUID:NONE
|
||||
NEWFILEUID:NONE
|
||||
|
||||
`)
|
||||
case "200", "201", "202", "203", "210", "211", "220":
|
||||
case OfxVersion200, OfxVersion201, OfxVersion202, OfxVersion203, OfxVersion210, OfxVersion211, OfxVersion220:
|
||||
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")
|
||||
b.WriteString(`<?OFX OFXHEADER="200" VERSION="` + oq.Version.String() + `" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?>` + "\n")
|
||||
default:
|
||||
return nil, errors.New(oq.Version + " is not a valid OFX version string")
|
||||
return nil, fmt.Errorf("%d is not a valid OFX version string", oq.Version)
|
||||
}
|
||||
|
||||
encoder := xml.NewEncoder(&b)
|
||||
|
30
response.go
30
response.go
@ -14,7 +14,7 @@ import (
|
||||
// It can be inspected by using type assertions or switches on the message set
|
||||
// you're interested in.
|
||||
type Response struct {
|
||||
Version string // String for OFX header, defaults to 203
|
||||
Version ofxVersion // OFX header version
|
||||
Signon SignonResponse //<SIGNONMSGSETV1>
|
||||
Signup []Message //<SIGNUPMSGSETV1>
|
||||
Bank []Message //<BANKMSGSETV1>
|
||||
@ -68,12 +68,14 @@ func (or *Response) readSGMLHeaders(r *bufio.Reader) error {
|
||||
return errors.New("OFX DATA header does not contain OFXSGML")
|
||||
}
|
||||
case "VERSION":
|
||||
switch headervalue {
|
||||
case "102", "103", "151", "160":
|
||||
seenVersion = true
|
||||
or.Version = headervalue
|
||||
default:
|
||||
return errors.New("Invalid OFX VERSION in header")
|
||||
err := or.Version.FromString(headervalue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
seenVersion = true
|
||||
|
||||
if or.Version > OfxVersion160 {
|
||||
return errors.New("OFX VERSION > 160 in SGML header")
|
||||
}
|
||||
case "SECURITY":
|
||||
if headervalue != "NONE" {
|
||||
@ -134,12 +136,14 @@ func (or *Response) readXMLHeaders(decoder *xml.Decoder) error {
|
||||
}
|
||||
seenHeader = true
|
||||
case "VERSION":
|
||||
switch value {
|
||||
case "200", "201", "202", "203", "210", "211", "220":
|
||||
seenVersion = true
|
||||
or.Version = value
|
||||
default:
|
||||
return errors.New("Invalid OFX VERSION in header")
|
||||
err := or.Version.FromString(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
seenVersion = true
|
||||
|
||||
if or.Version < OfxVersion200 {
|
||||
return errors.New("OFX VERSION < 200 in XML header")
|
||||
}
|
||||
case "SECURITY":
|
||||
if value != "NONE" {
|
||||
|
@ -9,7 +9,7 @@ func TestMarshalInvalidSignons(t *testing.T) {
|
||||
var client = ofxgo.Client{
|
||||
AppID: "OFXGO",
|
||||
AppVer: "0001",
|
||||
SpecVersion: "203",
|
||||
SpecVersion: ofxgo.OfxVersion203,
|
||||
}
|
||||
|
||||
var request ofxgo.Request
|
||||
|
@ -40,7 +40,7 @@ func TestMarshalAcctInfoRequest(t *testing.T) {
|
||||
var client = ofxgo.Client{
|
||||
AppID: "OFXGO",
|
||||
AppVer: "0001",
|
||||
SpecVersion: "203",
|
||||
SpecVersion: ofxgo.OfxVersion203,
|
||||
}
|
||||
|
||||
var request ofxgo.Request
|
||||
@ -112,7 +112,7 @@ func TestUnmarshalAcctInfoResponse(t *testing.T) {
|
||||
</OFX>`)
|
||||
var expected ofxgo.Response
|
||||
|
||||
expected.Version = "203"
|
||||
expected.Version = ofxgo.OfxVersion203
|
||||
expected.Signon.Status.Code = 0
|
||||
expected.Signon.Status.Severity = "INFO"
|
||||
expected.Signon.DtServer = *ofxgo.NewDateGMT(2006, 1, 15, 11, 23, 03, 0)
|
||||
|
Loading…
Reference in New Issue
Block a user