From 5f6ac135e9be2cace69912767d8b6a1514cfe094 Mon Sep 17 00:00:00 2001 From: Rauno Date: Tue, 16 Oct 2018 20:41:32 +0200 Subject: [PATCH] Add smart statement export --- api.go | 32 ++++++++++++++++++++++++++++++-- cmd/n26/n26.go | 30 ++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/api.go b/api.go index 5df98e1..ffaafca 100644 --- a/api.go +++ b/api.go @@ -3,10 +3,13 @@ package n26 import ( "context" "encoding/json" + "errors" "fmt" + "io" "io/ioutil" "net/http" "net/url" + "os" "golang.org/x/oauth2" ) @@ -224,7 +227,7 @@ func NewClient(a Auth) (*Client, error) { return (*Client)(c.Client(ctx, tok)), nil } -func (c *Client) n26Request(requestMethod, endpoint string, params map[string]string) []byte { +func (c *Client) n26RawRequest(requestMethod, endpoint string, params map[string]string, callback func(io.Reader) error) error { var req *http.Request var err error @@ -244,7 +247,15 @@ func (c *Client) n26Request(requestMethod, endpoint string, params map[string]st res, err := (*http.Client)(c).Do(req) check(err) defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) + return callback(res.Body) +} +func (c *Client) n26Request(requestMethod, endpoint string, params map[string]string) []byte { + var body []byte + err := c.n26RawRequest(requestMethod, endpoint, params, func(r io.Reader) error { + var err error + body, err = ioutil.ReadAll(r) + return err + }) check(err) return body } @@ -358,6 +369,23 @@ func (auth *Client) GetTransactions(from, to TimeStamp, limit string) (*Transact return transactions, nil } +// Get transactions for the given time window as N26 CSV file. Stored as 'smrt_statement.csv' +func (auth *Client) GetSmartStatementCsv(from, to TimeStamp) error { + //Filter is applied only if both values are set + if from.IsZero() || to.IsZero() { + return errors.New("Start and end time must be set") + } + return auth.n26RawRequest(http.MethodGet, fmt.Sprintf("/api/smrt/reports/%v/%v/statements", from.AsMillis(), to.AsMillis()), nil, func(r io.Reader) error { + file, err := os.Create("smrt_statement.csv") + if err != nil { + return err + } + defer file.Close() + _, err = io.Copy(file, r) + return err + }) +} + func (auth *Client) GetStatements(retType string) (string, *Statements) { body := auth.n26Request(http.MethodGet, "/api/statements", nil) statements := &Statements{} diff --git a/cmd/n26/n26.go b/cmd/n26/n26.go index feb4e25..1ef0311 100644 --- a/cmd/n26/n26.go +++ b/cmd/n26/n26.go @@ -228,8 +228,8 @@ func main() { }, { Name: "transactions", - Usage: "list your past transactions. Supports CSV output", - ArgsUsage: "[csv|json|table]", + Usage: "list your past transactions. Supports CSV output.", + ArgsUsage: "[csv|json|table|smartcsv]", Flags: []cli.Flag{ cli.StringFlag{Name: "limit", Value: "10", Usage: "retrieve last N transactions. Default to 10."}, cli.StringFlag{Name: "from", Usage: "retrieve transactions from this date. " + @@ -239,18 +239,32 @@ func main() { }, Action: func(c *cli.Context) (err error) { const dateFormat = "2006-01-02" + var from, to n26.TimeStamp + if c.IsSet("from") { + from.Time, err = time.Parse(dateFormat, c.String("from")) + check(err) + } + if c.IsSet("to") { + to.Time, err = time.Parse(dateFormat, c.String("to")) + check(err) + } API, err := authentication() check(err) + + if c.Args().First() == "smartcsv" { + if from.IsZero() || to.IsZero() { + fmt.Println("Start and end time must be set for smart CSV!") + return nil + } + err = API.GetSmartStatementCsv(from, to) + fmt.Println("Report saved as smrt_statement.csv.") + return + } writer, err := getTransactionWriter(c.Args().First()) check(err) limit := c.String("limit") var transactions *n26.Transactions - if c.IsSet("from") && c.IsSet("to") { - var from, to n26.TimeStamp - from.Time, err = time.Parse(dateFormat, c.String("from")) - check(err) - to.Time, err = time.Parse(dateFormat, c.String("to")) - check(err) + if !from.IsZero() && !to.IsZero() { transactions, err = API.GetTransactions(from, to, limit) } else { transactions, err = API.GetLastTransactions(limit)