From f9d4402a9dd0fbe850cc2031fdcb35a7a33d14c9 Mon Sep 17 00:00:00 2001 From: Guilherme Thomazi Bonicontro Date: Thu, 19 Jul 2018 17:15:18 +0200 Subject: [PATCH 1/4] adding version constant --- cmd/n26/n26.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/n26/n26.go b/cmd/n26/n26.go index bdfc26f..fa07cc6 100644 --- a/cmd/n26/n26.go +++ b/cmd/n26/n26.go @@ -14,6 +14,10 @@ import ( "github.com/urfave/cli" ) +const ( + appVersion = "1.4.2" +) + func check(e error) { if e != nil { panic(e) @@ -33,7 +37,7 @@ func authentication() (*n26.Client, error) { check(err) password = string(maskedPass) } - return n26.NewClient(n26.Auth{username, password}) + return n26.NewClient(n26.Auth{UserName: username, Password: password}) } // Interface for generic data writer that has a header and data table e.g. table writer and csv writer @@ -46,7 +50,7 @@ type transactionWriter interface { func main() { app := cli.NewApp() - app.Version = "1.4.1" + app.Version = appVersion app.UsageText = "n26 command [json|csv|statement ID]" app.Name = "N26" app.Usage = "your N26 Bank financial information on the command line" From 32ed7e734e0ebde4c1ef247a084a67a4543a5214 Mon Sep 17 00:00:00 2001 From: Guilherme Thomazi Bonicontro Date: Thu, 19 Jul 2018 18:20:53 +0200 Subject: [PATCH 2/4] block/unblock card feature --- api.go | 62 ++++++++++++++++++++++++++++++++++---------------- cmd/n26/n26.go | 27 +++++++++++++++++++++- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/api.go b/api.go index abe689c..c3f4827 100644 --- a/api.go +++ b/api.go @@ -4,10 +4,11 @@ import ( "context" "encoding/json" "fmt" - "golang.org/x/oauth2" "io/ioutil" "net/http" "net/url" + + "golang.org/x/oauth2" ) const apiURL = "https://api.tech26.de" @@ -105,13 +106,13 @@ type Cards []struct { PublicToken interface{} `json:"publicToken"` Pan interface{} `json:"pan"` MaskedPan string `json:"maskedPan"` - ExpirationDate int64 `json:"expirationDate"` + ExpirationDate TimeStamp `json:"expirationDate"` CardType string `json:"cardType"` Status string `json:"status"` CardProduct interface{} `json:"cardProduct"` CardProductType string `json:"cardProductType"` - PinDefined interface{} `json:"pinDefined"` - CardActivated interface{} `json:"cardActivated"` + PinDefined TimeStamp `json:"pinDefined"` + CardActivated TimeStamp `json:"cardActivated"` UsernameOnCard string `json:"usernameOnCard"` ExceetExpressCardDelivery interface{} `json:"exceetExpressCardDelivery"` Membership interface{} `json:"membership"` @@ -202,18 +203,31 @@ func NewClient(a Auth) (*Client, error) { return (*Client)(c.Client(ctx, tok)), nil } -func (c *Client) n26Request(endpoint string, params map[string]string) []byte { +func (c *Client) n26Request(requestMethod, endpoint string, params map[string]string) []byte { + var req *http.Request + var err error + u, _ := url.ParseRequestURI(apiURL) u.Path = endpoint - u.RawQuery = mapToQuery(params).Encode() - res, _ := (*http.Client)(c).Get(u.String()) - defer res.Body.Close() - body, _ := ioutil.ReadAll(res.Body) + switch requestMethod { + case http.MethodGet: + req, err = http.NewRequest(http.MethodGet, u.String(), nil) + check(err) + case http.MethodPost: + req, err = http.NewRequest(http.MethodPost, u.String(), nil) + check(err) + } + res, err := (*http.Client)(c).Do(req) + check(err) + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + check(err) return body } + func mapToQuery(params map[string]string) url.Values { values := url.Values{} for k, v := range params { @@ -223,7 +237,7 @@ func mapToQuery(params map[string]string) url.Values { } func (auth *Client) GetBalance(retType string) (string, *Balance) { - body := auth.n26Request("/api/accounts", nil) + body := auth.n26Request(http.MethodGet, "/api/accounts", nil) balance := &Balance{} check(json.Unmarshal(body, &balance)) identedJSON, _ := json.MarshalIndent(&balance, "", " ") @@ -234,7 +248,7 @@ func (auth *Client) GetBalance(retType string) (string, *Balance) { } func (auth *Client) GetInfo(retType string) (string, *PersonalInfo) { - body := auth.n26Request("/api/me", nil) + body := auth.n26Request(http.MethodGet, "/api/me", nil) info := &PersonalInfo{} check(json.Unmarshal(body, &info)) identedJSON, _ := json.MarshalIndent(&info, "", " ") @@ -245,7 +259,7 @@ func (auth *Client) GetInfo(retType string) (string, *PersonalInfo) { } func (auth *Client) GetStatus(retType string) (string, *Statuses) { - body := auth.n26Request("/api/me/statuses", nil) + body := auth.n26Request(http.MethodGet, "/api/me/statuses", nil) status := &Statuses{} check(json.Unmarshal(body, &status)) identedJSON, _ := json.MarshalIndent(&status, "", " ") @@ -256,7 +270,7 @@ func (auth *Client) GetStatus(retType string) (string, *Statuses) { } func (auth *Client) GetAddresses(retType string) (string, *Addresses) { - body := auth.n26Request("/api/addresses", nil) + body := auth.n26Request(http.MethodGet, "/api/addresses", nil) addresses := &Addresses{} check(json.Unmarshal(body, &addresses)) identedJSON, _ := json.MarshalIndent(&addresses, "", " ") @@ -267,7 +281,7 @@ func (auth *Client) GetAddresses(retType string) (string, *Addresses) { } func (auth *Client) GetCards(retType string) (string, *Cards) { - body := auth.n26Request("/api/v2/cards", nil) + body := auth.n26Request(http.MethodGet, "/api/v2/cards", nil) cards := &Cards{} check(json.Unmarshal(body, &cards)) identedJSON, _ := json.MarshalIndent(&cards, "", " ") @@ -278,7 +292,7 @@ func (auth *Client) GetCards(retType string) (string, *Cards) { } func (auth *Client) GetLimits(retType string) (string, *Limits) { - body := auth.n26Request("/api/settings/account/limits", nil) + body := auth.n26Request(http.MethodGet, "/api/settings/account/limits", nil) limits := &Limits{} check(json.Unmarshal(body, &limits)) identedJSON, _ := json.MarshalIndent(&limits, "", " ") @@ -289,7 +303,7 @@ func (auth *Client) GetLimits(retType string) (string, *Limits) { } func (auth *Client) GetContacts(retType string) (string, *Contacts) { - body := auth.n26Request("/api/smrt/contacts", nil) + body := auth.n26Request(http.MethodGet, "/api/smrt/contacts", nil) contacts := &Contacts{} check(json.Unmarshal(body, &contacts)) identedJSON, _ := json.MarshalIndent(&contacts, "", " ") @@ -313,7 +327,7 @@ func (auth *Client) GetTransactions(from, to TimeStamp) (*Transactions, error) { params["from"] = fmt.Sprint(from.AsMillis()) params["to"] = fmt.Sprint(to.AsMillis()) } - body := auth.n26Request("/api/smrt/transactions", params) + body := auth.n26Request(http.MethodGet, "/api/smrt/transactions", params) transactions := &Transactions{} if err := json.Unmarshal(body, &transactions); err != nil { return nil, err @@ -322,7 +336,7 @@ func (auth *Client) GetTransactions(from, to TimeStamp) (*Transactions, error) { } func (auth *Client) GetStatements(retType string) (string, *Statements) { - body := auth.n26Request("/api/statements", nil) + body := auth.n26Request(http.MethodGet, "/api/statements", nil) statements := &Statements{} check(json.Unmarshal(body, &statements)) identedJSON, _ := json.MarshalIndent(&statements, "", " ") @@ -333,7 +347,7 @@ func (auth *Client) GetStatements(retType string) (string, *Statements) { } func (auth *Client) GetStatementPDF(ID string) { - body := auth.n26Request(fmt.Sprintf("%s%s", "/api/statements/", ID), nil) + body := auth.n26Request(http.MethodGet, fmt.Sprintf("%s%s", "/api/statements/", ID), nil) ioutil.WriteFile( fmt.Sprintf("%s.pdf", ID), body, @@ -341,6 +355,16 @@ func (auth *Client) GetStatementPDF(ID string) { ) } +func (auth *Client) BlockCard(ID string) { + _ = auth.n26Request(http.MethodPost, fmt.Sprintf("/api/cards/%s/block", ID), nil) + fmt.Printf("\nYour card with ID: %s is DISABLED\n\n", ID) +} + +func (auth *Client) UnblockCard(ID string) { + _ = auth.n26Request(http.MethodPost, fmt.Sprintf("/api/cards/%s/unblock", ID), nil) + fmt.Printf("\nYour card with ID: %s is ACTIVE\n\n", ID) +} + func check(e error) { if e != nil { panic(e) diff --git a/cmd/n26/n26.go b/cmd/n26/n26.go index fa07cc6..bd3a70a 100644 --- a/cmd/n26/n26.go +++ b/cmd/n26/n26.go @@ -160,14 +160,17 @@ func main() { for _, card := range *cards { data = append(data, []string{ + card.ID, card.UsernameOnCard, card.CardType, card.CardProductType, card.MaskedPan, + card.ExpirationDate.String(), + card.Status, }, ) } - NewTableWriter().WriteData([]string{"Name on Card", "Type", "Product type", "Number"}, data) + NewTableWriter().WriteData([]string{"ID", "Name on Card", "Type", "Product type", "Number", "Expiration Date", "Status"}, data) } return nil }, @@ -289,6 +292,28 @@ func main() { return nil }, }, + { + Name: "block", + Usage: "blocks a card", + ArgsUsage: "[card ID]", + Action: func(c *cli.Context) error { + API, err := authentication() + check(err) + API.BlockCard(c.Args().First()) + return nil + }, + }, + { + Name: "unblock", + Usage: "unblocks a card", + ArgsUsage: "[card ID]", + Action: func(c *cli.Context) error { + API, err := authentication() + check(err) + API.UnblockCard(c.Args().First()) + return nil + }, + }, } sort.Sort(cli.CommandsByName(app.Commands)) From dbb3d473529b1d13327980e2dee4e990ea4eee9e Mon Sep 17 00:00:00 2001 From: Guilherme Thomazi Bonicontro Date: Thu, 19 Jul 2018 18:23:55 +0200 Subject: [PATCH 3/4] updating getstatement url --- api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.go b/api.go index c3f4827..253baf6 100644 --- a/api.go +++ b/api.go @@ -347,7 +347,7 @@ func (auth *Client) GetStatements(retType string) (string, *Statements) { } func (auth *Client) GetStatementPDF(ID string) { - body := auth.n26Request(http.MethodGet, fmt.Sprintf("%s%s", "/api/statements/", ID), nil) + body := auth.n26Request(http.MethodGet, fmt.Sprintf("/api/statements/%s", ID), nil) ioutil.WriteFile( fmt.Sprintf("%s.pdf", ID), body, From 8741cdf82c6c9794aa8c95a350d32112da7e9eb1 Mon Sep 17 00:00:00 2001 From: Guilherme Thomazi Bonicontro Date: Tue, 24 Jul 2018 16:33:29 +0200 Subject: [PATCH 4/4] feat: adding goreleaser --- .goreleaser.yml | 43 +++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 19 ++++++++----------- 2 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 .goreleaser.yml diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..665c281 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,43 @@ +# This is an example goreleaser.yaml file with some sane defaults. +# Make sure to check the documentation at http://goreleaser.com +builds: +- env: + - CGO_ENABLED=0 + main: ./cmd/n26 + binary: n26 + goos: + - linux + - darwin + - windows + - freebsd + goarch: + - amd64 +archive: + name_template: "{{ .ProjectName }}-cli-{{ .Version }}-{{ .Os }}-{{ .Arch }}" + format: zip + files: + - none* +checksum: + name_template: "checksums.txt" +release: + github: + owner: guitmz + name: n26 + name_template: "{{.Version}}" +changelog: + sort: asc + filters: + exclude: + - "typo" + - "^docs:" + - "^test:" +brew: + github: + owner: guitmz + name: homebrew-tools + + homepage: "https://github.com/guitmz/n26" + description: "CLI tool for N26 Bank" + test: | + system "#{bin}/n26", "--version" + diff --git a/.travis.yml b/.travis.yml index f8e25bb..cf96052 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,8 @@ -sudo: required - -services: - - docker - -before_script: - - docker build -t guitmz/n26 . - -script: - - docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" - - docker push guitmz/n26 +language: go +deploy: +- provider: script + skip_cleanup: true + script: curl -sL https://git.io/goreleaser | bash + on: + tags: true + condition: $TRAVIS_OS_NAME = linux