From 94a77ac754d1a2e2ae8d10d07b7f15d471230701 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Tue, 2 Oct 2018 20:55:25 -0400 Subject: [PATCH] Add BasicClient, update Client to be interface This paves the way for more easily implementing different clients for different financial institutions --- README.md | 2 +- bank_test.go | 4 +-- client.go | 52 ++++++++++++++++++++++++-------------- cmd/ofx/detect_settings.go | 2 +- cmd/ofx/util.go | 4 +-- creditcard_test.go | 2 +- invstmt_test.go | 2 +- profile_test.go | 2 +- request.go | 2 +- signon_test.go | 2 +- signup_test.go | 2 +- 11 files changed, 45 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index cdaf823..5c85a55 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ The following code snippet demonstrates how to use OFXGo to query and parse OFX code from a checking account, printing the balance and returned transactions: ```go -client := ofxgo.Client{} // Accept the default Client settings +client := ofxgo.BasicClient{} // Accept the default Client settings // These values are specific to your bank var query ofxgo.Request diff --git a/bank_test.go b/bank_test.go index 4b56a82..bb9da4f 100644 --- a/bank_test.go +++ b/bank_test.go @@ -42,7 +42,7 @@ func TestMarshalBankStatementRequest(t *testing.T) { ` - var client = ofxgo.Client{ + var client = ofxgo.BasicClient{ AppID: "OFXGO", AppVer: "0001", SpecVersion: ofxgo.OfxVersion203, @@ -116,7 +116,7 @@ NEWFILEUID:NONE ` - var client = ofxgo.Client{ + var client = ofxgo.BasicClient{ AppID: "OFXGO", AppVer: "0001", SpecVersion: ofxgo.OfxVersion103, diff --git a/client.go b/client.go index ecf7516..d77f435 100644 --- a/client.go +++ b/client.go @@ -8,11 +8,25 @@ import ( ) // Client serves to aggregate OFX client settings that may be necessary to talk -// to a particular server due to quirks in that server's implementation. Client -// also provides the Request, RequestNoParse, and RawRequest helper methods to -// aid in making and parsing requests. Client uses default, non-zero settings, -// even if its fields are not initialized. -type Client struct { +// to a particular server due to quirks in that server's implementation. +// Client also provides the Request and RequestNoParse helper methods to aid in +// making and parsing requests. +type Client interface { + // Used to fill out a Request object + OfxVersion() ofxVersion + ID() String + Version() String + IndentRequests() bool + + // Used to initiate requests to servers + Request(r *Request) (*Response, error) + RequestNoParse(r *Request) (*http.Response, error) +} + +// BasicClient provides a standard Client implementation suitable for most +// financial institutions. BasicClient uses default, non-zero settings, even if +// its fields are not initialized. +type BasicClient struct { // Request fields to overwrite with the client's values. If nonempty, // defaults are used SpecVersion ofxVersion // VERSION in header @@ -23,27 +37,27 @@ type Client struct { NoIndent bool } -// OfxVersion returns the OFX specification version this Client will marshal +// OfxVersion returns the OFX specification version this BasicClient will marshal // Requests as. Defaults to "203" if the client's SpecVersion field is empty. -func (c *Client) OfxVersion() ofxVersion { +func (c *BasicClient) OfxVersion() ofxVersion { if c.SpecVersion.Valid() { return c.SpecVersion } return OfxVersion203 } -// ID returns this Client's OFX AppID field, defaulting to "OFXGO" if +// ID returns this BasicClient's OFX AppID field, defaulting to "OFXGO" if // unspecified. -func (c *Client) ID() String { +func (c *BasicClient) ID() String { if len(c.AppID) > 0 { return String(c.AppID) } return String("OFXGO") } -// Version returns this Client's version number as a string, defaulting to +// Version returns this BasicClient's version number as a string, defaulting to // "0001" if unspecified. -func (c *Client) Version() String { +func (c *BasicClient) Version() String { if len(c.AppVer) > 0 { return String(c.AppVer) } @@ -52,7 +66,7 @@ func (c *Client) Version() String { // IndentRequests returns true if the marshaled XML should be indented (and // contain newlines, since the two are linked in the current implementation) -func (c *Client) IndentRequests() bool { +func (c *BasicClient) IndentRequests() bool { return !c.NoIndent } @@ -65,9 +79,9 @@ func (c *Client) IndentRequests() bool { // like to try. // // Caveats: RawRequest does *not* take client settings into account as -// Request() does, so your particular server may or may not like whatever we -// read from 'r'. The caller is responsible for closing the http Response.Body -// (see the http module's documentation for more information) +// Client.Request() does, so your particular server may or may not like +// whatever we read from 'r'. The caller is responsible for closing the http +// Response.Body (see the http module's documentation for more information) func RawRequest(URL string, r io.Reader) (*http.Response, error) { if !strings.HasPrefix(URL, "https://") { return nil, errors.New("Refusing to send OFX request with possible plain-text password over non-https protocol") @@ -119,7 +133,7 @@ func rawRequestCookies(URL string, r io.Reader, cookies []*http.Cookie) (*http.R // // Caveat: The caller is responsible for closing the http Response.Body (see // the http module's documentation for more information) -func (c *Client) RequestNoParse(r *Request) (*http.Response, error) { +func (c *BasicClient) RequestNoParse(r *Request) (*http.Response, error) { r.SetClientFields(c) b, err := r.Marshal() @@ -150,11 +164,11 @@ func (c *Client) RequestNoParse(r *Request) (*http.Response, error) { // it's URL, and then unmarshals the response into a Response object. // // Before being marshaled, some of the the Request object's values are -// overwritten, namely those dictated by the Client's configuration (Version, -// AppID, AppVer fields), and the client's curren time (DtClient). These are +// overwritten, namely those dictated by the BasicClient's configuration (Version, +// AppID, AppVer fields), and the client's current time (DtClient). These are // updated in place in the supplied Request object so they may later be // inspected by the caller. -func (c *Client) Request(r *Request) (*Response, error) { +func (c *BasicClient) Request(r *Request) (*Response, error) { response, err := c.RequestNoParse(r) if err != nil { return nil, err diff --git a/cmd/ofx/detect_settings.go b/cmd/ofx/detect_settings.go index 2f16b3e..b0c98a6 100644 --- a/cmd/ofx/detect_settings.go +++ b/cmd/ofx/detect_settings.go @@ -126,7 +126,7 @@ func tryProfile(appID, appVer, version string, noindent bool) bool { fmt.Println("Error creating new OfxVersion enum:", err) os.Exit(1) } - var client = ofxgo.Client{ + var client = ofxgo.BasicClient{ AppID: appID, AppVer: appVer, SpecVersion: ver, diff --git a/cmd/ofx/util.go b/cmd/ofx/util.go index fcf43b2..e96b554 100644 --- a/cmd/ofx/util.go +++ b/cmd/ofx/util.go @@ -6,13 +6,13 @@ import ( "os" ) -func newRequest() (*ofxgo.Client, *ofxgo.Request) { +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{ + var client = ofxgo.BasicClient{ AppID: appID, AppVer: appVer, SpecVersion: ver, diff --git a/creditcard_test.go b/creditcard_test.go index 6cae6fa..e384488 100644 --- a/creditcard_test.go +++ b/creditcard_test.go @@ -41,7 +41,7 @@ func TestMarshalCCStatementRequest(t *testing.T) { ` - var client = ofxgo.Client{ + var client = ofxgo.BasicClient{ AppID: "OFXGO", AppVer: "0001", SpecVersion: ofxgo.OfxVersion203, diff --git a/invstmt_test.go b/invstmt_test.go index 84ab78a..afe37b9 100644 --- a/invstmt_test.go +++ b/invstmt_test.go @@ -49,7 +49,7 @@ func TestMarshalInvStatementRequest(t *testing.T) { ` - var client = ofxgo.Client{ + var client = ofxgo.BasicClient{ AppID: "MYAPP", AppVer: "1234", SpecVersion: ofxgo.OfxVersion203, diff --git a/profile_test.go b/profile_test.go index ea2d149..71db474 100644 --- a/profile_test.go +++ b/profile_test.go @@ -36,7 +36,7 @@ func TestMarshalProfileRequest(t *testing.T) { ` - var client = ofxgo.Client{ + var client = ofxgo.BasicClient{ AppID: "OFXGO", AppVer: "0001", SpecVersion: ofxgo.OfxVersion203, diff --git a/request.go b/request.go index 750288e..dc63f19 100644 --- a/request.go +++ b/request.go @@ -63,7 +63,7 @@ func marshalMessageSet(e *xml.Encoder, requests []Message, set messageType, vers // SetClientFields overwrites the fields in this Request object controlled by // the Client -func (oq *Request) SetClientFields(c *Client) { +func (oq *Request) SetClientFields(c Client) { oq.Signon.DtClient.Time = time.Now() // Overwrite fields that the client controls diff --git a/signon_test.go b/signon_test.go index 527db42..4942eb5 100644 --- a/signon_test.go +++ b/signon_test.go @@ -6,7 +6,7 @@ import ( ) func TestMarshalInvalidSignons(t *testing.T) { - var client = ofxgo.Client{ + var client = ofxgo.BasicClient{ AppID: "OFXGO", AppVer: "0001", SpecVersion: ofxgo.OfxVersion203, diff --git a/signup_test.go b/signup_test.go index 2b75d84..da2ac68 100644 --- a/signup_test.go +++ b/signup_test.go @@ -37,7 +37,7 @@ func TestMarshalAcctInfoRequest(t *testing.T) { EST := time.FixedZone("EST", -5*60*60) - var client = ofxgo.Client{ + var client = ofxgo.BasicClient{ AppID: "OFXGO", AppVer: "0001", SpecVersion: ofxgo.OfxVersion203,