2017-03-16 19:31:26 -04:00
|
|
|
package ofxgo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"net/http"
|
2017-04-01 21:33:56 -04:00
|
|
|
"strings"
|
2017-03-16 19:31:26 -04:00
|
|
|
)
|
|
|
|
|
2017-04-16 20:38:45 -04:00
|
|
|
// Client serves to aggregate OFX client settings that may be necessary to talk
|
2018-10-02 20:55:25 -04:00
|
|
|
// 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
|
2019-06-14 01:40:22 -04:00
|
|
|
CarriageReturnNewLines() bool
|
2018-10-02 20:55:25 -04:00
|
|
|
|
2018-10-03 09:59:04 -04:00
|
|
|
// Request marshals a Request object into XML, makes an HTTP request
|
|
|
|
// against 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 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.
|
2018-10-02 20:55:25 -04:00
|
|
|
Request(r *Request) (*Response, error)
|
2017-03-16 19:31:26 -04:00
|
|
|
|
2018-10-03 09:59:04 -04:00
|
|
|
// RequestNoParse marshals a Request object into XML, makes an HTTP
|
|
|
|
// request, and returns the raw HTTP response. Unlike RawRequest(), it
|
|
|
|
// takes client settings into account. Unlike Request(), it doesn't parse
|
|
|
|
// the response into an ofxgo.Request object.
|
|
|
|
//
|
|
|
|
// Caveat: The caller is responsible for closing the http Response.Body
|
|
|
|
// (see the http module's documentation for more information)
|
|
|
|
RequestNoParse(r *Request) (*http.Response, error)
|
2017-03-16 19:31:26 -04:00
|
|
|
|
2018-10-03 09:59:04 -04:00
|
|
|
// RawRequest is little more than a thin wrapper around http.Post
|
|
|
|
//
|
|
|
|
// In most cases, you should probably be using Request() instead, but
|
|
|
|
// RawRequest can be useful if you need to read the raw unparsed http
|
|
|
|
// response yourself (perhaps for downloading an OFX file for use by an
|
|
|
|
// external program, or debugging server behavior), or have a handcrafted
|
|
|
|
// request you'd like to try.
|
|
|
|
//
|
|
|
|
// Caveats: RawRequest does *not* take client settings into account as
|
|
|
|
// 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)
|
|
|
|
RawRequest(URL string, r io.Reader) (*http.Response, error)
|
2017-03-16 19:31:26 -04:00
|
|
|
}
|
|
|
|
|
2018-10-03 09:59:04 -04:00
|
|
|
type clientCreationFunc func(*BasicClient) Client
|
|
|
|
|
|
|
|
// GetClient returns a new Client for a given URL. It attempts to find a
|
|
|
|
// specialized client for this URL, but simply returns the passed-in
|
|
|
|
// BasicClient if no such match is found.
|
|
|
|
func GetClient(URL string, bc *BasicClient) Client {
|
|
|
|
clients := []struct {
|
|
|
|
URL string
|
|
|
|
Func clientCreationFunc
|
|
|
|
}{
|
2018-10-03 10:18:11 -04:00
|
|
|
{"https://ofx.discovercard.com", NewDiscoverCardClient},
|
2018-10-03 09:59:04 -04:00
|
|
|
{"https://vesnc.vanguard.com/us/OfxDirectConnectServlet", NewVanguardClient},
|
2017-09-01 08:41:15 -04:00
|
|
|
}
|
2018-10-03 09:59:04 -04:00
|
|
|
for _, client := range clients {
|
|
|
|
if client.URL == strings.Trim(URL, "/") {
|
|
|
|
return client.Func(bc)
|
|
|
|
}
|
2017-09-01 08:41:15 -04:00
|
|
|
}
|
2018-10-03 09:59:04 -04:00
|
|
|
return bc
|
2017-09-01 08:41:15 -04:00
|
|
|
}
|
|
|
|
|
2018-10-03 09:59:04 -04:00
|
|
|
// clientRequestNoParse can be used for building clients' RequestNoParse
|
|
|
|
// methods if they require fairly standard behavior
|
|
|
|
func clientRequestNoParse(c Client, r *Request) (*http.Response, error) {
|
2017-03-28 20:42:22 -04:00
|
|
|
r.SetClientFields(c)
|
2017-03-16 19:31:26 -04:00
|
|
|
|
|
|
|
b, err := r.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-10-03 09:59:04 -04:00
|
|
|
return c.RawRequest(r.URL, b)
|
2017-03-22 19:56:59 -04:00
|
|
|
}
|
|
|
|
|
2018-10-03 09:59:04 -04:00
|
|
|
// clientRequest can be used for building clients' Request methods if they
|
|
|
|
// require fairly standard behavior
|
|
|
|
func clientRequest(c Client, r *Request) (*Response, error) {
|
2017-03-22 19:56:59 -04:00
|
|
|
response, err := c.RequestNoParse(r)
|
2017-03-16 19:31:26 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
|
|
|
|
ofxresp, err := ParseResponse(response.Body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ofxresp, nil
|
|
|
|
}
|