mirror of
https://github.com/aclindsa/ofxgo.git
synced 2024-12-26 15:02:27 -05:00
Comment investments, profiles, seclist, signon, signup, and types
This commit is contained in:
parent
a1aec204a8
commit
1ff64a9d55
@ -468,7 +468,8 @@ type InvTranList struct {
|
||||
BankTransactions []InvBankTransaction
|
||||
}
|
||||
|
||||
// UnmarshalXML handles unmarshalling an InvTranList element from an XML string
|
||||
// UnmarshalXML handles unmarshalling an InvTranList element from an SGML/XML
|
||||
// string
|
||||
func (l *InvTranList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
for {
|
||||
tok, err := nextNonWhitespaceToken(d)
|
||||
|
34
profile.go
34
profile.go
@ -5,6 +5,9 @@ import (
|
||||
"github.com/aclindsa/go/src/encoding/xml"
|
||||
)
|
||||
|
||||
// ProfileRequest represents a request for a server to provide a profile of its
|
||||
// capabilities (which message sets and versions it supports, how to access
|
||||
// them, which languages and which types of synchronization they support, etc.)
|
||||
type ProfileRequest struct {
|
||||
XMLName xml.Name `xml:"PROFTRNRQ"`
|
||||
TrnUID UID `xml:"TRNUID"`
|
||||
@ -12,26 +15,35 @@ type ProfileRequest struct {
|
||||
TAN String `xml:"TAN,omitempty"` // Transaction authorization number
|
||||
// TODO `xml:"OFXEXTENSION,omitempty"`
|
||||
ClientRouting String `xml:"PROFRQ>CLIENTROUTING"` // Forced to NONE
|
||||
DtProfUp Date `xml:"PROFRQ>DTPROFUP"`
|
||||
DtProfUp Date `xml:"PROFRQ>DTPROFUP"` // Date and time client last received a profile update
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (r *ProfileRequest) Name() string {
|
||||
return "PROFTRNRQ"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct would be valid OFX if marshalled
|
||||
// into XML/SGML
|
||||
func (r *ProfileRequest) Valid() (bool, error) {
|
||||
// TODO implement
|
||||
r.ClientRouting = "NONE"
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Type returns which message set this message belongs to (which Request
|
||||
// element of type []Message it should appended to)
|
||||
func (r *ProfileRequest) Type() messageType {
|
||||
return ProfRq
|
||||
}
|
||||
|
||||
// SignonInfo provides the requirements to login to a single signon realm. A
|
||||
// signon realm consists of all MessageSets which can be accessed using one set
|
||||
// of login credentials. Most FI's only use one signon realm to make it easier
|
||||
// and less confusing for the user.
|
||||
type SignonInfo struct {
|
||||
XMLName xml.Name `xml:"SIGNONINFO"`
|
||||
SignonRealm String `xml:"SIGNONREALM"`
|
||||
SignonRealm String `xml:"SIGNONREALM"` // The SignonRealm for which this SignonInfo provides information. This SignonInfo is valid for all MessageSets with SignonRealm fields matching this one
|
||||
Min Int `xml:"MIN"` // Minimum number of password characters
|
||||
Max Int `xml:"MAX"` // Maximum number of password characters
|
||||
CharType charType `xml:"CHARTYPE"` // One of ALPHAONLY, NUMERICONLY, ALPHAORNUMERIC, ALPHAANDNUMERIC
|
||||
@ -51,11 +63,13 @@ type SignonInfo struct {
|
||||
AccessTokenReq Boolean `xml:"ACCESSTOKENREQ,omitempty"` // Server requires ACCESSTOKEN to be sent with all requests except profile
|
||||
}
|
||||
|
||||
// MessageSet represents one message set supported by an FI and its
|
||||
// capabilities
|
||||
type MessageSet struct {
|
||||
XMLName xml.Name // <xxxMSGSETVn>
|
||||
Name string // <xxxMSGSETVn> (copy of XMLName.Local)
|
||||
Ver Int `xml:"MSGSETCORE>VER"` // Message set version - should always match 'n' in <xxxMSGSETVn>
|
||||
Url String `xml:"MSGSETCORE>URL"` // URL where messages in this set are to be set
|
||||
Ver Int `xml:"MSGSETCORE>VER"` // Message set version - should always match 'n' in <xxxMSGSETVn> of Name
|
||||
URL String `xml:"MSGSETCORE>URL"` // URL where messages in this set are to be set
|
||||
OfxSec ofxSec `xml:"MSGSETCORE>OFXSEC"` // NONE or 'TYPE 1'
|
||||
TranspSec Boolean `xml:"MSGSETCORE>TRANSPSEC"` // Transport-level security must be used
|
||||
SignonRealm String `xml:"MSGSETCORE>SIGNONREALM"` // Used to identify which SignonInfo to use for to this MessageSet
|
||||
@ -67,8 +81,11 @@ type MessageSet struct {
|
||||
// TODO MessageSet-specific stuff?
|
||||
}
|
||||
|
||||
// MessageSetList is a list of MessageSets (necessary because they must be
|
||||
// manually parsed)
|
||||
type MessageSetList []MessageSet
|
||||
|
||||
// UnmarshalXML handles unmarshalling a MessageSetList element from an XML string
|
||||
func (msl *MessageSetList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
for {
|
||||
var msgset MessageSet
|
||||
@ -106,6 +123,11 @@ func (msl *MessageSetList) UnmarshalXML(d *xml.Decoder, start xml.StartElement)
|
||||
}
|
||||
}
|
||||
|
||||
// ProfileResponse contains a requested profile of the server's capabilities
|
||||
// (which message sets and versions it supports, how to access them, which
|
||||
// languages and which types of synchronization they support, etc.). Note that
|
||||
// if the server does not support ClientRouting=NONE (as we always send with
|
||||
// ProfileRequest), this may be an error)
|
||||
type ProfileResponse struct {
|
||||
XMLName xml.Name `xml:"PROFTRNRS"`
|
||||
TrnUID UID `xml:"TRNUID"`
|
||||
@ -130,15 +152,19 @@ type ProfileResponse struct {
|
||||
Email String `xml:"PROFRS>EMAIL,omitempty"`
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (pr *ProfileResponse) Name() string {
|
||||
return "PROFTRNRS"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct was valid OFX when unmarshalled
|
||||
func (pr *ProfileResponse) Valid() (bool, error) {
|
||||
//TODO implement
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Type returns which message set this message belongs to (which Response
|
||||
// element of type []Message it belongs to)
|
||||
func (pr *ProfileResponse) Type() messageType {
|
||||
return ProfRs
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ NEWFILEUID:NONE
|
||||
ofxgo.MessageSet{
|
||||
Name: "SIGNONMSGSETV1",
|
||||
Ver: 1,
|
||||
Url: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
URL: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
OfxSec: ofxgo.OfxSecNone,
|
||||
TranspSec: true,
|
||||
SignonRealm: "Example Trade",
|
||||
@ -244,7 +244,7 @@ NEWFILEUID:NONE
|
||||
ofxgo.MessageSet{
|
||||
Name: "SIGNUPMSGSETV1",
|
||||
Ver: 1,
|
||||
Url: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
URL: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
OfxSec: ofxgo.OfxSecNone,
|
||||
TranspSec: true,
|
||||
SignonRealm: "Example Trade",
|
||||
@ -256,7 +256,7 @@ NEWFILEUID:NONE
|
||||
ofxgo.MessageSet{
|
||||
Name: "INVSTMTMSGSETV1",
|
||||
Ver: 1,
|
||||
Url: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
URL: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
OfxSec: ofxgo.OfxSecNone,
|
||||
TranspSec: true,
|
||||
SignonRealm: "Example Trade",
|
||||
@ -268,7 +268,7 @@ NEWFILEUID:NONE
|
||||
ofxgo.MessageSet{
|
||||
Name: "SECLISTMSGSETV1",
|
||||
Ver: 1,
|
||||
Url: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
URL: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
OfxSec: ofxgo.OfxSecNone,
|
||||
TranspSec: true,
|
||||
SignonRealm: "Example Trade",
|
||||
@ -280,7 +280,7 @@ NEWFILEUID:NONE
|
||||
ofxgo.MessageSet{
|
||||
Name: "PROFMSGSETV1",
|
||||
Ver: 1,
|
||||
Url: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
URL: "https://ofx.example.com/cgi-ofx/exampleofx",
|
||||
OfxSec: ofxgo.OfxSecNone,
|
||||
TranspSec: true,
|
||||
SignonRealm: "Example Trade",
|
||||
|
@ -60,7 +60,8 @@ func marshalMessageSet(e *xml.Encoder, requests []Message, set messageType) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
// Overwrite the fields in this Request object controlled by the Client
|
||||
// SetClientFields overwrites the fields in this Request object controlled by
|
||||
// the Client
|
||||
func (oq *Request) SetClientFields(c *Client) {
|
||||
oq.Signon.DtClient.Time = time.Now()
|
||||
|
||||
|
48
seclist.go
48
seclist.go
@ -5,12 +5,17 @@ import (
|
||||
"github.com/aclindsa/go/src/encoding/xml"
|
||||
)
|
||||
|
||||
// SecurityID identifies a security by its CUSIP (for US-based FI's, others may
|
||||
// use UniqueID types other than CUSIP)
|
||||
type SecurityID struct {
|
||||
XMLName xml.Name `xml:"SECID"`
|
||||
UniqueID String `xml:"UNIQUEID"` // CUSIP for US FI's
|
||||
UniqueIDType String `xml:"UNIQUEIDTYPE"` // Should always be "CUSIP" for US FI's
|
||||
}
|
||||
|
||||
// SecurityRequest represents a request for one security. It is specified with
|
||||
// a SECID aggregate, a ticker symbol, or an FI assigned identifier (but no
|
||||
// more than one of them at a time)
|
||||
type SecurityRequest struct {
|
||||
XMLName xml.Name `xml:"SECRQ"`
|
||||
// Only one of the next three should be present
|
||||
@ -19,6 +24,8 @@ type SecurityRequest struct {
|
||||
FiID String `xml:"FIID,omitempty"`
|
||||
}
|
||||
|
||||
// SecListRequest represents a request for information (namely price) about one
|
||||
// or more securities
|
||||
type SecListRequest struct {
|
||||
XMLName xml.Name `xml:"SECLISTTRNRQ"`
|
||||
TrnUID UID `xml:"TRNUID"`
|
||||
@ -28,19 +35,29 @@ type SecListRequest struct {
|
||||
Securities []SecurityRequest `xml:"SECLISTRQ>SECRQ,omitempty"`
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (r *SecListRequest) Name() string {
|
||||
return "SECLISTTRNRQ"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct would be valid OFX if marshalled
|
||||
// into XML/SGML
|
||||
func (r *SecListRequest) Valid() (bool, error) {
|
||||
// TODO implement
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Type returns which message set this message belongs to (which Request
|
||||
// element of type []Message it should appended to)
|
||||
func (r *SecListRequest) Type() messageType {
|
||||
return SecListRq
|
||||
}
|
||||
|
||||
// SecListResponse is always empty (except for the transaction UID, status, and
|
||||
// optional client cookie). Its presence signifies that the SecurityList (a
|
||||
// different element from this one) immediately after this element in
|
||||
// Response.SecList was been generated in response to the same SecListRequest
|
||||
// this is a response to.
|
||||
type SecListResponse struct {
|
||||
XMLName xml.Name `xml:"SECLISTTRNRS"`
|
||||
TrnUID UID `xml:"TRNUID"`
|
||||
@ -50,23 +67,31 @@ 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
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (r *SecListResponse) Name() string {
|
||||
return "SECLISTTRNRS"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct was valid OFX when unmarshalled
|
||||
func (r *SecListResponse) Valid() (bool, error) {
|
||||
// TODO implement
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Type returns which message set this message belongs to (which Response
|
||||
// element of type []Message it belongs to)
|
||||
func (r *SecListResponse) Type() messageType {
|
||||
return SecListRs
|
||||
}
|
||||
|
||||
// Security is satisfied by all *Info elements providing information about
|
||||
// securities for SecurityList
|
||||
type Security interface {
|
||||
SecurityType() string
|
||||
}
|
||||
|
||||
// SecInfo represents the generic information about a security. It is included
|
||||
// in most other *Info elements.
|
||||
type SecInfo struct {
|
||||
XMLName xml.Name `xml:"SECINFO"`
|
||||
SecID SecurityID `xml:"SECID"`
|
||||
@ -80,6 +105,7 @@ type SecInfo struct {
|
||||
Memo String `xml:"MEMO,omitempty"`
|
||||
}
|
||||
|
||||
// DebtInfo provides information about a debt security
|
||||
type DebtInfo struct {
|
||||
XMLName xml.Name `xml:"DEBTINFO"`
|
||||
SecInfo SecInfo `xml:"SECINFO"`
|
||||
@ -99,22 +125,29 @@ type DebtInfo struct {
|
||||
FiAssetClass String `xml:"FIASSETCLASS,omitempty"` // FI-defined asset class
|
||||
}
|
||||
|
||||
// SecurityType returns a string representation of this security's type
|
||||
func (i DebtInfo) SecurityType() string {
|
||||
return "DEBTINFO"
|
||||
}
|
||||
|
||||
// AssetPortion represents the percentage of a mutual fund with the given asset
|
||||
// classification
|
||||
type AssetPortion struct {
|
||||
XMLName xml.Name `xml:"PORTION"`
|
||||
AssetClass assetClass `xml:"ASSETCLASS"` // One of DOMESTICBOND, INTLBOND, LARGESTOCK, SMALLSTOCK, INTLSTOCK, MONEYMRKT, OTHER
|
||||
Percent Amount `xml:"PERCENT"` // Percentage of the fund that falls under this asset class
|
||||
}
|
||||
|
||||
// FiAssetPortion represents the percentage of a mutual fund with the given
|
||||
// FI-defined asset classification (AssetPortion should be used for all asset
|
||||
// classifications defined by the assetClass enum)
|
||||
type FiAssetPortion struct {
|
||||
XMLName xml.Name `xml:"FIPORTION"`
|
||||
FiAssetClass String `xml:"FIASSETCLASS,omitempty"` // FI-defined asset class
|
||||
Percent Amount `xml:"PERCENT"` // Percentage of the fund that falls under this asset class
|
||||
}
|
||||
|
||||
// MFInfo provides information about a mutual fund
|
||||
type MFInfo struct {
|
||||
XMLName xml.Name `xml:"MFINFO"`
|
||||
SecInfo SecInfo `xml:"SECINFO"`
|
||||
@ -125,10 +158,12 @@ type MFInfo struct {
|
||||
FiAssetClasses []FiAssetPortion `xml:"FIMFASSETCLASS>FIPORTION"`
|
||||
}
|
||||
|
||||
// SecurityType returns a string representation of this security's type
|
||||
func (i MFInfo) SecurityType() string {
|
||||
return "MFINFO"
|
||||
}
|
||||
|
||||
// OptInfo provides information about an option
|
||||
type OptInfo struct {
|
||||
XMLName xml.Name `xml:"OPTINFO"`
|
||||
SecInfo SecInfo `xml:"SECINFO"`
|
||||
@ -141,10 +176,13 @@ type OptInfo struct {
|
||||
FiAssetClass String `xml:"FIASSETCLASS,omitempty"` // FI-defined asset class
|
||||
}
|
||||
|
||||
// SecurityType returns a string representation of this security's type
|
||||
func (i OptInfo) SecurityType() string {
|
||||
return "OPTINFO"
|
||||
}
|
||||
|
||||
// OtherInfo provides information about a security type not covered by the
|
||||
// other *Info elements
|
||||
type OtherInfo struct {
|
||||
XMLName xml.Name `xml:"OTHERINFO"`
|
||||
SecInfo SecInfo `xml:"SECINFO"`
|
||||
@ -153,10 +191,12 @@ type OtherInfo struct {
|
||||
FiAssetClass String `xml:"FIASSETCLASS,omitempty"` // FI-defined asset class
|
||||
}
|
||||
|
||||
// SecurityType returns a string representation of this security's type
|
||||
func (i OtherInfo) SecurityType() string {
|
||||
return "OTHERINFO"
|
||||
}
|
||||
|
||||
// StockInfo provides information about a security type
|
||||
type StockInfo struct {
|
||||
XMLName xml.Name `xml:"STOCKINFO"`
|
||||
SecInfo SecInfo `xml:"SECINFO"`
|
||||
@ -167,27 +207,35 @@ type StockInfo struct {
|
||||
FiAssetClass String `xml:"FIASSETCLASS,omitempty"` // FI-defined asset class
|
||||
}
|
||||
|
||||
// SecurityType returns a string representation of this security's type
|
||||
func (i StockInfo) SecurityType() string {
|
||||
return "STOCKINFO"
|
||||
}
|
||||
|
||||
// SecurityList is a container for Security objects containaing information
|
||||
// about securities
|
||||
type SecurityList struct {
|
||||
Securities []Security
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (r *SecurityList) Name() string {
|
||||
return "SECLIST"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct was valid OFX when unmarshalled
|
||||
func (r *SecurityList) Valid() (bool, error) {
|
||||
// TODO implement
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Type returns which message set this message belongs to (which Response
|
||||
// element of type []Message it belongs to)
|
||||
func (r *SecurityList) Type() messageType {
|
||||
return SecListRs
|
||||
}
|
||||
|
||||
// UnmarshalXML handles unmarshalling a SecurityList from an SGML/XML string
|
||||
func (r *SecurityList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
for {
|
||||
tok, err := nextNonWhitespaceToken(d)
|
||||
|
13
signon.go
13
signon.go
@ -6,6 +6,8 @@ import (
|
||||
"github.com/aclindsa/go/src/encoding/xml"
|
||||
)
|
||||
|
||||
// SignonRequest identifies and authenticates a user to their FI and is
|
||||
// provided with every Request
|
||||
type SignonRequest struct {
|
||||
XMLName xml.Name `xml:"SONRQ"`
|
||||
DtClient Date `xml:"DTCLIENT"` // Current time on client, overwritten in Client.Request()
|
||||
@ -20,10 +22,13 @@ type SignonRequest struct {
|
||||
ClientUID UID `xml:"CLIENTUID,omitempty"`
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (r *SignonRequest) Name() string {
|
||||
return "SONRQ"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct would be valid OFX if marshalled
|
||||
// into XML/SGML
|
||||
func (r *SignonRequest) Valid() (bool, error) {
|
||||
if len(r.UserID) < 1 || len(r.UserID) > 32 {
|
||||
return false, errors.New("SONRQ>USERID invalid length")
|
||||
@ -40,7 +45,7 @@ func (r *SignonRequest) Valid() (bool, error) {
|
||||
if len(r.Language) == 0 {
|
||||
r.Language = "ENG"
|
||||
} else if len(r.Language) != 3 {
|
||||
return false, fmt.Errorf("SONRQ>LANGUAGE invalid length: \"%s\"\n", r.Language)
|
||||
return false, fmt.Errorf("SONRQ>LANGUAGE invalid length: \"%s\"", r.Language)
|
||||
}
|
||||
if len(r.AppID) < 1 || len(r.AppID) > 5 {
|
||||
return false, errors.New("SONRQ>APPID invalid length")
|
||||
@ -51,6 +56,8 @@ func (r *SignonRequest) Valid() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// SignonResponse is provided with every Response and indicates the success or
|
||||
// failure of the SignonRequest in the corresponding Request
|
||||
type SignonResponse struct {
|
||||
XMLName xml.Name `xml:"SONRS"`
|
||||
Status Status `xml:"STATUS"`
|
||||
@ -66,13 +73,15 @@ type SignonResponse struct {
|
||||
AccessKey String `xml:"ACCESSKEY,omitempty"`
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (r *SignonResponse) Name() string {
|
||||
return "SONRS"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct was valid OFX when unmarshalled
|
||||
func (r *SignonResponse) Valid() (bool, error) {
|
||||
if len(r.Language) != 3 {
|
||||
return false, fmt.Errorf("SONRS>LANGUAGE invalid length: \"%s\"\n", r.Language)
|
||||
return false, fmt.Errorf("SONRS>LANGUAGE invalid length: \"%s\"", r.Language)
|
||||
}
|
||||
return r.Status.Valid()
|
||||
}
|
||||
|
33
signup.go
33
signup.go
@ -5,6 +5,8 @@ import (
|
||||
"github.com/aclindsa/go/src/encoding/xml"
|
||||
)
|
||||
|
||||
// AcctInfoRequest represents a request for the server to provide information
|
||||
// for all of the user's available accounts at this FI
|
||||
type AcctInfoRequest struct {
|
||||
XMLName xml.Name `xml:"ACCTINFOTRNRQ"`
|
||||
TrnUID UID `xml:"TRNUID"`
|
||||
@ -14,19 +16,25 @@ type AcctInfoRequest struct {
|
||||
DtAcctUp Date `xml:"ACCTINFORQ>DTACCTUP"`
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (r *AcctInfoRequest) Name() string {
|
||||
return "ACCTINFOTRNRQ"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct would be valid OFX if marshalled
|
||||
// into XML/SGML
|
||||
func (r *AcctInfoRequest) Valid() (bool, error) {
|
||||
// TODO implement
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Type returns which message set this message belongs to (which Request
|
||||
// element of type []Message it should appended to)
|
||||
func (r *AcctInfoRequest) Type() messageType {
|
||||
return SignupRq
|
||||
}
|
||||
|
||||
// HolderInfo contains the information a FI has about an account-holder
|
||||
type HolderInfo struct {
|
||||
XMLName xml.Name
|
||||
FirstName String `xml:"FIRSTNAME"`
|
||||
@ -45,6 +53,9 @@ type HolderInfo struct {
|
||||
HolderType holderType `xml:"HOLDERTYPE,omitempty"` // One of INDIVIDUAL, JOINT, CUSTODIAL, TRUST, OTHER
|
||||
}
|
||||
|
||||
// BankAcctInfo contains information about a bank account, including how to
|
||||
// access it (BankAcct), and whether it supports downloading transactions
|
||||
// (SupTxDl).
|
||||
type BankAcctInfo struct {
|
||||
XMLName xml.Name `xml:"BANKACCTINFO"`
|
||||
BankAcctFrom BankAcct `xml:"BANKACCTFROM"`
|
||||
@ -59,11 +70,14 @@ type BankAcctInfo struct {
|
||||
SvcStatus svcStatus `xml:"SVCSTATUS"` // One of AVAIL (available, but not yet requested), PEND (requested, but not yet available), ACTIVE
|
||||
}
|
||||
|
||||
// Make pointers to these structs print nicely
|
||||
// String makes pointers to BankAcctInfo structs print nicely
|
||||
func (bai *BankAcctInfo) String() string {
|
||||
return fmt.Sprintf("%+v", *bai)
|
||||
}
|
||||
|
||||
// CCAcctInfo contains information about a credit card account, including how
|
||||
// to access it (CCAcct), and whether it supports downloading transactions
|
||||
// (SupTxDl).
|
||||
type CCAcctInfo struct {
|
||||
XMLName xml.Name `xml:"CCACCTINFO"`
|
||||
CCAcctFrom CCAcct `xml:"CCACCTFROM"`
|
||||
@ -74,11 +88,14 @@ type CCAcctInfo struct {
|
||||
SvcStatus svcStatus `xml:"SVCSTATUS"` // One of AVAIL (available, but not yet requested), PEND (requested, but not yet available), ACTIVE
|
||||
}
|
||||
|
||||
// Make pointers to these structs print nicely
|
||||
// String makes pointers to CCAcctInfo structs print nicely
|
||||
func (ci *CCAcctInfo) String() string {
|
||||
return fmt.Sprintf("%+v", *ci)
|
||||
}
|
||||
|
||||
// InvAcctInfo contains information about an investment account, including how
|
||||
// to access it (InvAcct), and whether it supports downloading transactions
|
||||
// (SupTxDl).
|
||||
type InvAcctInfo struct {
|
||||
XMLName xml.Name `xml:"INVACCTINFO"`
|
||||
InvAcctFrom InvAcct `xml:"INVACCTFROM"`
|
||||
@ -89,11 +106,14 @@ type InvAcctInfo struct {
|
||||
OptionLevel String `xml:"OPTIONLEVEL,omitempty"` // Text desribing option trading privileges
|
||||
}
|
||||
|
||||
// Make pointers to these structs print nicely
|
||||
// String makes pointers to InvAcctInfo structs print nicely
|
||||
func (iai *InvAcctInfo) String() string {
|
||||
return fmt.Sprintf("%+v", *iai)
|
||||
}
|
||||
|
||||
// AcctInfo represents generic account information. It should contain one (and
|
||||
// only one) *AcctInfo element corresponding to the tyep of account it
|
||||
// represents.
|
||||
type AcctInfo struct {
|
||||
XMLName xml.Name `xml:"ACCTINFO"`
|
||||
Name String `xml:"NAME,omitempty"`
|
||||
@ -101,6 +121,7 @@ type AcctInfo struct {
|
||||
Phone String `xml:"PHONE,omitempty"`
|
||||
PrimaryHolder HolderInfo `xml:"HOLDERINFO>PRIMARYHOLDER,omitempty"`
|
||||
SecondaryHolder HolderInfo `xml:"HOLDERINFO>SECONDARYHOLDER,omitempty"`
|
||||
|
||||
// Only one of the rest of the fields will be valid for any given AcctInfo
|
||||
BankAcctInfo *BankAcctInfo `xml:"BANKACCTINFO,omitempty"`
|
||||
CCAcctInfo *CCAcctInfo `xml:"CCACCTINFO,omitempty"`
|
||||
@ -109,6 +130,8 @@ type AcctInfo struct {
|
||||
// TODO BPACCTINFO?
|
||||
}
|
||||
|
||||
// AcctInfoResponse contains the information about all a user's accounts
|
||||
// accessible from this FI
|
||||
type AcctInfoResponse struct {
|
||||
XMLName xml.Name `xml:"ACCTINFOTRNRS"`
|
||||
TrnUID UID `xml:"TRNUID"`
|
||||
@ -119,15 +142,19 @@ type AcctInfoResponse struct {
|
||||
AcctInfo []AcctInfo `xml:"ACCTINFORS>ACCTINFO,omitempty"`
|
||||
}
|
||||
|
||||
// Name returns the name of the top-level transaction XML/SGML element
|
||||
func (air *AcctInfoResponse) Name() string {
|
||||
return "ACCTINFOTRNRS"
|
||||
}
|
||||
|
||||
// Valid returns (true, nil) if this struct was valid OFX when unmarshalled
|
||||
func (air *AcctInfoResponse) Valid() (bool, error) {
|
||||
//TODO implement
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Type returns which message set this message belongs to (which Response
|
||||
// element of type []Message it belongs to)
|
||||
func (air *AcctInfoResponse) Type() messageType {
|
||||
return SignupRs
|
||||
}
|
||||
|
46
types.go
46
types.go
@ -12,8 +12,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Int provides helper methods to unmarshal int64 values from SGML/XML
|
||||
type Int int64
|
||||
|
||||
// UnmarshalXML handles unmarshalling an Int from an SGML/XML string. Leading
|
||||
// and trailing whitespace is ignored.
|
||||
func (i *Int) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var value string
|
||||
|
||||
@ -33,14 +36,19 @@ func (i *Int) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Equal returns true if the two Ints are equal in value
|
||||
func (i Int) Equal(o Int) bool {
|
||||
return i == o
|
||||
}
|
||||
|
||||
// Amount represents non-integer values (or at least values for fields that may
|
||||
// not necessarily be integers)
|
||||
type Amount struct {
|
||||
big.Rat
|
||||
}
|
||||
|
||||
// UnmarshalXML handles unmarshalling an Amount from an SGML/XML string.
|
||||
// Leading and trailing whitespace is ignored.
|
||||
func (a *Amount) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var value string
|
||||
|
||||
@ -61,18 +69,22 @@ func (a *Amount) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String prints a string representation of an Amount
|
||||
func (a Amount) String() string {
|
||||
return strings.TrimRight(strings.TrimRight(a.FloatString(100), "0"), ".")
|
||||
}
|
||||
|
||||
// MarshalXML marshals an Amount to SGML/XML
|
||||
func (a *Amount) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
return e.EncodeElement(a.String(), start)
|
||||
}
|
||||
|
||||
// Equal returns true if two Amounts are equal in value
|
||||
func (a Amount) Equal(o Amount) bool {
|
||||
return (&a).Cmp(&o.Rat) == 0
|
||||
}
|
||||
|
||||
// Date represents OFX date/time values
|
||||
type Date struct {
|
||||
time.Time
|
||||
}
|
||||
@ -86,6 +98,10 @@ var ofxDateFormats = []string{
|
||||
}
|
||||
var ofxDateZoneRegex = regexp.MustCompile(`^([+-]?[0-9]+)(\.([0-9]{2}))?(:([A-Z]+))?$`)
|
||||
|
||||
// UnmarshalXML handles unmarshalling a Date from an SGML/XML string. It
|
||||
// attempts to unmarshal the valid date formats in order of decreasing length
|
||||
// and defaults to GMT if a time zone is not provided, as per the OFX spec.
|
||||
// Leading and trailing whitespace is ignored.
|
||||
func (od *Date) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var value, zone, zoneFormat string
|
||||
err := d.DecodeElement(&value, &start)
|
||||
@ -148,6 +164,7 @@ func (od *Date) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
return errors.New("OFX: Couldn't parse date:" + value)
|
||||
}
|
||||
|
||||
// String returns a string representation of the Date abiding by the OFX spec
|
||||
func (od Date) String() string {
|
||||
format := od.Format(ofxDateFormats[0])
|
||||
zonename, zoneoffset := od.Zone()
|
||||
@ -165,31 +182,39 @@ func (od Date) String() string {
|
||||
|
||||
if len(zonename) > 0 {
|
||||
return format + ":" + zonename + "]"
|
||||
} else {
|
||||
return format + "]"
|
||||
}
|
||||
return format + "]"
|
||||
}
|
||||
|
||||
// MarshalXML marshals a Date to XML
|
||||
func (od *Date) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
return e.EncodeElement(od.String(), start)
|
||||
}
|
||||
|
||||
// Equal returns true if the two Dates represent the same time (time zones are
|
||||
// accounted for when comparing, but are not required to match)
|
||||
func (od Date) Equal(o Date) bool {
|
||||
return od.Time.Equal(o.Time)
|
||||
}
|
||||
|
||||
// NewDate returns a new Date object with the provided date, time, and timezone
|
||||
func NewDate(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) *Date {
|
||||
return &Date{Time: time.Date(year, month, day, hour, min, sec, nsec, loc)}
|
||||
}
|
||||
|
||||
var gmt = time.FixedZone("GMT", 0)
|
||||
|
||||
// NewDateGMT returns a new Date object with the provided date and time in the
|
||||
// GMT timezone
|
||||
func NewDateGMT(year int, month time.Month, day, hour, min, sec, nsec int) *Date {
|
||||
return &Date{Time: time.Date(year, month, day, hour, min, sec, nsec, gmt)}
|
||||
}
|
||||
|
||||
// String provides helper methods to unmarshal OFX string values from SGML/XML
|
||||
type String string
|
||||
|
||||
// UnmarshalXML handles unmarshalling a String from an SGML/XML string. Leading
|
||||
// and trailing whitespace is ignored.
|
||||
func (os *String) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var value string
|
||||
err := d.DecodeElement(&value, &start)
|
||||
@ -200,16 +225,21 @@ func (os *String) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string
|
||||
func (os *String) String() string {
|
||||
return string(*os)
|
||||
}
|
||||
|
||||
// Equal returns true if the two Strings are equal in value
|
||||
func (os String) Equal(o String) bool {
|
||||
return os == o
|
||||
}
|
||||
|
||||
// Boolean provides helper methods to unmarshal bool values from OFX SGML/XML
|
||||
type Boolean bool
|
||||
|
||||
// UnmarshalXML handles unmarshalling a Boolean from an SGML/XML string.
|
||||
// Leading and trailing whitespace is ignored.
|
||||
func (ob *Boolean) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var value string
|
||||
err := d.DecodeElement(&value, &start)
|
||||
@ -228,6 +258,7 @@ func (ob *Boolean) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalXML marshals a Boolean to XML
|
||||
func (ob *Boolean) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
if *ob {
|
||||
return e.EncodeElement("Y", start)
|
||||
@ -235,16 +266,21 @@ func (ob *Boolean) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
return e.EncodeElement("N", start)
|
||||
}
|
||||
|
||||
// String returns a string representation of a Boolean value
|
||||
func (ob *Boolean) String() string {
|
||||
return fmt.Sprintf("%v", *ob)
|
||||
}
|
||||
|
||||
// Equal returns true if the two Booleans are the same
|
||||
func (ob Boolean) Equal(o Boolean) bool {
|
||||
return ob == o
|
||||
}
|
||||
|
||||
// UID represents an UID according to the OFX spec
|
||||
type UID string
|
||||
|
||||
// UnmarshalXML handles unmarshalling an UID from an SGML/XML string. Leading
|
||||
// and trailing whitespace is ignored.
|
||||
func (ou *UID) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var value string
|
||||
err := d.DecodeElement(&value, &start)
|
||||
@ -255,8 +291,8 @@ func (ou *UID) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The OFX specification recommends that UIDs follow the standard UUID
|
||||
// 36-character format
|
||||
// RecommendedFormat returns true iff this UID meets the OFX specification's
|
||||
// recommendation that UIDs follow the standard UUID 36-character format
|
||||
func (ou UID) RecommendedFormat() (bool, error) {
|
||||
if len(ou) != 36 {
|
||||
return false, errors.New("UID not 36 characters long")
|
||||
@ -267,10 +303,12 @@ func (ou UID) RecommendedFormat() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Equal returns true if the two UIDs are the same
|
||||
func (ou UID) Equal(o UID) bool {
|
||||
return ou == o
|
||||
}
|
||||
|
||||
// RandomUID creates a new randomly-generated UID
|
||||
func RandomUID() (*UID, error) {
|
||||
uidbytes := make([]byte, 16)
|
||||
n, err := rand.Read(uidbytes[:])
|
||||
|
Loading…
Reference in New Issue
Block a user