diff --git a/src/bank/teller/teller.go b/src/bank/teller/teller.go new file mode 100644 index 0000000..1fc5cdd --- /dev/null +++ b/src/bank/teller/teller.go @@ -0,0 +1,59 @@ +package teller + +import ( + "context" + "crypto/tls" + "encoding/json" + "net/http" + "time" + + "gogs.inhome.blapointe.com/ana-ledger/src/bank" +) + +type Client struct { + cert tls.Certificate +} + +var _ bank.Agg = Client{} + +func New() (Client, error) { + cert, err := tls.LoadX509KeyPair("./certificate.pem", "./private_key.pem") // TODO + return Client{cert: cert}, err +} + +func (c Client) Accounts(ctx context.Context) ([]bank.Account, error) { + var result []bank.Account + err := c.get(ctx, "https://api.teller.io/accounts", &result) + return result, err +} + +func (c Client) Transactions(ctx context.Context, a bank.Account) ([]bank.Transaction, error) { + var result []bank.Transaction + err := c.get(ctx, "https://api.teller.io/accounts/"+a.Account+"/transactions", &result) + return result, err +} + +func (c Client) get(ctx context.Context, url string, ptr interface{}) error { + httpc := &http.Client{ + Timeout: time.Second, + Transport: &http.Transport{ + DisableKeepAlives: true, + TLSClientConfig: &tls.Config{Certificates: []tls.Certificate{c.cert}}, + }, + } + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return err + } + req.SetBasicAuth("test_token_bfu2cyvq3il6o", "") // TODO + req = req.WithContext(ctx) + + resp, err := httpc.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return json.NewDecoder(resp.Body).Decode(ptr) +} diff --git a/src/bank/teller/teller_integration_test.go b/src/bank/teller/teller_integration_test.go index 2b0d491..9f47a76 100644 --- a/src/bank/teller/teller_integration_test.go +++ b/src/bank/teller/teller_integration_test.go @@ -1,53 +1,39 @@ +//go:build integration + package teller_test import ( - "crypto/tls" - "io" - "net/http" + "context" "testing" - "time" + + "gogs.inhome.blapointe.com/ana-ledger/src/bank/teller" ) -func TestIntegration(t *testing.T) { - //curl --cert certificate.pem --cert-key private_key.pem --auth test_token_bfu2cyvq3il6o: https://api.teller.io/accounts - cert, err := tls.LoadX509KeyPair("./certificate.pem", "./private_key.pem") +func Test(t *testing.T) { + c, err := teller.New() if err != nil { t.Fatal(err) } - c := &http.Client{ - Timeout: time.Second, - Transport: &http.Transport{ - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{Certificates: []tls.Certificate{cert}}, - }, + ctx := context.Background() + + accounts, err := c.Accounts(ctx) + if err != nil { + t.Fatal(err) } - //curl --cert certificate.pem --cert-key private_key.pem --auth test_token_bfu2cyvq3il6o: https://api.teller.io/accounts - for _, url := range []string{ - "https://api.teller.io/accounts", - "https://api.teller.io/accounts/acc_pdvv4810fi9hmrcn6g000/transactions", - } { - url := url - t.Run(url, func(t *testing.T) { - req, err := http.NewRequest(http.MethodGet, url, nil) + for _, account := range accounts { + account := account + t.Run(account.Account, func(t *testing.T) { + transactions, err := c.Transactions(ctx, account) if err != nil { t.Fatal(err) } - req.SetBasicAuth("test_token_bfu2cyvq3il6o", "") - resp, err := c.Do(req) - if err != nil { - t.Fatal(err) + for i, tr := range transactions { + t.Logf("[%d] %+v", i, tr) } - defer resp.Body.Close() - - body, _ := io.ReadAll(resp.Body) - if code := resp.StatusCode; code >= 300 { - t.Fatalf("(%d) %s", code, body) - } - - t.Logf("(%d) %s", resp.StatusCode, body) }) + break } } diff --git a/src/bank/teller/teller_manual_test.go b/src/bank/teller/teller_manual_test.go new file mode 100644 index 0000000..2f66771 --- /dev/null +++ b/src/bank/teller/teller_manual_test.go @@ -0,0 +1,55 @@ +//go:build manual + +package teller_test + +import ( + "crypto/tls" + "io" + "net/http" + "testing" + "time" +) + +func TestIntegration(t *testing.T) { + //curl --cert certificate.pem --cert-key private_key.pem --auth test_token_bfu2cyvq3il6o: https://api.teller.io/accounts + cert, err := tls.LoadX509KeyPair("./certificate.pem", "./private_key.pem") + if err != nil { + t.Fatal(err) + } + + c := &http.Client{ + Timeout: time.Second, + Transport: &http.Transport{ + DisableKeepAlives: true, + TLSClientConfig: &tls.Config{Certificates: []tls.Certificate{cert}}, + }, + } + //curl --cert certificate.pem --cert-key private_key.pem --auth test_token_bfu2cyvq3il6o: https://api.teller.io/accounts + + for _, url := range []string{ + "https://api.teller.io/accounts", + "https://api.teller.io/accounts/acc_pdvv4810fi9hmrcn6g000/transactions", + } { + url := url + t.Run(url, func(t *testing.T) { + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + t.Fatal(err) + } + req.SetBasicAuth("test_token_bfu2cyvq3il6o", "") + + resp, err := c.Do(req) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + if code := resp.StatusCode; code >= 300 { + t.Fatalf("(%d) %s", code, body) + } + + t.Logf("(%d) %s", resp.StatusCode, body) + }) + } +} diff --git a/src/bank/types.go b/src/bank/types.go new file mode 100644 index 0000000..d89771d --- /dev/null +++ b/src/bank/types.go @@ -0,0 +1,26 @@ +package bank + +type Agg interface { +} + +type Account struct { + Institution struct { + Name string `json:"name"` + } `json:"institution"` + Name string `json:"last_four"` + Account string `json:"id"` +} + +type Transaction struct { + Amount string `json:"amount"` + Details struct { + ProcessingStatus string `json:"processing_status"` + CounterParty struct { + Name string `json:"name"` + } `json:"counterparty"` + } `json:"details"` + Description string `json:"description"` + Date string `json:"date"` + Type string `json:"fee"` + Status string `json:"status"` +}