From c01306942f8d11cbfad8e02058dfb8b52a42779b Mon Sep 17 00:00:00 2001 From: Tobi Nehrlich Date: Sat, 24 Jun 2023 20:48:08 +0200 Subject: [PATCH] Add cards and user commands (#15) --- cmd/viseca-cli/main.go | 2 + go.sum | 2 - internal/app/cards.go | 44 ++++++++++++++ internal/app/login.go | 13 +++++ internal/app/transactions.go | 5 +- internal/app/user.go | 42 ++++++++++++++ pkg/viseca/card.go | 19 ++++++ pkg/viseca/types.go | 109 +++++++++++++++++++++++++++++++++++ pkg/viseca/user.go | 22 +++++++ pkg/viseca/viseca.go | 24 ++++++++ 10 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 internal/app/cards.go create mode 100644 internal/app/user.go create mode 100644 pkg/viseca/user.go diff --git a/cmd/viseca-cli/main.go b/cmd/viseca-cli/main.go index 9090aca..2a29a66 100644 --- a/cmd/viseca-cli/main.go +++ b/cmd/viseca-cli/main.go @@ -28,6 +28,8 @@ func main() { }, Commands: []*cli.Command{ app.NewTransactionsCommand(), + app.NewCardsCommand(), + app.NewUserCommand(), }, } diff --git a/go.sum b/go.sum index 5b0c6f8..fe42f06 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,6 @@ github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= diff --git a/internal/app/cards.go b/internal/app/cards.go new file mode 100644 index 0000000..160a020 --- /dev/null +++ b/internal/app/cards.go @@ -0,0 +1,44 @@ +package app + +import ( + "context" + "encoding/json" + "os" + "time" + + "github.com/anothertobi/viseca-exporter/pkg/viseca" + "github.com/urfave/cli/v2" +) + +// NewCardsCommand creates a new cards CLI command +func NewCardsCommand() *cli.Command { + return &cli.Command{ + Name: "cards", + Usage: "list all cards", + Action: func(cCtx *cli.Context) error { + return cards(cCtx) + }, + } +} + +func cards(cCtx *cli.Context) error { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + visecaClient, err := loginCLI(ctx, cCtx) + if err != nil { + return err + } + + cardListOptions := viseca.NewDefaultCardListOptions() + cards, err := visecaClient.ListCards(ctx, cardListOptions) + if err != nil { + return err + } + + encoder := json.NewEncoder(os.Stdout) + + encoder.Encode(cards) + + return nil +} diff --git a/internal/app/login.go b/internal/app/login.go index 588c50a..d7baf3b 100644 --- a/internal/app/login.go +++ b/internal/app/login.go @@ -10,8 +10,21 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/anothertobi/viseca-exporter/pkg/viseca" + "github.com/urfave/cli/v2" ) +func loginCLI(ctx context.Context, cCtx *cli.Context) (*viseca.Client, error) { + username := cCtx.String("username") + password := cCtx.String("password") + + visecaClient, err := login(ctx, username, password) + if err != nil { + return nil, err + } + + return visecaClient, nil +} + func login(ctx context.Context, username string, password string) (*viseca.Client, error) { cookieJar, err := cookiejar.New(nil) if err != nil { diff --git a/internal/app/transactions.go b/internal/app/transactions.go index 3eda3ea..2e1057e 100644 --- a/internal/app/transactions.go +++ b/internal/app/transactions.go @@ -49,10 +49,7 @@ func transactions(cCtx *cli.Context) error { } cardID := cCtx.Args().First() - username := cCtx.String("username") - password := cCtx.String("password") - - visecaClient, err := login(ctx, username, password) + visecaClient, err := loginCLI(ctx, cCtx) if err != nil { return err } diff --git a/internal/app/user.go b/internal/app/user.go new file mode 100644 index 0000000..f58e566 --- /dev/null +++ b/internal/app/user.go @@ -0,0 +1,42 @@ +package app + +import ( + "context" + "encoding/json" + "os" + "time" + + "github.com/urfave/cli/v2" +) + +// NewUserCommand creates a new user CLI command +func NewUserCommand() *cli.Command { + return &cli.Command{ + Name: "user", + Usage: "get user", + Action: func(cCtx *cli.Context) error { + return user(cCtx) + }, + } +} + +func user(cCtx *cli.Context) error { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + visecaClient, err := loginCLI(ctx, cCtx) + if err != nil { + return err + } + + user, err := visecaClient.GetUser(ctx) + if err != nil { + return err + } + + encoder := json.NewEncoder(os.Stdout) + + encoder.Encode(user) + + return nil +} diff --git a/pkg/viseca/card.go b/pkg/viseca/card.go index 2ea59ff..88ecbef 100644 --- a/pkg/viseca/card.go +++ b/pkg/viseca/card.go @@ -51,3 +51,22 @@ func (client *Client) ListAllTransactionsOpts(ctx context.Context, card string, return transactions, nil } + +// ListCards returns all cards for the given CardListOptions. +func (client *Client) ListCards(ctx context.Context, cardListOptions CardListOptions) (*[]Card, error) { + request, err := client.NewRequest("cards", "GET", nil) + if err != nil { + return nil, err + } + + addCardListOptions(request.URL, cardListOptions) + + cards := &[]Card{} + + _, err = client.Do(ctx, request, cards) + if err != nil { + return nil, err + } + + return cards, err +} diff --git a/pkg/viseca/types.go b/pkg/viseca/types.go index 3bc0320..87eecf9 100644 --- a/pkg/viseca/types.go +++ b/pkg/viseca/types.go @@ -41,3 +41,112 @@ type PFMCategory struct { type TransactionLinks struct { Transactiondetails string `json:"transactiondetails"` } + +type User struct { + VisecaOneID string `json:"visecaOneId"` + Email string `json:"email"` + EmailStatus string `json:"emailStatus"` + Language string `json:"language"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + Gender string `json:"gender"` + MaskedPhoneNumber string `json:"maskedPhoneNumber"` + LastLoginDate string `json:"lastLoginDate"` + LastLogoutType string `json:"lastLogoutType"` + DefaultChannelType string `json:"defaultChannelType"` + AppDescription string `json:"appDescription"` + AppRegistrationDate string `json:"appRegistrationDate"` +} + +type Card struct { + ID string `json:"cardId"` + CardSwitch CardSwitch `json:"switch"` + MaskedCardNumber string `json:"maskedCardNumber"` + CardAccountNr string `json:"cardAccountNr"` + CardHolder CardHolder `json:"cardHolder"` + CardStatus CardStatus `json:"cardStatus"` + CardName string `json:"cardName"` + CardDescription string `json:"cardDescription"` + ExpirationDate string `json:"expirationDate"` + ProductType string `json:"productType"` + ProductLine string `json:"productLine"` + CreditIndicator string `json:"creditIndicator"` + AvailableReplacementReasons []string `json:"availableReplacementReasons"` + BonusProgram []string `json:"bonusProgram"` + MainBonusProgram string `json:"mainBonusProgram"` + Currency string `json:"currency"` + CardLimit float32 `json:"cardLimit"` + ActiveCurrency string `json:"activeCurrency"` + ActiveLimit float32 `json:"activeLimit"` + IsSelfIssued bool `json:"isSelfIssued"` + CardScheme string `json:"cardScheme"` + EmbossingLine string `json:"embossingLine"` + CardType CardType `json:"cardType"` + CallCenter string `json:"callCenter"` + CardImageDetails CardImageDetails `json:"cardImageDetails"` + CardLinks CardLinks `json:"links"` + CardGrants CardGrants `json:"grants"` +} + +type CardSwitch struct { + Reason string `json:"reason"` +} + +type CardHolder struct { + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + BirthDate string `json:"birthDate"` + Nationality string `json:"nationality"` + IsCurrentUser bool `json:"isCurrentUser"` +} + +type CardStatus struct { + Value string `json:"value"` + AdvValue string `json:"advValue"` + Description string `json:"description"` + ChangeDate string `json:"changeDate"` +} + +type CardType struct { + Value string `json:"value"` + Description string `json:"description"` +} + +type CardImageDetails struct { + URL string `json:"url"` + TemplateName string `json:"templateName"` + Category string `json:"category"` + Status string `json:"status"` + DenialReason string `json:"denialReason"` + ReplacementAvailability string `json:"replacementAvailability"` + UploadContext string `json:"uploadContext"` + LastStatusUpdate string `json:"lastStatusUpdate"` +} + +type CardLinks struct { + CardDetails string `json:"carddetails"` + CardImage string `json:"cardimage"` + CardSwitcherLogoImage string `json:"cardswitcherlogoimage"` + CockpitLogoImage string `json:"cockpitlogoimage"` +} + +type CardGrants struct { + CanSurprizeRead bool `json:"canSurprizeRead"` + CanAccountDetailsRead bool `json:"canAccountDetailsRead"` + CanStatementSettingsRead bool `json:"canStatementSettingsRead"` + CanStatementSettingsUpdate bool `json:"canStatementSettingsUpdate"` + CanStatementDetailRead bool `json:"canStatementDetailRead"` + CanTransactionNotificationRead bool `json:"canTransactionNotificationRead"` + CanTransactionNotificationUpdate bool `json:"canTransactionNotificationUpdate"` + CanMasterpassRead bool `json:"canMasterpassRead"` + CanMasterpassUpdate bool `json:"canMasterpassUpdate"` + CanCardFreeze bool `json:"canCardFreeze"` + CanCardPINRequest bool `json:"canCardPINRequest"` + CanSmsSettingsRead bool `json:"canSmsSettingsRead"` + CanSmsSettingsUpdate bool `json:"canSmsSettingsUpdate"` + CanReplaceCard bool `json:"canReplaceCard"` + CanCardControlsRead bool `json:"canCardControlsRead"` + CanCardControlsUpdate bool `json:"canCardControlsUpdate"` + CanCouponsRead bool `json:"canCouponsRead"` + CanPanCvvPinRead bool `json:"canPanCvvPinRead"` +} diff --git a/pkg/viseca/user.go b/pkg/viseca/user.go new file mode 100644 index 0000000..3dd561c --- /dev/null +++ b/pkg/viseca/user.go @@ -0,0 +1,22 @@ +package viseca + +import ( + "context" +) + +// GetUser returns the user information. +func (client *Client) GetUser(ctx context.Context) (*User, error) { + request, err := client.NewRequest("user", "GET", nil) + if err != nil { + return nil, err + } + + user := &User{} + + _, err = client.Do(ctx, request, user) + if err != nil { + return nil, err + } + + return user, err +} diff --git a/pkg/viseca/viseca.go b/pkg/viseca/viseca.go index 99a77cf..68700ec 100644 --- a/pkg/viseca/viseca.go +++ b/pkg/viseca/viseca.go @@ -45,6 +45,7 @@ type ListOptions struct { DateFrom time.Time } +// NewDefaultListOptions creates new default ListOptions. func NewDefaultListOptions() ListOptions { listOptions := ListOptions{} listOptions.Offset = 0 @@ -69,6 +70,29 @@ func addListOptions(url *url.URL, listOptions ListOptions) { url.RawQuery = query.Encode() } +// CardListOptions known CreditIndicators are "credit" and "debit". +type CardListOptions struct { + CreditIndicators []string +} + +// NewDefaultCardListOptions creates new default CardListOptions. +func NewDefaultCardListOptions() CardListOptions { + cardListOptions := CardListOptions{} + cardListOptions.CreditIndicators = []string{"credit", "debit"} + + return cardListOptions +} + +func addCardListOptions(url *url.URL, cardListOptions CardListOptions) { + query := url.Query() + + for _, creditIndicator := range cardListOptions.CreditIndicators { + query.Add("creditIndicators", creditIndicator) + } + + url.RawQuery = query.Encode() +} + // Do returns the HTTP response and decodes the JSON response body into responseBody. func (client *Client) Do(ctx context.Context, request *http.Request, responseBody interface{}) (*http.Response, error) { request = request.WithContext(ctx)