From 30dd5cb7dd8bd85552f4932f9cdd449772a2d0e6 Mon Sep 17 00:00:00 2001 From: Sidharthan C Kamaraj Date: Sun, 22 Sep 2024 23:02:40 +0200 Subject: [PATCH] feat: update client to confirm to gocardless api changes (#13) * feat: add refresh token method to the client * feat: updates * feat: update nordigen references to gocardless * update nordigen references to gocardless --- .github/workflows/pr.yml | 6 +- .version | 2 +- README.md | 10 +-- Taskfile.yml | 2 +- accounts.go | 46 +++++------ accounts_endpoints.go | 38 ++++++--- accounts_endpoints_test.go | 76 +++++++---------- agreements.go | 18 ++--- agreements_endpoints.go | 22 ++--- agreements_endpoints_test.go | 128 ++++++++++++++--------------- api_info.go | 8 +- assets/cover-treemap.svg | 2 +- client.go | 78 +++++++++++++++--- client_test.go | 37 ++++----- error.go | 3 +- go.mod | 4 +- helpers.go | 36 ++++++++- http-client.go | 2 +- institutions.go | 21 ++++- institutions_endpoints.go | 69 +++++++++++++--- institutions_endpoints_test.go | 44 +++++----- payments.go | 2 +- payments_endpoints.go | 6 +- payments_endpoints_test.go | 11 +-- premium.go | 34 ++++---- premium_endpoints.go | 7 +- requisitions.go | 30 ++++--- requisitions_endpoints.go | 20 ++--- requisitions_endpoints_test.go | 144 ++++++++++++++++----------------- token.go | 2 +- token_endpoints.go | 2 +- token_endpoints_test.go | 23 +++--- 32 files changed, 531 insertions(+), 402 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index ac0d486..75b74f6 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -6,9 +6,9 @@ jobs: runs-on: ubuntu-latest env: GOPRIVATE: "github.com/cksidharthan/*" - NORDIGEN_SECRET_ID: ${{ secrets.NORDIGEN_SECRET_ID }} - NORDIGEN_SECRET_KEY: ${{ secrets.NORDIGEN_SECRET_KEY }} - NORDIGEN_TEST_ACCOUNT_ID: ${{ secrets.NORDIGEN_TEST_ACCOUNT_ID }} + GOCARDLESS_SECRET_ID: ${{ secrets.GOCARDLESS_SECRET_ID }} + GOCARDLESS_SECRET_KEY: ${{ secrets.GOCARDLESS_SECRET_KEY }} + GOCARDLESS_TEST_ACCOUNT_ID: ${{ secrets.GOCARDLESS_TEST_ACCOUNT_ID }} steps: # Checkout - name: Checkout diff --git a/.version b/.version index a92e827..837042c 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -v0.0.4 \ No newline at end of file +v0.0.5 \ No newline at end of file diff --git a/README.md b/README.md index 9d76259..bd3fb56 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# go-nordigen -Go Library for [Nordigen Openbanking API v2](https://nordigen.com/en/account_information_documenation/api-documention/overview/) +# go-gocardless +Go Library for [GoCardless Bank Account Data API](https://developer.gocardless.com/bank-account-data/overview) ## Requirements - Go 1.16+ -- Nordigen API Secret ID & Key +- Gocardless API Secret ID & Key - Taskfile (optional) ## Usage @@ -16,12 +16,12 @@ Usage examples can be found in the respective package's `*_test.go` files. - Institutions - Requisitions -Please refer to [Nordigen API Documentation](https://nordigen.com/en/docs/account-information/integration/parameters-and-responses/) for more information on the endpoints. +Please refer to [GoCardless Bank Account Data API](https://developer.gocardless.com/bank-account-data/overview) for more information on the endpoints. # Pending Endpoints - Payments - Since this needs some access to the API which is paid, I will not be implementing this since I can't test. If you would like to contribute, please feel free to open a PR. # Issues -Please report any issues or bugs to the [Issues](https://github.com/cksidharthan/go-nordigen/issues) page. +Please report any issues or bugs to the [Issues](https://github.com/cksidharthan/go-gocardless/issues) page. ![pkg-coverage-img](./assets/cover-treemap.svg?raw=true "Unit Test Coverage Image") \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml index a804c7d..1b3d2b0 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -63,4 +63,4 @@ tasks: - task deps - task fmt - task lint - - task test \ No newline at end of file + - task test diff --git a/accounts.go b/accounts.go index a5e9cc5..16afa26 100644 --- a/accounts.go +++ b/accounts.go @@ -1,6 +1,4 @@ -package nordigen - -import "time" +package gocardless type Account struct { ID string `json:"id"` @@ -13,12 +11,12 @@ type Account struct { } type Balance struct { - BalanceAmount Amount `json:"balanceAmount"` - BalanceType string `json:"balanceType"` - ReferenceDate string `json:"referenceDate"` - CreditLimitIncluded bool `json:"creditLimitIncluded"` - LastChangeDateTime time.Time `json:"lastChangeDateTime"` - LastCommittedTransaction string `json:"lastCommittedTransaction"` + BalanceAmount Amount `json:"balanceAmount"` + BalanceType string `json:"balanceType"` + ReferenceDate string `json:"referenceDate"` + CreditLimitIncluded bool `json:"creditLimitIncluded"` + LastChangeDateTime TimeWithTimeZoneInfo `json:"lastChangeDateTime"` + LastCommittedTransaction string `json:"lastCommittedTransaction"` } type Amount struct { @@ -61,21 +59,21 @@ type TransactionParams struct { } type Transaction struct { - TransactionID string `json:"transactionId"` - BookingDate string `json:"bookingDate"` - ValueDate string `json:"valueDate"` - BookingDateTime time.Time `json:"bookingDateTime"` - ValueDateTime time.Time `json:"valueDateTime"` - TransactionAmount Amount `json:"transactionAmount"` - CreditorName string `json:"creditorName"` - CreditorAccount Account `json:"creditorAccount"` - DebtorName string `json:"debtorName"` - DebtorAccount Account `json:"debtorAccount"` - BankTransactionCode string `json:"bankTransactionCode"` - RemittanceInformationUnstructured string `json:"remittanceInformationUnstructured"` - RemittanceInformationUnstructuredArray []string `json:"remittanceInformationUnstructuredArray"` - ProprietaryBankTransactionCode string `json:"proprietaryBankTransactionCode"` - InternalTransactionID string `json:"internalTransactionId"` + TransactionID string `json:"transactionId"` + BookingDate string `json:"bookingDate"` + ValueDate string `json:"valueDate"` + BookingDateTime TimeWithTimeZoneInfo `json:"bookingDateTime"` + ValueDateTime TimeWithTimeZoneInfo `json:"valueDateTime"` + TransactionAmount Amount `json:"transactionAmount"` + CreditorName string `json:"creditorName"` + CreditorAccount Account `json:"creditorAccount"` + DebtorName string `json:"debtorName"` + DebtorAccount Account `json:"debtorAccount"` + BankTransactionCode string `json:"bankTransactionCode"` + RemittanceInformationUnstructured string `json:"remittanceInformationUnstructured"` + RemittanceInformationUnstructuredArray []string `json:"remittanceInformationUnstructuredArray"` + ProprietaryBankTransactionCode string `json:"proprietaryBankTransactionCode"` + InternalTransactionID string `json:"internalTransactionId"` AdditionalInformation string `json:"additionalInformation"` AdditionalInformationStructured string `json:"additionalInformationStructured"` diff --git a/accounts_endpoints.go b/accounts_endpoints.go index 288c9ec..0e59aa9 100644 --- a/accounts_endpoints.go +++ b/accounts_endpoints.go @@ -1,14 +1,17 @@ -package nordigen +package gocardless import ( "context" + "fmt" + "net/url" + "time" ) // GetAccount retrieves an account by ID -func (c Client) GetAccount(ctx context.Context, token string, accountID string) (*Account, error) { +func (c Client) GetAccount(ctx context.Context, accountID string) (*Account, error) { var account Account endpointURL := AccountsPath + accountID - err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &account) + err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(c.Token.Access), &account) if err != nil { return nil, err } @@ -16,10 +19,10 @@ func (c Client) GetAccount(ctx context.Context, token string, accountID string) } // GetAccountBalances retrieves balances for an account by ID -func (c Client) GetAccountBalances(ctx context.Context, token string, accountID string) (*Balances, error) { +func (c Client) GetAccountBalances(ctx context.Context, accountID string) (*Balances, error) { var balances Balances endpointURL := AccountsPath + accountID + "/balances" - err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &balances) + err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(c.Token.Access), &balances) if err != nil { return nil, err } @@ -28,10 +31,10 @@ func (c Client) GetAccountBalances(ctx context.Context, token string, accountID } // GetAccountDetails retrieves details for an account by ID -func (c Client) GetAccountDetails(ctx context.Context, token string, accountID string) (*Details, error) { +func (c Client) GetAccountDetails(ctx context.Context, accountID string) (*Details, error) { var details Details endpointURL := AccountsPath + accountID + "/details" - err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &details) + err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(c.Token.Access), &details) if err != nil { return nil, err } @@ -40,10 +43,27 @@ func (c Client) GetAccountDetails(ctx context.Context, token string, accountID s } // GetAccountTransactions retrieves transactions for an account by ID -func (c Client) GetAccountTransactions(ctx context.Context, token string, accountID string) (*Transactions, error) { +// dateFrom and dateTo are optional parameters. +func (c Client) GetAccountTransactions(ctx context.Context, accountID string, dateFrom, dateTo *time.Time) (*Transactions, error) { var transactions Transactions endpointURL := AccountsPath + accountID + "/transactions" - err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &transactions) + + // Build query parameters + u, err := url.Parse(endpointURL) + if err != nil { + return nil, fmt.Errorf("failed to parse URL: %w", err) + } + + q := u.Query() + if dateFrom != nil { + q.Set("date_from", dateFrom.Format(time.RFC3339)) + } + if dateTo != nil { + q.Set("date_to", dateTo.Format(time.RFC3339)) + } + u.RawQuery = q.Encode() + + err = c.HTTP.Get(ctx, u.String(), RequestHeadersWithAuth(c.Token.Access), &transactions) if err != nil { return nil, err } diff --git a/accounts_endpoints_test.go b/accounts_endpoints_test.go index 1875ca8..fd70cf0 100644 --- a/accounts_endpoints_test.go +++ b/accounts_endpoints_test.go @@ -1,4 +1,4 @@ -package nordigen_test +package gocardless_test import ( "context" @@ -14,16 +14,13 @@ func TestClient_GetAccount(t *testing.T) { t.Run("get an account by ID", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getTestClient(t) assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) assert.NoError(t, err) - assert.NotNil(t, token) - testAccountID := os.Getenv("NORDIGEN_TEST_ACCOUNT_ID") + testAccountID := os.Getenv("GOCARDLESS_TEST_ACCOUNT_ID") - account, err := client.GetAccount(context.Background(), token.Access, testAccountID) + account, err := client.GetAccount(context.Background(), testAccountID) assert.NoError(t, err) assert.NotNil(t, account) }) @@ -31,14 +28,11 @@ func TestClient_GetAccount(t *testing.T) { t.Run("get an account by invalid ID", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - account, err := client.GetAccount(context.Background(), token.Access, "invalid") + account, err := client.GetAccount(context.Background(), "invalid") assert.Error(t, err) assert.Nil(t, account) }) @@ -50,16 +44,13 @@ func TestClient_GetAccountBalances(t *testing.T) { t.Run("get balances for an account by ID", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - testAccountID := os.Getenv("NORDIGEN_TEST_ACCOUNT_ID") + testAccountID := os.Getenv("GOCARDLESS_TEST_ACCOUNT_ID") - balances, err := client.GetAccountBalances(context.Background(), token.Access, testAccountID) + balances, err := client.GetAccountBalances(context.Background(), testAccountID) assert.NoError(t, err) assert.NotNil(t, balances) }) @@ -67,14 +58,11 @@ func TestClient_GetAccountBalances(t *testing.T) { t.Run("get balances for an account by invalid ID", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - balances, err := client.GetAccountBalances(context.Background(), token.Access, "invalid") + balances, err := client.GetAccountBalances(context.Background(), "invalid") assert.Error(t, err) assert.Nil(t, balances) }) @@ -86,16 +74,13 @@ func TestClient_GetAccountDetails(t *testing.T) { t.Run("get details for an account by ID", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - testAccountID := os.Getenv("NORDIGEN_TEST_ACCOUNT_ID") + testAccountID := os.Getenv("GOCARDLESS_TEST_ACCOUNT_ID") - details, err := client.GetAccountDetails(context.Background(), token.Access, testAccountID) + details, err := client.GetAccountDetails(context.Background(), testAccountID) assert.NoError(t, err) assert.NotNil(t, details) }) @@ -103,14 +88,15 @@ func TestClient_GetAccountDetails(t *testing.T) { t.Run("get details for an account by invalid ID", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) token, err := client.NewToken(context.Background()) assert.NoError(t, err) assert.NotNil(t, token) - details, err := client.GetAccountDetails(context.Background(), token.Access, "invalid") + details, err := client.GetAccountDetails(context.Background(), "invalid") assert.Error(t, err) assert.Nil(t, details) }) @@ -122,16 +108,13 @@ func TestClient_GetAccountTransactions(t *testing.T) { t.Run("get transactions for an account by ID", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - testAccountID := os.Getenv("NORDIGEN_TEST_ACCOUNT_ID") + testAccountID := os.Getenv("GOCARDLESS_TEST_ACCOUNT_ID") - transactions, err := client.GetAccountTransactions(context.Background(), token.Access, testAccountID) + transactions, err := client.GetAccountTransactions(context.Background(), testAccountID, nil, nil) assert.NoError(t, err) assert.NotNil(t, transactions) }) @@ -139,14 +122,11 @@ func TestClient_GetAccountTransactions(t *testing.T) { t.Run("get transactions for an account by invalid ID", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - transactions, err := client.GetAccountTransactions(context.Background(), token.Access, "invalid") + transactions, err := client.GetAccountTransactions(context.Background(), "invalid", nil, nil) assert.Error(t, err) assert.Nil(t, transactions) }) diff --git a/agreements.go b/agreements.go index ca31956..44d0fd5 100644 --- a/agreements.go +++ b/agreements.go @@ -1,6 +1,4 @@ -package nordigen - -import "time" +package gocardless type AgreementRequestBody struct { InstitutionID string `json:"institution_id"` @@ -10,13 +8,13 @@ type AgreementRequestBody struct { } type Agreement struct { - ID string `json:"id"` - Created time.Time `json:"created"` - InstitutionID string `json:"institution_id"` - MaxHistoricalDays int `json:"max_historical_days"` - AccessValidForDays int `json:"access_valid_for_days"` - AccessScope []string `json:"access_scope"` - Accepted time.Time `json:"accepted"` + ID string `json:"id"` + Created *TimeWithTimeZoneInfoZ `json:"created"` + InstitutionID string `json:"institution_id"` + MaxHistoricalDays int `json:"max_historical_days"` + AccessValidForDays int `json:"access_valid_for_days"` + AccessScope []string `json:"access_scope"` + Accepted *TimeWithTimeZoneInfoZ `json:"accepted"` } type ListAgreementsParams struct { diff --git a/agreements_endpoints.go b/agreements_endpoints.go index 8e83094..d82de15 100644 --- a/agreements_endpoints.go +++ b/agreements_endpoints.go @@ -1,4 +1,4 @@ -package nordigen +package gocardless import ( "context" @@ -6,9 +6,9 @@ import ( ) // CreateAgreement creates a new agreement for the enduser -func (c Client) CreateAgreement(ctx context.Context, token string, agreementRequestBody AgreementRequestBody) (*Agreement, error) { +func (c Client) CreateAgreement(ctx context.Context, agreementRequestBody AgreementRequestBody) (*Agreement, error) { var agreement Agreement - err := c.HTTP.Post(ctx, AgreementsEndusersPath, RequestHeadersWithAuth(token), agreementRequestBody, &agreement) + err := c.HTTP.Post(ctx, AgreementsEndusersPath, RequestHeadersWithAuth(c.Token.Access), agreementRequestBody, &agreement) if err != nil { return nil, err } @@ -17,9 +17,9 @@ func (c Client) CreateAgreement(ctx context.Context, token string, agreementRequ } // FetchAgreement retrieves an agreement for the enduser by agreementID -func (c Client) FetchAgreement(ctx context.Context, token string, agreementID string) (*Agreement, error) { +func (c Client) FetchAgreement(ctx context.Context, agreementID string) (*Agreement, error) { var agreement Agreement - err := c.HTTP.Get(ctx, AgreementsEndusersPath+agreementID, RequestHeadersWithAuth(token), &agreement) + err := c.HTTP.Get(ctx, AgreementsEndusersPath+agreementID, RequestHeadersWithAuth(c.Token.Access), &agreement) if err != nil { return nil, err } @@ -28,7 +28,7 @@ func (c Client) FetchAgreement(ctx context.Context, token string, agreementID st } // ListAgreements returns a list of agreements for the enduser -func (c Client) ListAgreements(ctx context.Context, token string, requestParams *ListAgreementsParams) (*Agreements, error) { +func (c Client) ListAgreements(ctx context.Context, requestParams *ListAgreementsParams) (*Agreements, error) { var agreements Agreements endpointURL := AgreementsEndusersPath @@ -41,7 +41,7 @@ func (c Client) ListAgreements(ctx context.Context, token string, requestParams } } - err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &agreements) + err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(c.Token.Access), &agreements) if err != nil { return nil, err } @@ -50,8 +50,8 @@ func (c Client) ListAgreements(ctx context.Context, token string, requestParams } // DeleteAgreement deletes an agreement for the enduser by agreementID -func (c Client) DeleteAgreement(ctx context.Context, token string, agreementID string) error { - err := c.HTTP.Delete(ctx, AgreementsEndusersPath+agreementID, RequestHeadersWithAuth(token), nil) +func (c Client) DeleteAgreement(ctx context.Context, agreementID string) error { + err := c.HTTP.Delete(ctx, AgreementsEndusersPath+agreementID, RequestHeadersWithAuth(c.Token.Access), nil) if err != nil { return err } @@ -60,10 +60,10 @@ func (c Client) DeleteAgreement(ctx context.Context, token string, agreementID s } // UpdateAgreement updates an agreement for the enduser by agreementID -func (c Client) UpdateAgreement(ctx context.Context, token string, agreementID string, updateRequestBody UpdateRequestBody) (*Agreement, error) { +func (c Client) UpdateAgreement(ctx context.Context, agreementID string, updateRequestBody UpdateRequestBody) (*Agreement, error) { var agreement Agreement // TODO: Check if this is the correct way to update an agreement, The API doc wants to append a /accept to the end of the URL - err := c.HTTP.Put(ctx, AgreementsEndusersPath+agreementID, RequestHeadersWithAuth(token), updateRequestBody, &agreement) + err := c.HTTP.Put(ctx, AgreementsEndusersPath+agreementID, RequestHeadersWithAuth(c.Token.Access), updateRequestBody, &agreement) if err != nil { return nil, err } diff --git a/agreements_endpoints_test.go b/agreements_endpoints_test.go index 50e2c3c..964227f 100644 --- a/agreements_endpoints_test.go +++ b/agreements_endpoints_test.go @@ -1,10 +1,10 @@ -package nordigen_test +package gocardless_test import ( "context" "testing" - "github.com/cksidharthan/go-nordigen" + "github.com/cksidharthan/go-gocardless" "github.com/stretchr/testify/assert" ) @@ -14,44 +14,42 @@ func TestClient_CreateAgreement(t *testing.T) { t.Run("create a new agreement", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.NoError(t, err) assert.NotNil(t, agreement) - assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, agreement.InstitutionID) }) t.Run("create a new agreement with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), "invalid", agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.Error(t, err) assert.Nil(t, agreement) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } @@ -62,26 +60,23 @@ func TestClient_FetchAgreement(t *testing.T) { t.Run("fetch an agreement", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.NoError(t, err) assert.NotNil(t, agreement) - assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, agreement.InstitutionID) - fetchedAgreement, err := client.FetchAgreement(context.Background(), token.Access, agreement.ID) + fetchedAgreement, err := client.FetchAgreement(context.Background(), agreement.ID) assert.NoError(t, err) assert.NotNil(t, fetchedAgreement) assert.Equal(t, agreement.ID, fetchedAgreement.ID) @@ -90,21 +85,22 @@ func TestClient_FetchAgreement(t *testing.T) { t.Run("fetch an agreement with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), "invalid", agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.Error(t, err) assert.Nil(t, agreement) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } @@ -115,14 +111,11 @@ func TestClient_ListAgreements(t *testing.T) { t.Run("list agreements", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - responseAgreements, err := client.ListAgreements(context.Background(), token.Access, nil) + responseAgreements, err := client.ListAgreements(context.Background(), nil) assert.NoError(t, err) assert.NotNil(t, responseAgreements) }) @@ -130,14 +123,15 @@ func TestClient_ListAgreements(t *testing.T) { t.Run("list agreements with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - responseAgreements, err := client.ListAgreements(context.Background(), "invalid", nil) + responseAgreements, err := client.ListAgreements(context.Background(), nil) assert.Error(t, err) assert.Nil(t, responseAgreements) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } @@ -148,39 +142,37 @@ func TestClient_DeleteAgreement(t *testing.T) { t.Run("delete an agreement", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.NoError(t, err) assert.NotNil(t, agreement) - assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, agreement.InstitutionID) - err = client.DeleteAgreement(context.Background(), token.Access, agreement.ID) + err = client.DeleteAgreement(context.Background(), agreement.ID) assert.NoError(t, err) }) t.Run("delete an agreement with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - err := client.DeleteAgreement(context.Background(), "invalid", "invalid") + err = client.DeleteAgreement(context.Background(), "invalid") assert.Error(t, err) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } @@ -191,53 +183,51 @@ func TestClient_UpdateAgreement(t *testing.T) { t.Run("update an agreement", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.NoError(t, err) assert.NotNil(t, agreement) - assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, agreement.InstitutionID) - updateRequestBody := nordigen.UpdateRequestBody{ + updateRequestBody := gocardless.UpdateRequestBody{ UserAgent: "test", IPAddress: "0.0.0.0", } - updatedAgreement, err := client.UpdateAgreement(context.Background(), token.Access, agreement.ID, updateRequestBody) + updatedAgreement, err := client.UpdateAgreement(context.Background(), agreement.ID, updateRequestBody) assert.NoError(t, err) assert.NotNil(t, updatedAgreement) - assert.Equal(t, nordigen.TestInstitutionID, updatedAgreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, updatedAgreement.InstitutionID) assert.Equal(t, agreement.ID, updatedAgreement.ID) }) t.Run("update an agreement with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - updateRequestBody := nordigen.UpdateRequestBody{ + updateRequestBody := gocardless.UpdateRequestBody{ UserAgent: "test", IPAddress: "", } - updatedAgreement, err := client.UpdateAgreement(context.Background(), "invalid", "invalid", updateRequestBody) + updatedAgreement, err := client.UpdateAgreement(context.Background(), "invalid", updateRequestBody) assert.Error(t, err) assert.Nil(t, updatedAgreement) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } diff --git a/api_info.go b/api_info.go index dff3a50..87aed99 100644 --- a/api_info.go +++ b/api_info.go @@ -1,9 +1,9 @@ -package nordigen +package gocardless const ( - NordigenBaseURL = "https://bankaccountdata.gocardless.com/api" - APIVersion = "v2" - TestBaseURL = "https://localhost:8000/api" + BaseURL = "https://bankaccountdata.gocardless.com/api" + APIVersion = "v2" + TestBaseURL = "https://localhost:8000/api" ) const ( diff --git a/assets/cover-treemap.svg b/assets/cover-treemap.svg index badd595..b494fd9 100644 --- a/assets/cover-treemap.svg +++ b/assets/cover-treemap.svg @@ -14,7 +14,7 @@ text-anchor="start" transform="translate(28.000000,35.600000) scale(1.000000)" style="font-family: Open Sans, verdana, arial, sans-serif !important; font-size: 12px; fill: rgb(0, 0, 0, 65535); fill-opacity: 1; white-space: pre;" - data-math="N">github.com/cksidharthan/go-nordigen + data-math="N">github.com/cksidharthan/go-gocardless diff --git a/client.go b/client.go index 97fe5c2..1713f1f 100644 --- a/client.go +++ b/client.go @@ -1,25 +1,85 @@ -package nordigen +package gocardless + +import ( + "context" + "fmt" + "os" + "os/signal" + "syscall" + "time" +) // Client is the Nordigen client type Client struct { HTTP IHTTPClient SecretID string SecretKey string + Token *Token } type Config struct { - BaseURL string - APIVersion string - SecretID string `json:"secret_id"` - SecretKey string `json:"secret_key"` - HTTP *Client + BaseURL string + APIVersion string + SecretID string `json:"secret_id"` + SecretKey string `json:"secret_key"` + TokenRefresh bool + HTTP *Client } -// New creates a new Nordigen client -func New(config *Config) *Client { - return &Client{ +// New creates a new Gocardless client +func New(config *Config) (*Client, error) { + client := &Client{ HTTP: NewHTTPClient(config.BaseURL, config.APIVersion), SecretID: config.SecretID, SecretKey: config.SecretKey, } + + token, err := client.NewToken(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + client.Token = token + + if config.TokenRefresh { + go refreshGocardlessToken(client) + } + + return client, nil +} + +// refreshGocardlessToken refreshes the access token and the refresh token +func refreshGocardlessToken(client *Client) { + sigterm := make(chan os.Signal, 1) + signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT) + + for { + select { + case <-sigterm: + fmt.Println("Received termination signal, exiting refreshGocardlessToken") + + return + case <-time.After(1 * time.Minute): + if time.Unix(int64(client.Token.AccessExpires), 0).Before(time.Now().Add(1 * time.Minute)) { + newToken, err := client.RefreshToken(context.Background(), client.Token.Refresh) + if err != nil { + fmt.Printf("failed to refresh access token: %v\n", err) + continue + } + + client.Token.Access = newToken.Access + client.Token.AccessExpires = newToken.AccessExpires + } + + if time.Unix(int64(client.Token.RefreshExpires), 0).Before(time.Now().Add(1 * time.Minute)) { + newToken, err := client.NewToken(context.Background()) + if err != nil { + fmt.Printf("failed to create new token: %v\n", err) + continue + } + + client.Token = newToken + } + } + } } diff --git a/client_test.go b/client_test.go index ea21be1..6ffdb9d 100644 --- a/client_test.go +++ b/client_test.go @@ -1,41 +1,42 @@ -package nordigen_test +package gocardless_test import ( "fmt" "os" "testing" - "github.com/cksidharthan/go-nordigen" + gocardless "github.com/cksidharthan/go-gocardless" ) -func getTestClient(t *testing.T) *nordigen.Client { +func getTestClient(t *testing.T) (*gocardless.Client, error) { t.Helper() - secretID := os.Getenv("NORDIGEN_SECRET_ID") - secretKey := os.Getenv("NORDIGEN_SECRET_KEY") + secretID := os.Getenv("GOCARDLESS_SECRET_ID") + secretKey := os.Getenv("GOCARDLESS_SECRET_KEY") if secretID == "" || secretKey == "" { - fmt.Println("NORDIGEN_SECRET_ID or NORDIGEN_SECRET_KEY is not set") + fmt.Println("GOCARDLESS_SECRET_ID or GOCARDLESS_SECRET_KEY is not set") } - return nordigen.New( - &nordigen.Config{ - BaseURL: nordigen.NordigenBaseURL, - APIVersion: nordigen.APIVersion, + return gocardless.New( + &gocardless.Config{ + BaseURL: gocardless.BaseURL, + APIVersion: gocardless.APIVersion, SecretID: secretID, SecretKey: secretKey, }, ) } -func getInvalidTestClient(t *testing.T) *nordigen.Client { +func getInvalidTestClient(t *testing.T) (*gocardless.Client, error) { t.Helper() - return nordigen.New( - &nordigen.Config{ - BaseURL: nordigen.NordigenBaseURL, - APIVersion: nordigen.APIVersion, - SecretID: "invalid", - SecretKey: "invalid", + return &gocardless.Client{ + HTTP: gocardless.NewHTTPClient(gocardless.BaseURL, gocardless.APIVersion), + SecretID: "invalid", + SecretKey: "invalid", + Token: &gocardless.Token{ + Access: "invalid", + Refresh: "invalid", }, - ) + }, nil } diff --git a/error.go b/error.go index 6363244..c8fb24a 100644 --- a/error.go +++ b/error.go @@ -1,4 +1,4 @@ -package nordigen +package gocardless import ( "encoding/json" @@ -21,7 +21,6 @@ type Reference struct { func NewError(errResponse *http.Response) error { var newErr Error - // print response body err := json.NewDecoder(errResponse.Body).Decode(&newErr) if err != nil { return fmt.Errorf("failed to decode response: %w", err) diff --git a/go.mod b/go.mod index 351d61b..f6a2695 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ -module github.com/cksidharthan/go-nordigen +module github.com/cksidharthan/go-gocardless -go 1.22.7 +go 1.23.1 require github.com/stretchr/testify v1.8.2 diff --git a/helpers.go b/helpers.go index 3f16425..69a550e 100644 --- a/helpers.go +++ b/helpers.go @@ -1,7 +1,9 @@ -package nordigen +package gocardless import ( "fmt" + "strings" + "time" ) func RequestHeadersWithAuth(token string) map[string]string { @@ -34,3 +36,35 @@ func BuildQueryURL(path string, queryParams map[string]string) string { return queryURL } + +type TimeWithTimeZoneInfo struct { + time.Time +} + +const timeWithTimeZoneInfo = "2006-01-02T15:04:05.999999" + +func (ct *TimeWithTimeZoneInfo) UnmarshalJSON(b []byte) error { + str := strings.Trim(string(b), `"`) + t, err := time.Parse(timeWithTimeZoneInfo, str) + if err != nil { + return fmt.Errorf("failed to parse time: %w", err) + } + ct.Time = t + return nil +} + +type TimeWithTimeZoneInfoZ struct { + time.Time +} + +const timeWithTimeZoneInfoZ = "2006-01-02T15:04:05.999999Z" + +func (ct *TimeWithTimeZoneInfoZ) UnmarshalJSON(b []byte) error { + str := strings.Trim(string(b), `"`) + t, err := time.Parse(timeWithTimeZoneInfoZ, str) + if err != nil { + return fmt.Errorf("failed to parse time: %w", err) + } + ct.Time = t + return nil +} diff --git a/http-client.go b/http-client.go index 3961874..99392c6 100644 --- a/http-client.go +++ b/http-client.go @@ -1,4 +1,4 @@ -package nordigen +package gocardless import ( "bytes" diff --git a/institutions.go b/institutions.go index 7fc6944..771c597 100644 --- a/institutions.go +++ b/institutions.go @@ -1,4 +1,22 @@ -package nordigen +package gocardless + +// ListInstitutionsParams are the parameters that can be used when listing institutions +// NOTE: Except for Country, all other fields should be boolean strings +type ListInstitutionsParams struct { + AccessScopesSupported string `url:"access_scopes_supported,omitempty" json:"access_scopes_supported,omitempty"` + AccountSelectionSupported string `url:"account_selection_supported,omitempty" json:"account_selection_supported,omitempty"` + BusinessAccountsSupported string `url:"business_accounts_supported,omitempty" json:"business_accounts_supported,omitempty"` + CardAccountsSupported string `url:"card_accounts_supported,omitempty" json:"card_accounts_supported,omitempty"` + CorporateAccountsSupported string `url:"corporate_accounts_supported,omitempty" json:"corporate_accounts_supported,omitempty"` + Country string `url:"country,omitempty" json:"country,omitempty"` + PaymentSubmissionSupported string `url:"payment_submission_supported,omitempty" json:"payment_submission_supported,omitempty"` + PaymentsEnabled string `url:"payments_enabled,omitempty" json:"payments_enabled,omitempty"` + PendingTransactionsSupported string `url:"pending_transactions_supported,omitempty" json:"pending_transactions_supported,omitempty"` + PrivateAccountsSupported string `url:"private_accounts_supported,omitempty" json:"private_accounts_supported,omitempty"` + ReadDebtorAccountSupported string `url:"read_debtor_account_supported,omitempty" json:"read_debtor_account_supported,omitempty"` + ReadRefundAccountSupported string `url:"read_refund_account_supported,omitempty" json:"read_refund_account_supported,omitempty"` + SSNVerificationSupported string `url:"ssn_verification_supported,omitempty" json:"ssn_verification_supported,omitempty"` +} type Institution struct { ID string `json:"id"` @@ -9,6 +27,7 @@ type Institution struct { Logo string `json:"logo"` SupportedPayments SupportedPayments `json:"supported_payments"` SupportedFeatures []string `json:"supported_features"` + IdentificationCodes []string `json:"identification_codes"` } type SupportedPayments struct { diff --git a/institutions_endpoints.go b/institutions_endpoints.go index ddbcbdf..784f273 100644 --- a/institutions_endpoints.go +++ b/institutions_endpoints.go @@ -1,15 +1,25 @@ -package nordigen +package gocardless import ( "context" + "fmt" + "net/url" ) // ListInstitutions returns a list of institutions -func (c Client) ListInstitutions(ctx context.Context, token string, country string, paymentsEnabled bool) ([]Institution, error) { - endpointURL := InstitutionsPath + "?country=" + country + "&payments_enabled=" + boolToString(paymentsEnabled) +func (c Client) ListInstitutions(ctx context.Context, queryParams ListInstitutionsParams) ([]Institution, error) { + endpointURL, err := url.Parse(InstitutionsPath) + if err != nil { + return nil, fmt.Errorf("failed to parse URL: %v", err) + } + + query := bindListInstitutionsParams(queryParams) + + endpointURL.RawQuery = query.Encode() + var institutions []Institution - err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &institutions) + err = c.HTTP.Get(ctx, endpointURL.String(), RequestHeadersWithAuth(c.Token.Access), &institutions) if err != nil { return nil, err } @@ -18,9 +28,9 @@ func (c Client) ListInstitutions(ctx context.Context, token string, country stri } // FetchInstitution returns an institution -func (c Client) FetchInstitution(ctx context.Context, token, id string) (*Institution, error) { +func (c Client) FetchInstitution(ctx context.Context, id string) (*Institution, error) { var institution Institution - err := c.HTTP.Get(ctx, InstitutionsPath+id, RequestHeadersWithAuth(token), &institution) + err := c.HTTP.Get(ctx, InstitutionsPath+id, RequestHeadersWithAuth(c.Token.Access), &institution) if err != nil { return nil, err } @@ -28,10 +38,47 @@ func (c Client) FetchInstitution(ctx context.Context, token, id string) (*Instit return &institution, nil } -// boolToString converts a bool to a string -func boolToString(b bool) string { - if b { - return "true" +func bindListInstitutionsParams(queryParams ListInstitutionsParams) url.Values { + query := make(url.Values) + + if queryParams.Country != "" { + query.Add("country", queryParams.Country) + } + if queryParams.AccessScopesSupported != "" { + query.Add("access_scopes_supported", queryParams.AccessScopesSupported) + } + if queryParams.AccountSelectionSupported != "" { + query.Add("account_selection_supported", queryParams.AccountSelectionSupported) + } + if queryParams.BusinessAccountsSupported != "" { + query.Add("business_accounts_supported", queryParams.BusinessAccountsSupported) + } + if queryParams.CardAccountsSupported != "" { + query.Add("card_accounts_supported", queryParams.CardAccountsSupported) + } + if queryParams.CorporateAccountsSupported != "" { + query.Add("corporate_accounts_supported", queryParams.CorporateAccountsSupported) + } + if queryParams.PaymentSubmissionSupported != "" { + query.Add("payment_submission_supported", queryParams.PaymentSubmissionSupported) + } + if queryParams.PaymentsEnabled != "" { + query.Add("payments_enabled", queryParams.PaymentsEnabled) + } + if queryParams.PendingTransactionsSupported != "" { + query.Add("pending_transactions_supported", queryParams.PendingTransactionsSupported) + } + if queryParams.PrivateAccountsSupported != "" { + query.Add("private_accounts_supported", queryParams.PrivateAccountsSupported) + } + if queryParams.ReadDebtorAccountSupported != "" { + query.Add("read_debtor_account_supported", queryParams.ReadDebtorAccountSupported) + } + if queryParams.ReadRefundAccountSupported != "" { + query.Add("read_refund_account_supported", queryParams.ReadRefundAccountSupported) + } + if queryParams.SSNVerificationSupported != "" { + query.Add("ssn_verification_supported", queryParams.SSNVerificationSupported) } - return "false" + return query } diff --git a/institutions_endpoints_test.go b/institutions_endpoints_test.go index c2fe0eb..30a4e96 100644 --- a/institutions_endpoints_test.go +++ b/institutions_endpoints_test.go @@ -1,10 +1,10 @@ -package nordigen_test +package gocardless_test import ( "context" "testing" - "github.com/cksidharthan/go-nordigen" + "github.com/cksidharthan/go-gocardless" "github.com/stretchr/testify/assert" ) @@ -14,14 +14,14 @@ func TestClient_ListInstitutions(t *testing.T) { t.Run("list institutions", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - institutions, err := client.ListInstitutions(context.Background(), token.Access, nordigen.NetherlandsInstitution, true) + institutions, err := client.ListInstitutions(context.Background(), gocardless.ListInstitutionsParams{ + Country: gocardless.NetherlandsInstitution, + PaymentsEnabled: "true", + }) assert.NoError(t, err) assert.NotNil(t, institutions) }) @@ -29,14 +29,18 @@ func TestClient_ListInstitutions(t *testing.T) { t.Run("list institutions with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - institutions, err := client.ListInstitutions(context.Background(), "invalid", nordigen.NetherlandsInstitution, true) + institutions, err := client.ListInstitutions(context.Background(), gocardless.ListInstitutionsParams{ + Country: gocardless.NetherlandsInstitution, + PaymentsEnabled: "true", + }) assert.Error(t, err) assert.Nil(t, institutions) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } @@ -47,30 +51,28 @@ func TestClient_FetchInstitution(t *testing.T) { t.Run("fetch institution", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - institution, err := client.FetchInstitution(context.Background(), token.Access, nordigen.TestInstitutionID) + institution, err := client.FetchInstitution(context.Background(), gocardless.TestInstitutionID) assert.NoError(t, err) assert.NotNil(t, institution) - assert.Equal(t, nordigen.TestInstitutionID, institution.ID) + assert.Equal(t, gocardless.TestInstitutionID, institution.ID) }) t.Run("fetch institution with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - institution, err := client.FetchInstitution(context.Background(), "invalid", nordigen.TestInstitutionID) + institution, err := client.FetchInstitution(context.Background(), gocardless.TestInstitutionID) assert.Error(t, err) assert.Nil(t, institution) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } diff --git a/payments.go b/payments.go index 573f3cd..aa16df7 100644 --- a/payments.go +++ b/payments.go @@ -1,4 +1,4 @@ -package nordigen +package gocardless type Payment struct { PaymentID string `json:"payment_id"` diff --git a/payments_endpoints.go b/payments_endpoints.go index 9d3da2b..04aba74 100644 --- a/payments_endpoints.go +++ b/payments_endpoints.go @@ -1,4 +1,4 @@ -package nordigen +package gocardless import ( "context" @@ -6,7 +6,7 @@ import ( ) // ListPayments returns a list of payments -func (c Client) ListPayments(ctx context.Context, token string, limit, offset int) (*Payments, error) { +func (c Client) ListPayments(ctx context.Context, limit, offset int) (*Payments, error) { var response Payments params := map[string]string{ @@ -14,7 +14,7 @@ func (c Client) ListPayments(ctx context.Context, token string, limit, offset in "offset": strconv.Itoa(offset), } - err := c.HTTP.Get(ctx, BuildQueryURL(PaymentsPath, params), RequestHeadersWithAuth(token), &response) + err := c.HTTP.Get(ctx, BuildQueryURL(PaymentsPath, params), RequestHeadersWithAuth(c.Token.Access), &response) if err != nil { return nil, err } diff --git a/payments_endpoints_test.go b/payments_endpoints_test.go index 6af1a57..5067137 100644 --- a/payments_endpoints_test.go +++ b/payments_endpoints_test.go @@ -1,4 +1,4 @@ -package nordigen_test +package gocardless_test import ( "context" @@ -14,14 +14,11 @@ func TestClient_ListPayments(t *testing.T) { t.Run("list payments", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - payments, err := client.ListPayments(context.Background(), token.Access, 10, 0) + payments, err := client.ListPayments(context.Background(), 10, 0) assert.Error(t, err) assert.Nil(t, payments) }) diff --git a/premium.go b/premium.go index d5ccf13..4e8326e 100644 --- a/premium.go +++ b/premium.go @@ -1,6 +1,4 @@ -package nordigen - -import "time" +package gocardless type PremiumTransactions struct { Transactions PremiumTransactionList `json:"transactions"` @@ -12,21 +10,21 @@ type PremiumTransactionList struct { } type PremiumTransaction struct { - TransactionID string `json:"transactionId"` - BookingDate string `json:"bookingDate"` - ValueDate string `json:"valueDate"` - BookingDateTime time.Time `json:"bookingDateTime"` - ValueDateTime time.Time `json:"valueDateTime"` - TransactionAmount Amount `json:"transactionAmount"` - CreditorName string `json:"creditorName"` - CreditorAccount Account `json:"creditorAccount"` - DebtorName string `json:"debtorName"` - DebtorAccount Account `json:"debtorAccount"` - BankTransactionCode string `json:"bankTransactionCode"` - RemittanceInformationUnstructured string `json:"remittanceInformationUnstructured"` - RemittanceInformationUnstructuredArray []string `json:"remittanceInformationUnstructuredArray"` - ProprietaryBankTransactionCode string `json:"proprietaryBankTransactionCode"` - InternalTransactionID string `json:"internalTransactionId"` + TransactionID string `json:"transactionId"` + BookingDate string `json:"bookingDate"` + ValueDate string `json:"valueDate"` + BookingDateTime TimeWithTimeZoneInfo `json:"bookingDateTime"` + ValueDateTime TimeWithTimeZoneInfo `json:"valueDateTime"` + TransactionAmount Amount `json:"transactionAmount"` + CreditorName string `json:"creditorName"` + CreditorAccount Account `json:"creditorAccount"` + DebtorName string `json:"debtorName"` + DebtorAccount Account `json:"debtorAccount"` + BankTransactionCode string `json:"bankTransactionCode"` + RemittanceInformationUnstructured string `json:"remittanceInformationUnstructured"` + RemittanceInformationUnstructuredArray []string `json:"remittanceInformationUnstructuredArray"` + ProprietaryBankTransactionCode string `json:"proprietaryBankTransactionCode"` + InternalTransactionID string `json:"internalTransactionId"` AdditionalInformation string `json:"additionalInformation"` AdditionalInformationStructured string `json:"additionalInformationStructured"` diff --git a/premium_endpoints.go b/premium_endpoints.go index 5a6948a..4242642 100644 --- a/premium_endpoints.go +++ b/premium_endpoints.go @@ -1,12 +1,13 @@ -package nordigen +package gocardless import "context" // ListPremiumTransactions retrieves transactions for an account by ID -func (c Client) ListPremiumTransactions(ctx context.Context, token string, accountID string) (*PremiumTransactions, error) { +// Deprecated: This method may or may not work. Use GetAccountTransactions instead. +func (c Client) ListPremiumTransactions(ctx context.Context, accountID string) (*PremiumTransactions, error) { var transactions PremiumTransactions endpointURL := AccountsTransactionPremiumPath + accountID + "/transactions" - err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &transactions) + err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(c.Token.Access), &transactions) if err != nil { return nil, err } diff --git a/requisitions.go b/requisitions.go index a2d3e02..ecee9ee 100644 --- a/requisitions.go +++ b/requisitions.go @@ -1,6 +1,4 @@ -package nordigen - -import "time" +package gocardless type ListRequisitionsParams struct { Limit int `url:"limit,omitempty" json:"limit,omitempty"` @@ -19,19 +17,19 @@ type RequisitionRequestBody struct { } type Requisition struct { - ID string `json:"id"` - Created time.Time `json:"created"` - Redirect string `json:"redirect"` - Status string `json:"status"` - InstitutionID string `json:"institution_id"` - Agreement string `json:"agreement"` - Reference string `json:"reference"` - Accounts []string `json:"accounts"` - UserLanguage string `json:"user_language"` - Link string `json:"link"` - SSN string `json:"ssn"` - AccountSelection bool `json:"account_selection"` - RedirectImmediate bool `json:"redirect_immediate"` + ID string `json:"id"` + Created *TimeWithTimeZoneInfoZ `json:"created"` + Redirect string `json:"redirect"` + Status string `json:"status"` + InstitutionID string `json:"institution_id"` + Agreement string `json:"agreement"` + Reference string `json:"reference"` + Accounts []string `json:"accounts"` + UserLanguage string `json:"user_language"` + Link string `json:"link"` + SSN string `json:"ssn"` + AccountSelection bool `json:"account_selection"` + RedirectImmediate bool `json:"redirect_immediate"` } type Requisitions struct { diff --git a/requisitions_endpoints.go b/requisitions_endpoints.go index 22920a3..64013f1 100644 --- a/requisitions_endpoints.go +++ b/requisitions_endpoints.go @@ -1,4 +1,4 @@ -package nordigen +package gocardless import ( "context" @@ -6,9 +6,9 @@ import ( ) // CreateRequisition creates a new requisition -func (c Client) CreateRequisition(ctx context.Context, token string, requisitionRequestBody *RequisitionRequestBody) (*Requisition, error) { +func (c Client) CreateRequisition(ctx context.Context, requisitionRequestBody *RequisitionRequestBody) (*Requisition, error) { var requisition Requisition - err := c.HTTP.Post(ctx, RequisitionsPath, RequestHeadersWithAuth(token), requisitionRequestBody, &requisition) + err := c.HTTP.Post(ctx, RequisitionsPath, RequestHeadersWithAuth(c.Token.Access), requisitionRequestBody, &requisition) if err != nil { return nil, err } @@ -16,8 +16,8 @@ func (c Client) CreateRequisition(ctx context.Context, token string, requisition return &requisition, nil } -// ListRequisition lists all requisitions -func (c Client) ListRequisitions(ctx context.Context, token string, requestParams *ListRequisitionsParams) (*Requisitions, error) { +// ListRequisitions lists all requisitions +func (c Client) ListRequisitions(ctx context.Context, requestParams *ListRequisitionsParams) (*Requisitions, error) { var requisitions Requisitions endpointURL := RequisitionsPath @@ -30,7 +30,7 @@ func (c Client) ListRequisitions(ctx context.Context, token string, requestParam } } - err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &requisitions) + err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(c.Token.Access), &requisitions) if err != nil { return nil, err } @@ -39,9 +39,9 @@ func (c Client) ListRequisitions(ctx context.Context, token string, requestParam } // FetchRequisition retrieves a requisition by requisitionID -func (c Client) FetchRequisition(ctx context.Context, token string, requisitionID string) (*Requisition, error) { +func (c Client) FetchRequisition(ctx context.Context, requisitionID string) (*Requisition, error) { var requisition Requisition - err := c.HTTP.Get(ctx, RequisitionsPath+requisitionID, RequestHeadersWithAuth(token), &requisition) + err := c.HTTP.Get(ctx, RequisitionsPath+requisitionID, RequestHeadersWithAuth(c.Token.Access), &requisition) if err != nil { return nil, err } @@ -50,8 +50,8 @@ func (c Client) FetchRequisition(ctx context.Context, token string, requisitionI } // DeleteRequisition deletes a requisition by requisitionID -func (c Client) DeleteRequisition(ctx context.Context, token string, requisitionID string) error { - err := c.HTTP.Delete(ctx, RequisitionsPath+requisitionID, RequestHeadersWithAuth(token), nil) +func (c Client) DeleteRequisition(ctx context.Context, requisitionID string) error { + err := c.HTTP.Delete(ctx, RequisitionsPath+requisitionID, RequestHeadersWithAuth(c.Token.Access), nil) if err != nil { return err } diff --git a/requisitions_endpoints_test.go b/requisitions_endpoints_test.go index 4712b73..e480e9f 100644 --- a/requisitions_endpoints_test.go +++ b/requisitions_endpoints_test.go @@ -1,4 +1,4 @@ -package nordigen_test +package gocardless_test import ( "context" @@ -6,7 +6,7 @@ import ( "strconv" "testing" - "github.com/cksidharthan/go-nordigen" + "github.com/cksidharthan/go-gocardless" "github.com/stretchr/testify/assert" ) @@ -16,62 +16,60 @@ func TestClient_CreateRequisition(t *testing.T) { t.Run("create new requisition", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.NoError(t, err) assert.NotNil(t, agreement) - assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, agreement.InstitutionID) - requisitionRequestBody := &nordigen.RequisitionRequestBody{ + requisitionRequestBody := &gocardless.RequisitionRequestBody{ Redirect: "https://example.com", - InstitutionID: nordigen.TestInstitutionID, + InstitutionID: gocardless.TestInstitutionID, Agreement: agreement.ID, Reference: strconv.Itoa(rand.Intn(1000000000000000000)), - UserLanguage: nordigen.LangEN, + UserLanguage: gocardless.LangEN, AccountSelection: false, RedirectImmediate: false, } - requisition, err := client.CreateRequisition(context.Background(), token.Access, requisitionRequestBody) + requisition, err := client.CreateRequisition(context.Background(), requisitionRequestBody) assert.NoError(t, err) assert.NotNil(t, requisition) - assert.Equal(t, nordigen.TestInstitutionID, requisition.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, requisition.InstitutionID) }) t.Run("create new requisition with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - requisitionRequestBody := &nordigen.RequisitionRequestBody{ + requisitionRequestBody := &gocardless.RequisitionRequestBody{ Redirect: "https://example.com", - InstitutionID: nordigen.TestInstitutionID, + InstitutionID: gocardless.TestInstitutionID, Agreement: "invalid", Reference: "12345", - UserLanguage: nordigen.LangEN, + UserLanguage: gocardless.LangEN, AccountSelection: false, RedirectImmediate: false, } - requisition, err := client.CreateRequisition(context.Background(), "invalid", requisitionRequestBody) + requisition, err := client.CreateRequisition(context.Background(), requisitionRequestBody) assert.Error(t, err) assert.Nil(t, requisition) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } @@ -82,41 +80,38 @@ func TestClient_ListRequisitions(t *testing.T) { t.Run("list requisitions", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.NoError(t, err) assert.NotNil(t, agreement) - assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, agreement.InstitutionID) - requisitionRequestBody := &nordigen.RequisitionRequestBody{ + requisitionRequestBody := &gocardless.RequisitionRequestBody{ Redirect: "https://example.com", - InstitutionID: nordigen.TestInstitutionID, + InstitutionID: gocardless.TestInstitutionID, Agreement: agreement.ID, Reference: strconv.Itoa(rand.Intn(1000000000000000000)), - UserLanguage: nordigen.LangEN, + UserLanguage: gocardless.LangEN, AccountSelection: false, RedirectImmediate: false, } - requisition, err := client.CreateRequisition(context.Background(), token.Access, requisitionRequestBody) + requisition, err := client.CreateRequisition(context.Background(), requisitionRequestBody) assert.NoError(t, err) assert.NotNil(t, requisition) - assert.Equal(t, nordigen.TestInstitutionID, requisition.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, requisition.InstitutionID) - responseRequisitions, err := client.ListRequisitions(context.Background(), token.Access, nil) + responseRequisitions, err := client.ListRequisitions(context.Background(), nil) assert.NoError(t, err) assert.NotNil(t, responseRequisitions) }) @@ -124,14 +119,15 @@ func TestClient_ListRequisitions(t *testing.T) { t.Run("list requisitions with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - responseRequisitions, err := client.ListRequisitions(context.Background(), "invalid", nil) + responseRequisitions, err := client.ListRequisitions(context.Background(), nil) assert.Error(t, err) assert.Nil(t, responseRequisitions) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } @@ -142,41 +138,38 @@ func TestClient_FetchRequisition(t *testing.T) { t.Run("fetch requisition", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.NoError(t, err) assert.NotNil(t, agreement) - assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, agreement.InstitutionID) - requisitionRequestBody := &nordigen.RequisitionRequestBody{ + requisitionRequestBody := &gocardless.RequisitionRequestBody{ Redirect: "https://example.com", - InstitutionID: nordigen.TestInstitutionID, + InstitutionID: gocardless.TestInstitutionID, Agreement: agreement.ID, Reference: strconv.Itoa(rand.Intn(1000000000000000000)), - UserLanguage: nordigen.LangEN, + UserLanguage: gocardless.LangEN, AccountSelection: false, RedirectImmediate: false, } - requisition, err := client.CreateRequisition(context.Background(), token.Access, requisitionRequestBody) + requisition, err := client.CreateRequisition(context.Background(), requisitionRequestBody) assert.NoError(t, err) assert.NotNil(t, requisition) - assert.Equal(t, nordigen.TestInstitutionID, requisition.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, requisition.InstitutionID) - responseRequisition, err := client.FetchRequisition(context.Background(), token.Access, requisition.ID) + responseRequisition, err := client.FetchRequisition(context.Background(), requisition.ID) assert.NoError(t, err) assert.NotNil(t, responseRequisition) }) @@ -184,14 +177,15 @@ func TestClient_FetchRequisition(t *testing.T) { t.Run("fetch requisition with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - responseRequisition, err := client.FetchRequisition(context.Background(), "invalid", "invalid") + responseRequisition, err := client.FetchRequisition(context.Background(), "invalid") assert.Error(t, err) assert.Nil(t, responseRequisition) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } @@ -202,54 +196,52 @@ func TestClient_DeleteRequisition(t *testing.T) { t.Run("delete requisition", func(t *testing.T) { t.Parallel() - client := getTestClient(t) - assert.NotNil(t, client) - - token, err := client.NewToken(context.Background()) + client, err := getTestClient(t) assert.NoError(t, err) - assert.NotNil(t, token) + assert.NotNil(t, client) - agreementRequestBody := nordigen.AgreementRequestBody{ - InstitutionID: nordigen.TestInstitutionID, + agreementRequestBody := gocardless.AgreementRequestBody{ + InstitutionID: gocardless.TestInstitutionID, MaxHistoricalDays: "180", AccessValidForDays: "2", AccessScope: []string{"balances", "details", "transactions"}, } - agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody) + agreement, err := client.CreateAgreement(context.Background(), agreementRequestBody) assert.NoError(t, err) assert.NotNil(t, agreement) - assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, agreement.InstitutionID) - requisitionRequestBody := &nordigen.RequisitionRequestBody{ + requisitionRequestBody := &gocardless.RequisitionRequestBody{ Redirect: "https://example.com", - InstitutionID: nordigen.TestInstitutionID, + InstitutionID: gocardless.TestInstitutionID, Agreement: agreement.ID, Reference: strconv.Itoa(rand.Intn(1000000000000000000)), - UserLanguage: nordigen.LangEN, + UserLanguage: gocardless.LangEN, AccountSelection: false, RedirectImmediate: false, } - requisition, err := client.CreateRequisition(context.Background(), token.Access, requisitionRequestBody) + requisition, err := client.CreateRequisition(context.Background(), requisitionRequestBody) assert.NoError(t, err) assert.NotNil(t, requisition) - assert.Equal(t, nordigen.TestInstitutionID, requisition.InstitutionID) + assert.Equal(t, gocardless.TestInstitutionID, requisition.InstitutionID) - err = client.DeleteRequisition(context.Background(), token.Access, requisition.ID) + err = client.DeleteRequisition(context.Background(), requisition.ID) assert.NoError(t, err) }) t.Run("delete requisition with invalid token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) - err := client.DeleteRequisition(context.Background(), "invalid", "invalid") + err = client.DeleteRequisition(context.Background(), "invalid") assert.Error(t, err) - checkErr := nordigen.ExtractError(err) + checkErr := gocardless.ExtractError(err) assert.Equal(t, 401, checkErr.StatusCode) }) } diff --git a/token.go b/token.go index bd84858..69418ad 100644 --- a/token.go +++ b/token.go @@ -1,4 +1,4 @@ -package nordigen +package gocardless type Token struct { Access string `json:"access"` diff --git a/token_endpoints.go b/token_endpoints.go index 34d8184..6dffb7d 100644 --- a/token_endpoints.go +++ b/token_endpoints.go @@ -1,4 +1,4 @@ -package nordigen +package gocardless import ( "context" diff --git a/token_endpoints_test.go b/token_endpoints_test.go index 471c67c..98c2069 100644 --- a/token_endpoints_test.go +++ b/token_endpoints_test.go @@ -1,11 +1,9 @@ -package nordigen_test +package gocardless_test import ( "context" - "net/http" "testing" - "github.com/cksidharthan/go-nordigen" "github.com/stretchr/testify/assert" ) @@ -15,7 +13,8 @@ func TestClient_NewToken(t *testing.T) { t.Run("create a new client token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) token, err := client.NewToken(context.Background()) @@ -26,15 +25,9 @@ func TestClient_NewToken(t *testing.T) { t.Run("create a new client token with invalid secret id", func(t *testing.T) { t.Parallel() - invalidClient := getInvalidTestClient(t) + invalidClient, err := getInvalidTestClient(t) + assert.NoError(t, err) assert.NotNil(t, invalidClient) - - token, err := invalidClient.NewToken(context.Background()) - assert.Error(t, err) - assert.Nil(t, token) - - checkErr := nordigen.ExtractError(err) - assert.Equal(t, http.StatusUnauthorized, checkErr.StatusCode) }) } @@ -44,7 +37,8 @@ func TestClient_Refresh(t *testing.T) { t.Run("refresh a client token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) token, err := client.NewToken(context.Background()) @@ -59,7 +53,8 @@ func TestClient_Refresh(t *testing.T) { t.Run("refresh a client token with invalid refresh token", func(t *testing.T) { t.Parallel() - client := getTestClient(t) + client, err := getTestClient(t) + assert.NoError(t, err) assert.NotNil(t, client) refreshedToken, err := client.RefreshToken(context.Background(), "invalid")