Skip to content

Commit

Permalink
Add BasicClient, update Client to be interface
Browse files Browse the repository at this point in the history
This paves the way for more easily implementing different clients for
different financial institutions
  • Loading branch information
aclindsa committed Oct 3, 2018
1 parent 88e5521 commit 94a77ac
Show file tree
Hide file tree
Showing 11 changed files with 45 additions and 31 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions bank_test.go
Expand Up @@ -42,7 +42,7 @@ func TestMarshalBankStatementRequest(t *testing.T) {
</BANKMSGSRQV1>
</OFX>`

var client = ofxgo.Client{
var client = ofxgo.BasicClient{
AppID: "OFXGO",
AppVer: "0001",
SpecVersion: ofxgo.OfxVersion203,
Expand Down Expand Up @@ -116,7 +116,7 @@ NEWFILEUID:NONE
</BANKMSGSRQV1>
</OFX>`

var client = ofxgo.Client{
var client = ofxgo.BasicClient{
AppID: "OFXGO",
AppVer: "0001",
SpecVersion: ofxgo.OfxVersion103,
Expand Down
52 changes: 33 additions & 19 deletions client.go
Expand Up @@ -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
Expand All @@ -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)
}
Expand All @@ -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
}

Expand All @@ -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")
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion cmd/ofx/detect_settings.go
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions cmd/ofx/util.go
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion creditcard_test.go
Expand Up @@ -41,7 +41,7 @@ func TestMarshalCCStatementRequest(t *testing.T) {
</CREDITCARDMSGSRQV1>
</OFX>`

var client = ofxgo.Client{
var client = ofxgo.BasicClient{
AppID: "OFXGO",
AppVer: "0001",
SpecVersion: ofxgo.OfxVersion203,
Expand Down
2 changes: 1 addition & 1 deletion invstmt_test.go
Expand Up @@ -49,7 +49,7 @@ func TestMarshalInvStatementRequest(t *testing.T) {
</INVSTMTMSGSRQV1>
</OFX>`

var client = ofxgo.Client{
var client = ofxgo.BasicClient{
AppID: "MYAPP",
AppVer: "1234",
SpecVersion: ofxgo.OfxVersion203,
Expand Down
2 changes: 1 addition & 1 deletion profile_test.go
Expand Up @@ -36,7 +36,7 @@ func TestMarshalProfileRequest(t *testing.T) {
</PROFMSGSRQV1>
</OFX>`

var client = ofxgo.Client{
var client = ofxgo.BasicClient{
AppID: "OFXGO",
AppVer: "0001",
SpecVersion: ofxgo.OfxVersion203,
Expand Down
2 changes: 1 addition & 1 deletion request.go
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion signon_test.go
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion signup_test.go
Expand Up @@ -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,
Expand Down

0 comments on commit 94a77ac

Please sign in to comment.