diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 40322ab..6bf8be9 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -8,9 +8,6 @@ A brief summary of the changes made in the PR
### Check list
- [ ] `task pr-check` command executed successfully after the changes
-- [ ] Tested helm deploy is working fine in local machine with the changes
-- [ ] Tested docker compose is working fine in local machine with the changes
- [ ] Required changes are made in `Taskfile.yml` (if required)
- [ ] Optimal test coverage is added ( > 70%)
-- [ ] Check if the Traces are present in Jaeger
- [ ] Ran manual e2e testing (if required)
\ No newline at end of file
diff --git a/README.md b/README.md
index ea25d8f..fd37dbe 100644
--- a/README.md
+++ b/README.md
@@ -18,4 +18,10 @@ Usage examples can be found in the respective package's `*_test.go` files.
Please refer to [Nordigen API Documentation](https://nordigen.com/en/docs/account-information/integration/parameters-and-responses/) 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/weportfolio/go-nordigen/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 3e60d69..a804c7d 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -1,5 +1,17 @@
version: 3
+env:
+ TAG_NAME:
+ sh: |
+ # check if the current branch is main
+ if [ "$(git rev-parse --abbrev-ref HEAD)" = "main" ]; then
+ # if the current branch is main, then use the version as is
+ cat .version
+ else
+ # if the current branch is not main, then append the branch hash to the version
+ cat .version | git rev-parse --short HEAD
+ fi
+
tasks:
dev-setup:
desc: "Setup development environment"
@@ -42,5 +54,13 @@ tasks:
tag:
desc: "Tagging the release"
cmds:
- - git tag -a $(cat .version) -m "Release v$(cat .version)"
- - git push origin $(cat .version)
\ No newline at end of file
+ - git tag -a {{.TAG_NAME}} -m "Release {{.TAG_NAME}}"
+ - git push origin {{.TAG_NAME}}
+
+ pr-check:
+ desc: "PR Check"
+ cmds:
+ - task deps
+ - task fmt
+ - task lint
+ - task test
\ No newline at end of file
diff --git a/accounts.go b/accounts.go
new file mode 100644
index 0000000..a5e9cc5
--- /dev/null
+++ b/accounts.go
@@ -0,0 +1,104 @@
+package nordigen
+
+import "time"
+
+type Account struct {
+ ID string `json:"id"`
+ Created string `json:"created"`
+ LastAccessed string `json:"last_accessed"`
+ IBAN string `json:"iban"`
+ InstitutionID string `json:"institution_id"`
+ Status string `json:"status"`
+ OwnerName string `json:"owner_name"`
+}
+
+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"`
+}
+
+type Amount struct {
+ Amount string `json:"amount"`
+ Currency string `json:"currency"`
+}
+
+type Balances struct {
+ Balances []Balance `json:"balances"`
+}
+
+// AccountDetails is a struct that contains the details of an account
+// Some fields might be empty, depending on the account type
+type AccountDetails struct {
+ BBAN string `json:"bban"`
+ BIC string `json:"bic"`
+ Details string `json:"details"`
+ DisplayName string `json:"displayName"`
+ LinkedAccounts string `json:"linkedAccounts"`
+ MisISDN string `json:"misIsdn"`
+ OwnerAddressUnstructured string `json:"ownerAddressUnstructured"`
+ Status string `json:"status"`
+ Usage string `json:"usage"`
+ ResourceID string `json:"resourceId"`
+ IBAN string `json:"iban"`
+ Currency string `json:"currency"`
+ OwnerName string `json:"ownerName"`
+ Name string `json:"name"`
+ Product string `json:"product"`
+ CashAccountType string `json:"cashAccountType"`
+}
+
+type Details struct {
+ Account AccountDetails `json:"account"`
+}
+
+type TransactionParams struct {
+ DateFrom string `url:"date_from,omitempty" json:"date_from,omitempty"`
+ DateTo string `url:"date_to,omitempty" json:"date_to,omitempty"`
+}
+
+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"`
+
+ AdditionalInformation string `json:"additionalInformation"`
+ AdditionalInformationStructured string `json:"additionalInformationStructured"`
+ BalanceAfterTransaction Balance `json:"balanceAfterTransaction"`
+ CheckID string `json:"checkId"`
+ CreditorID string `json:"creditorId"`
+ // CurrencyExchange []string `json:"currencyExchange"`
+ DebtorAgent string `json:"debtorAgent"`
+ EndToEndID string `json:"endToEndId"`
+ EntryReference string `json:"entryReference"`
+ MandateID string `json:"mandateId"`
+ MerchantCategoryCode string `json:"merchantCategoryCode"`
+ RemittanceInformationStructured string `json:"remittanceInformationStructured"`
+ RemittanceInformationStructuredArray []string `json:"remittanceInformationStructuredArray"`
+ UltimateCollector string `json:"ultimateCreditor"`
+ UltimateDebtor string `json:"ultimateDebtor"`
+}
+
+type Transactions struct {
+ Transactions TransactionList `json:"transactions"`
+}
+
+type TransactionList struct {
+ Booked []Transaction `json:"booked"`
+ Pending []Transaction `json:"pending"`
+}
diff --git a/accounts_endpoints.go b/accounts_endpoints.go
new file mode 100644
index 0000000..288c9ec
--- /dev/null
+++ b/accounts_endpoints.go
@@ -0,0 +1,52 @@
+package nordigen
+
+import (
+ "context"
+)
+
+// GetAccount retrieves an account by ID
+func (c Client) GetAccount(ctx context.Context, token string, accountID string) (*Account, error) {
+ var account Account
+ endpointURL := AccountsPath + accountID
+ err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &account)
+ if err != nil {
+ return nil, err
+ }
+ return &account, nil
+}
+
+// GetAccountBalances retrieves balances for an account by ID
+func (c Client) GetAccountBalances(ctx context.Context, token string, accountID string) (*Balances, error) {
+ var balances Balances
+ endpointURL := AccountsPath + accountID + "/balances"
+ err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &balances)
+ if err != nil {
+ return nil, err
+ }
+
+ return &balances, nil
+}
+
+// GetAccountDetails retrieves details for an account by ID
+func (c Client) GetAccountDetails(ctx context.Context, token string, accountID string) (*Details, error) {
+ var details Details
+ endpointURL := AccountsPath + accountID + "/details"
+ err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &details)
+ if err != nil {
+ return nil, err
+ }
+
+ return &details, nil
+}
+
+// GetAccountTransactions retrieves transactions for an account by ID
+func (c Client) GetAccountTransactions(ctx context.Context, token string, accountID string) (*Transactions, error) {
+ var transactions Transactions
+ endpointURL := AccountsPath + accountID + "/transactions"
+ err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &transactions)
+ if err != nil {
+ return nil, err
+ }
+
+ return &transactions, nil
+}
diff --git a/endpoints/accounts/accounts_endpoints_test.go b/accounts_endpoints_test.go
similarity index 53%
rename from endpoints/accounts/accounts_endpoints_test.go
rename to accounts_endpoints_test.go
index 1068ea9..1875ca8 100644
--- a/endpoints/accounts/accounts_endpoints_test.go
+++ b/accounts_endpoints_test.go
@@ -1,12 +1,11 @@
-package accounts_test
+package nordigen_test
import (
"context"
- "github.com/stretchr/testify/assert"
- "github.com/weportfolio/go-nordigen"
- "github.com/weportfolio/go-nordigen/consts"
"os"
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestClient_GetAccount(t *testing.T) {
@@ -15,18 +14,16 @@ func TestClient_GetAccount(t *testing.T) {
t.Run("get an account by ID", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
testAccountID := os.Getenv("NORDIGEN_TEST_ACCOUNT_ID")
- account, err := client.Accounts().GetAccount(context.Background(), token.Access, testAccountID)
+ account, err := client.GetAccount(context.Background(), token.Access, testAccountID)
assert.NoError(t, err)
assert.NotNil(t, account)
})
@@ -34,39 +31,35 @@ func TestClient_GetAccount(t *testing.T) {
t.Run("get an account by invalid ID", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
- account, err := client.Accounts().GetAccount(context.Background(), token.Access, "invalid")
+ account, err := client.GetAccount(context.Background(), token.Access, "invalid")
assert.Error(t, err)
assert.Nil(t, account)
})
}
-func TestClient_GetBalances(t *testing.T) {
+func TestClient_GetAccountBalances(t *testing.T) {
t.Parallel()
t.Run("get balances for an account by ID", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
testAccountID := os.Getenv("NORDIGEN_TEST_ACCOUNT_ID")
- balances, err := client.Accounts().GetBalances(context.Background(), token.Access, testAccountID)
+ balances, err := client.GetAccountBalances(context.Background(), token.Access, testAccountID)
assert.NoError(t, err)
assert.NotNil(t, balances)
})
@@ -74,39 +67,35 @@ func TestClient_GetBalances(t *testing.T) {
t.Run("get balances for an account by invalid ID", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
- balances, err := client.Accounts().GetBalances(context.Background(), token.Access, "invalid")
+ balances, err := client.GetAccountBalances(context.Background(), token.Access, "invalid")
assert.Error(t, err)
assert.Nil(t, balances)
})
}
-func TestClient_GetDetails(t *testing.T) {
+func TestClient_GetAccountDetails(t *testing.T) {
t.Parallel()
t.Run("get details for an account by ID", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
testAccountID := os.Getenv("NORDIGEN_TEST_ACCOUNT_ID")
- details, err := client.Accounts().GetDetails(context.Background(), token.Access, testAccountID)
+ details, err := client.GetAccountDetails(context.Background(), token.Access, testAccountID)
assert.NoError(t, err)
assert.NotNil(t, details)
})
@@ -114,39 +103,35 @@ func TestClient_GetDetails(t *testing.T) {
t.Run("get details for an account by invalid ID", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
- details, err := client.Accounts().GetDetails(context.Background(), token.Access, "invalid")
+ details, err := client.GetAccountDetails(context.Background(), token.Access, "invalid")
assert.Error(t, err)
assert.Nil(t, details)
})
}
-func TestClient_GetTransactions(t *testing.T) {
+func TestClient_GetAccountTransactions(t *testing.T) {
t.Parallel()
t.Run("get transactions for an account by ID", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
testAccountID := os.Getenv("NORDIGEN_TEST_ACCOUNT_ID")
- transactions, err := client.Accounts().GetTransactions(context.Background(), token.Access, testAccountID)
+ transactions, err := client.GetAccountTransactions(context.Background(), token.Access, testAccountID)
assert.NoError(t, err)
assert.NotNil(t, transactions)
})
@@ -154,16 +139,14 @@ func TestClient_GetTransactions(t *testing.T) {
t.Run("get transactions for an account by invalid ID", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
- transactions, err := client.Accounts().GetTransactions(context.Background(), token.Access, "invalid")
+ transactions, err := client.GetAccountTransactions(context.Background(), token.Access, "invalid")
assert.Error(t, err)
assert.Nil(t, transactions)
})
diff --git a/endpoints/agreements/agreements.go b/agreements.go
similarity index 95%
rename from endpoints/agreements/agreements.go
rename to agreements.go
index 7edd267..ca31956 100644
--- a/endpoints/agreements/agreements.go
+++ b/agreements.go
@@ -1,4 +1,4 @@
-package agreements
+package nordigen
import "time"
@@ -19,7 +19,7 @@ type Agreement struct {
Accepted time.Time `json:"accepted"`
}
-type ListRequestParams struct {
+type ListAgreementsParams struct {
Limit int `url:"limit,omitempty" json:"limit,omitempty"`
Offset int `url:"offset,omitempty" json:"offset,omitempty"`
}
diff --git a/agreements_endpoints.go b/agreements_endpoints.go
new file mode 100644
index 0000000..8e83094
--- /dev/null
+++ b/agreements_endpoints.go
@@ -0,0 +1,72 @@
+package nordigen
+
+import (
+ "context"
+ "strconv"
+)
+
+// CreateAgreement creates a new agreement for the enduser
+func (c Client) CreateAgreement(ctx context.Context, token string, agreementRequestBody AgreementRequestBody) (*Agreement, error) {
+ var agreement Agreement
+ err := c.HTTP.Post(ctx, AgreementsEndusersPath, RequestHeadersWithAuth(token), agreementRequestBody, &agreement)
+ if err != nil {
+ return nil, err
+ }
+
+ return &agreement, nil
+}
+
+// FetchAgreement retrieves an agreement for the enduser by agreementID
+func (c Client) FetchAgreement(ctx context.Context, token string, agreementID string) (*Agreement, error) {
+ var agreement Agreement
+ err := c.HTTP.Get(ctx, AgreementsEndusersPath+agreementID, RequestHeadersWithAuth(token), &agreement)
+ if err != nil {
+ return nil, err
+ }
+
+ return &agreement, nil
+}
+
+// ListAgreements returns a list of agreements for the enduser
+func (c Client) ListAgreements(ctx context.Context, token string, requestParams *ListAgreementsParams) (*Agreements, error) {
+ var agreements Agreements
+
+ endpointURL := AgreementsEndusersPath
+ if requestParams != nil {
+ if requestParams.Limit != 0 {
+ endpointURL = endpointURL + "?" + strconv.Itoa(requestParams.Limit)
+ }
+ if requestParams.Offset != 0 {
+ endpointURL = endpointURL + "&" + strconv.Itoa(requestParams.Offset)
+ }
+ }
+
+ err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &agreements)
+ if err != nil {
+ return nil, err
+ }
+
+ return &agreements, nil
+}
+
+// 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)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// UpdateAgreement updates an agreement for the enduser by agreementID
+func (c Client) UpdateAgreement(ctx context.Context, token string, 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)
+ if err != nil {
+ return nil, err
+ }
+
+ return &agreement, nil
+}
diff --git a/agreements_endpoints_test.go b/agreements_endpoints_test.go
new file mode 100644
index 0000000..ee5d4ac
--- /dev/null
+++ b/agreements_endpoints_test.go
@@ -0,0 +1,243 @@
+package nordigen_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/weportfolio/go-nordigen"
+)
+
+func TestClient_CreateAgreement(t *testing.T) {
+ t.Parallel()
+
+ 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())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, agreement)
+ assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID)
+ })
+
+ t.Run("create a new agreement with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), "invalid", agreementRequestBody)
+ assert.Error(t, err)
+ assert.Nil(t, agreement)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
+
+func TestClient_FetchAgreement(t *testing.T) {
+ t.Parallel()
+
+ t.Run("fetch an agreement", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, agreement)
+ assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID)
+
+ fetchedAgreement, err := client.FetchAgreement(context.Background(), token.Access, agreement.ID)
+ assert.NoError(t, err)
+ assert.NotNil(t, fetchedAgreement)
+ assert.Equal(t, agreement.ID, fetchedAgreement.ID)
+ })
+
+ t.Run("fetch an agreement with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), "invalid", agreementRequestBody)
+ assert.Error(t, err)
+ assert.Nil(t, agreement)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
+
+func TestClient_ListAgreements(t *testing.T) {
+ t.Parallel()
+
+ t.Run("list agreements", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ responseAgreements, err := client.ListAgreements(context.Background(), token.Access, nil)
+ assert.NoError(t, err)
+ assert.NotNil(t, responseAgreements)
+ })
+
+ t.Run("list agreements with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ responseAgreements, err := client.ListAgreements(context.Background(), "invalid", nil)
+ assert.Error(t, err)
+ assert.Nil(t, responseAgreements)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
+
+func TestClient_DeleteAgreement(t *testing.T) {
+ t.Parallel()
+
+ t.Run("delete an agreement", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, agreement)
+ assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID)
+
+ err = client.DeleteAgreement(context.Background(), token.Access, agreement.ID)
+ assert.NoError(t, err)
+ })
+
+ t.Run("delete an agreement with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ err := client.DeleteAgreement(context.Background(), "invalid", "invalid")
+ assert.Error(t, err)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
+
+func TestClient_UpdateAgreement(t *testing.T) {
+ t.Parallel()
+
+ t.Run("update an agreement", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, agreement)
+ assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID)
+
+ updateRequestBody := nordigen.UpdateRequestBody{
+ UserAgent: "test",
+ IPAddress: "0.0.0.0",
+ }
+
+ updatedAgreement, err := client.UpdateAgreement(context.Background(), token.Access, agreement.ID, updateRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, updatedAgreement)
+ assert.Equal(t, nordigen.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)
+ assert.NotNil(t, client)
+
+ updateRequestBody := nordigen.UpdateRequestBody{
+ UserAgent: "test",
+ IPAddress: "",
+ }
+
+ updatedAgreement, err := client.UpdateAgreement(context.Background(), "invalid", "invalid", updateRequestBody)
+ assert.Error(t, err)
+ assert.Nil(t, updatedAgreement)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
diff --git a/consts/api_info.go b/api_info.go
similarity index 58%
rename from consts/api_info.go
rename to api_info.go
index a9004d6..5db993f 100644
--- a/consts/api_info.go
+++ b/api_info.go
@@ -1,4 +1,4 @@
-package consts
+package nordigen
const (
NordigenBaseURL = "https://ob.nordigen.com/api"
@@ -7,20 +7,15 @@ const (
)
const (
- AccountsPath = "/accounts/:id/"
- AccountBalancesPath = "/accounts/:id/balances/"
- AccountDetailsPath = "/accounts/:id/details/"
- AccountTransactionsPath = "/accounts/:id/transactions/"
+ AccountsPath = "/accounts/"
)
const (
- AccountsTransactionPremiumPath = "/accounts/premium/:id/transactions/"
+ AccountsTransactionPremiumPath = "/accounts/premium/"
)
const (
- AgreementsEndusersPath = "/agreements/enduser/"
- AgreementsEnduserPath = "/agreements/enduser/:id/"
- AgreementsEnduserAcceptPath = "/agreements/enduser/:id/accept/"
+ AgreementsEndusersPath = "/agreements/enduser/"
)
const (
@@ -28,18 +23,11 @@ const (
)
const (
- PaymentsPath = "/payments/"
- PaymentPath = "/payments/:id/"
- PaymentSubmitPath = "/payments/:id/submit/"
- PaymentsAccountPath = "/payments/account/"
- PaymentsCreditorsPath = "/payments/creditors/"
- PaymentsCreditorPath = "/payments/creditors/:id/"
- PaymentsFieldsInstitutionPath = "/payments/fields/:institution_id/"
+ PaymentsPath = "/payments/"
)
const (
- RequisitionsPath = "/requisitions"
- RequisitionPath = "/requisitions/:id/"
+ RequisitionsPath = "/requisitions/"
)
const (
@@ -87,5 +75,5 @@ const (
)
const (
- TestInstitutionID = "REVOLUT_REVOLT21"
+ TestInstitutionID = "N26_NTSBDEB1"
)
diff --git a/assets/cover-treemap.svg b/assets/cover-treemap.svg
index 8132a64..ad14bae 100644
--- a/assets/cover-treemap.svg
+++ b/assets/cover-treemap.svg
@@ -7,153 +7,142 @@
>
-
+
github.com/weportfolio/go-nordigen/endpoints
+ data-math="N">github.com/weportfolio/go-nordigen
-
+
accounts
+ data-math="N">accounts_endpoints.go
-
+
agreements
+ data-math="N">agreements_endpoints.go
-
+
institutions
+ transform="translate(991.843604,505.958621) scale(0.001810)"
+ style="font-family: Open Sans, verdana, arial, sans-serif !important; font-size: 12px; fill: rgb(255, 255, 255, 65535); fill-opacity: 1; white-space: pre;"
+ data-math="N">client.go
-
+
requisitions
+ data-math="N">error.go
-
+
token
+ data-math="N">helpers.go
-
+
accounts_endpoints.go
+ transform="translate(36.000000,57.200000) 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">http-client.go
-
-
-
-
-
-
-
+
agreements_endpoints.go
+ data-math="N">institutions_endpoints.go
-
-
-
-
-
-
-
-
-
-
-
-
-
+
institutions_endpoints.go
+ data-math="N">payments_endpoints.go
-
+
+premium_endpoints.go
+
-
+
requisitions_endpoints.go
@@ -161,18 +150,12 @@
-
-
-
-
-
-
-
+
token_endpoints.go
diff --git a/client.go b/client.go
index 1d01d4d..b374f24 100644
--- a/client.go
+++ b/client.go
@@ -1,62 +1,25 @@
package nordigen
-import (
- "github.com/weportfolio/go-nordigen/endpoints/accounts"
- "github.com/weportfolio/go-nordigen/endpoints/agreements"
- "github.com/weportfolio/go-nordigen/endpoints/institutions"
- "github.com/weportfolio/go-nordigen/endpoints/payments"
- "github.com/weportfolio/go-nordigen/endpoints/requisitions"
- "github.com/weportfolio/go-nordigen/endpoints/token"
-)
-
// Client is the Nordigen client
type Client struct {
- accounts *accounts.Client
- token *token.Client
- institutions *institutions.Client
- agreements *agreements.Client
- requisitions *requisitions.Client
- payments *payments.Client
-}
-
-// Accounts returns the accounts client
-func (c *Client) Accounts() *accounts.Client {
- return c.accounts
-}
-
-// Token returns the token client
-func (c *Client) Token() *token.Client {
- return c.token
-}
-
-// Institutions returns the institutions client
-func (c *Client) Institutions() *institutions.Client {
- return c.institutions
-}
-
-// Agreements returns the agreements client
-func (c *Client) Agreements() *agreements.Client {
- return c.agreements
-}
-
-// Requisitions returns the requisitions client
-func (c *Client) Requisitions() *requisitions.Client {
- return c.requisitions
+ HTTP IHTTPClient
+ SecretID string
+ SecretKey string
}
-// Payments returns the payments client
-func (c *Client) Payments() *payments.Client {
- return c.payments
+type Config struct {
+ BaseURL string
+ APIVersion string
+ SecretID string
+ SecretKey string
+ HTTP *Client
}
// New creates a new Nordigen client
-func New(secretID, secretKey string) *Client {
+func New(config *Config) *Client {
return &Client{
- accounts: accounts.New(secretID, secretKey),
- token: token.New(secretID, secretKey),
- institutions: institutions.New(secretID, secretKey),
- agreements: agreements.New(secretID, secretKey),
- requisitions: requisitions.New(secretID, secretKey),
- payments: payments.New(secretID, secretKey),
+ HTTP: NewHTTPClient(config.BaseURL, config.APIVersion),
+ SecretID: config.SecretID,
+ SecretKey: config.SecretKey,
}
}
diff --git a/client_test.go b/client_test.go
new file mode 100644
index 0000000..516ca12
--- /dev/null
+++ b/client_test.go
@@ -0,0 +1,41 @@
+package nordigen_test
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/weportfolio/go-nordigen"
+)
+
+func getTestClient(t *testing.T) *nordigen.Client {
+ t.Helper()
+
+ secretID := os.Getenv("NORDIGEN_SECRET_ID")
+ secretKey := os.Getenv("NORDIGEN_SECRET_KEY")
+ if secretID == "" || secretKey == "" {
+ fmt.Println("NORDIGEN_SECRET_ID or NORDIGEN_SECRET_KEY is not set")
+ }
+
+ return nordigen.New(
+ &nordigen.Config{
+ BaseURL: nordigen.NordigenBaseURL,
+ APIVersion: nordigen.APIVersion,
+ SecretID: secretID,
+ SecretKey: secretKey,
+ },
+ )
+}
+
+func getInvalidTestClient(t *testing.T) *nordigen.Client {
+ t.Helper()
+
+ return nordigen.New(
+ &nordigen.Config{
+ BaseURL: nordigen.NordigenBaseURL,
+ APIVersion: nordigen.APIVersion,
+ SecretID: "invalid",
+ SecretKey: "invalid",
+ },
+ )
+}
diff --git a/endpoints/accounts/accounts.go b/endpoints/accounts/accounts.go
deleted file mode 100644
index a8acc6a..0000000
--- a/endpoints/accounts/accounts.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package accounts
-
-import "time"
-
-type Account struct {
- ID string `json:"id"`
- Created string `json:"created"`
- LastAccessed string `json:"last_accessed"`
- IBAN string `json:"iban"`
- InstitutionID string `json:"institution_id"`
- Status string `json:"status"`
- OwnerName string `json:"owner_name"`
-}
-
-type Balance struct {
- BalanceAmount Amount `json:"balanceAmount"`
- BalanceType string `json:"balanceType"`
- ReferenceDate string `json:"referenceDate"`
-}
-
-type Amount struct {
- Amount string `json:"amount"`
- Currency string `json:"currency"`
-}
-
-type Balances struct {
- Balances []Balance `json:"balances"`
-}
-
-type AccountDetails struct {
- ResourceID string `json:"resourceId"`
- IBAN string `json:"iban"`
- Currency string `json:"currency"`
- OwnerName string `json:"ownerName"`
- Name string `json:"name"`
- Product string `json:"product"`
- CashAccountType string `json:"cashAccountType"`
-}
-
-type Details struct {
- Account AccountDetails `json:"account"`
-}
-
-type TransactionParams struct {
- DateFrom string `url:"date_from,omitempty" json:"date_from,omitempty"`
- DateTo string `url:"date_to,omitempty" json:"date_to,omitempty"`
-}
-
-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"`
-}
-
-type Transactions struct {
- Transactions TransactionList `json:"transactions"`
-}
-
-type TransactionList struct {
- Booked []Transaction `json:"booked"`
- Pending []Transaction `json:"pending"`
-}
diff --git a/endpoints/accounts/accounts_endpoints.go b/endpoints/accounts/accounts_endpoints.go
deleted file mode 100644
index b6e1286..0000000
--- a/endpoints/accounts/accounts_endpoints.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package accounts
-
-import (
- "context"
- "fmt"
- "github.com/weportfolio/go-nordigen/consts"
-)
-
-// GetAccount retrieves an account by ID
-func (c Client) GetAccount(ctx context.Context, token string, accountID string) (*Account, error) {
- var account Account
- endpointURL := fmt.Sprintf("/accounts/%s", accountID)
- err := c.HTTP.Get(ctx, endpointURL, consts.RequestHeadersWithAuth(token), &account)
- if err != nil {
- return nil, err
- }
- return &account, nil
-}
-
-// GetBalances retrieves balances for an account by ID
-func (c Client) GetBalances(ctx context.Context, token string, accountID string) (*Balances, error) {
- var balances Balances
- endpointURL := fmt.Sprintf("/accounts/%s/balances", accountID)
- err := c.HTTP.Get(ctx, endpointURL, consts.RequestHeadersWithAuth(token), &balances)
- if err != nil {
- return nil, err
- }
-
- return &balances, nil
-}
-
-func (c Client) GetDetails(ctx context.Context, token string, accountID string) (*Details, error) {
- var details Details
- endpointURL := fmt.Sprintf("/accounts/%s/details", accountID)
- err := c.HTTP.Get(ctx, endpointURL, consts.RequestHeadersWithAuth(token), &details)
- if err != nil {
- return nil, err
- }
-
- return &details, nil
-}
-
-func (c Client) GetTransactions(ctx context.Context, token string, accountID string) (*Transactions, error) {
- var transactions Transactions
- endpointURL := fmt.Sprintf("/accounts/%s/transactions", accountID)
- err := c.HTTP.Get(ctx, endpointURL, consts.RequestHeadersWithAuth(token), &transactions)
- if err != nil {
- return nil, err
- }
-
- return &transactions, nil
-}
diff --git a/endpoints/accounts/client.go b/endpoints/accounts/client.go
deleted file mode 100644
index 2552843..0000000
--- a/endpoints/accounts/client.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package accounts
-
-import "github.com/weportfolio/go-nordigen/http"
-
-type Client struct {
- HTTP http.IClient
-}
-
-func New(secretID, secretKey string) *Client {
- return &Client{
- HTTP: http.New(secretID, secretKey),
- }
-}
diff --git a/endpoints/agreements/agreements_endpoints.go b/endpoints/agreements/agreements_endpoints.go
deleted file mode 100644
index 3439d3b..0000000
--- a/endpoints/agreements/agreements_endpoints.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package agreements
-
-import (
- "context"
- "strconv"
-
- "github.com/weportfolio/go-nordigen/consts"
-)
-
-// Post creates a new agreement for the enduser
-func (c Client) Post(ctx context.Context, token string, agreementRequestBody AgreementRequestBody) (*Agreement, error) {
- var agreement Agreement
- err := c.HTTP.Post(ctx, consts.AgreementsEndusersPath, consts.RequestHeadersWithAuth(token), agreementRequestBody, &agreement)
- if err != nil {
- return nil, err
- }
-
- return &agreement, nil
-}
-
-// Fetch retrieves an agreement for the enduser by agreementID
-func (c Client) Fetch(ctx context.Context, token string, agreementID string) (*Agreement, error) {
- var agreement Agreement
- err := c.HTTP.Get(ctx, consts.AgreementsEndusersPath+agreementID, consts.RequestHeadersWithAuth(token), &agreement)
- if err != nil {
- return nil, err
- }
-
- return &agreement, nil
-}
-
-// List returns a list of agreements for the enduser
-func (c Client) List(ctx context.Context, token string, requestParams *ListRequestParams) (*Agreements, error) {
- var agreements Agreements
-
- endpointURL := consts.AgreementsEndusersPath
- if requestParams != nil {
- if requestParams.Limit != 0 {
- endpointURL = endpointURL + "?" + strconv.Itoa(requestParams.Limit)
- }
- if requestParams.Offset != 0 {
- endpointURL = endpointURL + "&" + strconv.Itoa(requestParams.Offset)
- }
- }
-
- err := c.HTTP.Get(ctx, endpointURL, consts.RequestHeadersWithAuth(token), &agreements)
- if err != nil {
- return nil, err
- }
-
- return &agreements, nil
-}
-
-// Delete deletes an agreement for the enduser by agreementID
-func (c Client) Delete(ctx context.Context, token string, agreementID string) error {
- err := c.HTTP.Delete(ctx, consts.AgreementsEndusersPath+agreementID, consts.RequestHeadersWithAuth(token), nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// Update updates an agreement for the enduser by agreementID
-func (c Client) Update(ctx context.Context, token string, 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, consts.AgreementsEndusersPath+agreementID, consts.RequestHeadersWithAuth(token), updateRequestBody, &agreement)
- if err != nil {
- return nil, err
- }
-
- return &agreement, nil
-}
diff --git a/endpoints/agreements/agreements_endpoints_test.go b/endpoints/agreements/agreements_endpoints_test.go
deleted file mode 100644
index 825b2e6..0000000
--- a/endpoints/agreements/agreements_endpoints_test.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package agreements_test
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/weportfolio/go-nordigen"
- "github.com/weportfolio/go-nordigen/consts"
- "github.com/weportfolio/go-nordigen/endpoints/agreements"
-)
-
-func TestClient_Post(t *testing.T) {
- t.Parallel()
-
- t.Run("create a new agreement", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), token.Access, agreementRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, agreement)
- assert.Equal(t, consts.TestInstitutionID, agreement.InstitutionID)
- })
-
- t.Run("create a new agreement with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), "invalid", agreementRequestBody)
- assert.Error(t, err)
- assert.Nil(t, agreement)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
-
-func TestClient_Fetch(t *testing.T) {
- t.Parallel()
-
- t.Run("fetch an agreement", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), token.Access, agreementRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, agreement)
- assert.Equal(t, consts.TestInstitutionID, agreement.InstitutionID)
-
- fetchedAgreement, err := client.Agreements().Fetch(context.Background(), token.Access, agreement.ID)
- assert.NoError(t, err)
- assert.NotNil(t, fetchedAgreement)
- assert.Equal(t, agreement.ID, fetchedAgreement.ID)
- })
-
- t.Run("fetch an agreement with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), "invalid", agreementRequestBody)
- assert.Error(t, err)
- assert.Nil(t, agreement)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
-
-func TestClient_List(t *testing.T) {
- t.Parallel()
-
- t.Run("list agreements", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- responseAgreements, err := client.Agreements().List(context.Background(), token.Access, nil)
- assert.NoError(t, err)
- assert.NotNil(t, responseAgreements)
- })
-
- t.Run("list agreements with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- responseAgreements, err := client.Agreements().List(context.Background(), "invalid", nil)
- assert.Error(t, err)
- assert.Nil(t, responseAgreements)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
-
-func TestClient_Delete(t *testing.T) {
- t.Parallel()
-
- t.Run("delete an agreement", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), token.Access, agreementRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, agreement)
- assert.Equal(t, consts.TestInstitutionID, agreement.InstitutionID)
-
- err = client.Agreements().Delete(context.Background(), token.Access, agreement.ID)
- assert.NoError(t, err)
- })
-
- t.Run("delete an agreement with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- err := client.Agreements().Delete(context.Background(), "invalid", "invalid")
- assert.Error(t, err)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
-
-func TestClient_Update(t *testing.T) {
- t.Parallel()
-
- t.Run("update an agreement", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), token.Access, agreementRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, agreement)
- assert.Equal(t, consts.TestInstitutionID, agreement.InstitutionID)
-
- updateRequestBody := agreements.UpdateRequestBody{
- UserAgent: "test",
- IPAddress: "0.0.0.0",
- }
-
- updatedAgreement, err := client.Agreements().Update(context.Background(), token.Access, agreement.ID, updateRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, updatedAgreement)
- assert.Equal(t, consts.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 := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- updateRequestBody := agreements.UpdateRequestBody{
- UserAgent: "test",
- IPAddress: "",
- }
-
- updatedAgreement, err := client.Agreements().Update(context.Background(), "invalid", "invalid", updateRequestBody)
- assert.Error(t, err)
- assert.Nil(t, updatedAgreement)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
diff --git a/endpoints/agreements/client.go b/endpoints/agreements/client.go
deleted file mode 100644
index 97d8e38..0000000
--- a/endpoints/agreements/client.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package agreements
-
-import "github.com/weportfolio/go-nordigen/http"
-
-type Client struct {
- HTTP http.IClient
-}
-
-func New(secretID, secretKey string) *Client {
- return &Client{
- HTTP: http.New(secretID, secretKey),
- }
-}
diff --git a/endpoints/institutions/client.go b/endpoints/institutions/client.go
deleted file mode 100644
index ef43e69..0000000
--- a/endpoints/institutions/client.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package institutions
-
-import "github.com/weportfolio/go-nordigen/http"
-
-type Client struct {
- HTTP *http.Client
-}
-
-func New(secretID, secretKey string) *Client {
- return &Client{
- HTTP: http.New(secretID, secretKey),
- }
-}
diff --git a/endpoints/institutions/institutions_endpoints.go b/endpoints/institutions/institutions_endpoints.go
deleted file mode 100644
index c3ba22b..0000000
--- a/endpoints/institutions/institutions_endpoints.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package institutions
-
-import (
- "context"
-
- "github.com/weportfolio/go-nordigen/consts"
-)
-
-// List returns a list of institutions
-func (c *Client) List(ctx context.Context, token string, country string, paymentsEnabled bool) ([]Institution, error) {
- endpointURL := consts.InstitutionsPath + "?country=" + country + "&payments_enabled=" + boolToString(paymentsEnabled)
- var institutions []Institution
-
- err := c.HTTP.Get(ctx, endpointURL, consts.RequestHeadersWithAuth(token), &institutions)
- if err != nil {
- return nil, err
- }
-
- return institutions, nil
-}
-
-// Get returns an institution
-func (c *Client) Get(ctx context.Context, token, id string) (*Institution, error) {
- var institution Institution
- err := c.HTTP.Get(ctx, consts.InstitutionsPath+"/"+id, consts.RequestHeadersWithAuth(token), &institution)
- if err != nil {
- return nil, err
- }
-
- return &institution, nil
-}
-
-// boolToString converts a bool to a string
-func boolToString(b bool) string {
- if b {
- return "true"
- }
- return "false"
-}
diff --git a/endpoints/institutions/institutions_endpoints_test.go b/endpoints/institutions/institutions_endpoints_test.go
deleted file mode 100644
index 1600845..0000000
--- a/endpoints/institutions/institutions_endpoints_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package institutions_test
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/weportfolio/go-nordigen"
- "github.com/weportfolio/go-nordigen/consts"
-)
-
-func TestClient_List(t *testing.T) {
- t.Parallel()
-
- t.Run("list institutions", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- institutions, err := client.Institutions().List(context.Background(), token.Access, consts.NetherlandsInstitution, true)
- assert.NoError(t, err)
- assert.NotNil(t, institutions)
- })
-
- t.Run("list institutions with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- institutions, err := client.Institutions().List(context.Background(), "invalid", consts.NetherlandsInstitution, true)
- assert.Error(t, err)
- assert.Nil(t, institutions)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
diff --git a/endpoints/payments/client.go b/endpoints/payments/client.go
deleted file mode 100644
index 281d033..0000000
--- a/endpoints/payments/client.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package payments
-
-import "github.com/weportfolio/go-nordigen/http"
-
-type Client struct {
- HTTP http.IClient
-}
-
-func New(secretID, secretKey string) *Client {
- return &Client{
- HTTP: http.New(secretID, secretKey),
- }
-}
diff --git a/endpoints/payments/payments.go b/endpoints/payments/payments.go
deleted file mode 100644
index d0eb223..0000000
--- a/endpoints/payments/payments.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package payments
-
-import "github.com/weportfolio/go-nordigen/endpoints/accounts"
-
-type ListParams struct {
- Limit int `url:"limit,omitempty"`
- Offset int `url:"offset,omitempty"`
-}
-
-type Payment struct {
- PaymentID string `json:"payment_id"`
- PaymentStatus string `json:"payment_status"`
- PaymentProduct string `json:"payment_product"`
- PaymentType string `json:"payment_type"`
- Redirect string `json:"redirect"`
- Description string `json:"description"`
- CustomPaymentID string `json:"custom_payment_id"`
- CreditorAccount string `json:"creditor_account"`
- CreditorObject PaymentAccount `json:"creditor_object"`
- DebtorObject PaymentAccount `json:"debtor_object"`
- InstructedAmount accounts.Amount `json:"instructed_amount"`
-}
-
-type Payments struct {
- Code int `json:"code"`
- Next string `json:"next"`
- Previous string `json:"previous"`
- Results []Payment `json:"results"`
-}
-
-type PaymentAccount struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Type string `json:"type"`
- Account string `json:"account"`
- Currency string `json:"currency"`
- AddressCountry string `json:"address_country"`
- InstitutionID string `json:"institution_id"`
- Agent string `json:"agent"`
- AgentName string `json:"agent_name"`
- AddressStreet string `json:"address_street"`
- PostCode string `json:"post_code"`
- TypeNumber string `json:"type_number"`
-}
diff --git a/endpoints/payments/payments_endpoints.go b/endpoints/payments/payments_endpoints.go
deleted file mode 100644
index 531f48d..0000000
--- a/endpoints/payments/payments_endpoints.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package payments
-
-import (
- "context"
- "github.com/weportfolio/go-nordigen/consts"
- "strconv"
-)
-
-// List returns a list of payments
-func (c Client) List(ctx context.Context, token string, limit, offset int) (*Payments, error) {
- var response Payments
-
- params := map[string]string{
- "limit": strconv.Itoa(limit),
- "offset": strconv.Itoa(offset),
- }
-
- err := c.HTTP.Get(ctx, consts.BuildQueryURL(consts.PaymentsPath, params), consts.RequestHeadersWithAuth(token), &response)
- if err != nil {
- return nil, err
- }
-
- return &response, nil
-}
diff --git a/endpoints/premium/.gitkeep b/endpoints/premium/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/endpoints/requisitions/client.go b/endpoints/requisitions/client.go
deleted file mode 100644
index c528332..0000000
--- a/endpoints/requisitions/client.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package requisitions
-
-import "github.com/weportfolio/go-nordigen/http"
-
-type Client struct {
- HTTP *http.Client
-}
-
-func New(secretID, secretKey string) *Client {
- return &Client{
- HTTP: http.New(secretID, secretKey),
- }
-}
diff --git a/endpoints/requisitions/requisitions_endpoints.go b/endpoints/requisitions/requisitions_endpoints.go
deleted file mode 100644
index 08b433b..0000000
--- a/endpoints/requisitions/requisitions_endpoints.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package requisitions
-
-import (
- "context"
- "strconv"
-
- "github.com/weportfolio/go-nordigen/consts"
-)
-
-// Post creates a new requisition
-func (c Client) Post(ctx context.Context, token string, requisitionRequestBody *RequisitionRequestBody) (*Requisition, error) {
- var requisition Requisition
- err := c.HTTP.Post(ctx, consts.RequisitionsPath+"/", consts.RequestHeadersWithAuth(token), requisitionRequestBody, &requisition)
- if err != nil {
- return nil, err
- }
-
- return &requisition, nil
-}
-
-func (c Client) List(ctx context.Context, token string, requestParams *ListRequestParams) (*Requisitions, error) {
- var requisitions Requisitions
-
- endpointURL := consts.RequisitionsPath + "/"
- if requestParams != nil {
- if requestParams.Limit != 0 {
- endpointURL = endpointURL + "?" + strconv.Itoa(requestParams.Limit)
- }
- if requestParams.Offset != 0 {
- endpointURL = endpointURL + "&" + strconv.Itoa(requestParams.Offset)
- }
- }
-
- err := c.HTTP.Get(ctx, endpointURL, consts.RequestHeadersWithAuth(token), &requisitions)
- if err != nil {
- return nil, err
- }
-
- return &requisitions, nil
-}
-
-// Fetch retrieves a requisition by requisitionID
-func (c Client) Fetch(ctx context.Context, token string, requisitionID string) (*Requisition, error) {
- var requisition Requisition
- err := c.HTTP.Get(ctx, consts.RequisitionsPath+"/"+requisitionID, consts.RequestHeadersWithAuth(token), &requisition)
- if err != nil {
- return nil, err
- }
-
- return &requisition, nil
-}
-
-// Delete deletes a requisition by requisitionID
-func (c Client) Delete(ctx context.Context, token string, requisitionID string) error {
- err := c.HTTP.Delete(ctx, consts.RequisitionsPath+"/"+requisitionID, consts.RequestHeadersWithAuth(token), nil)
- if err != nil {
- return err
- }
-
- return nil
-}
diff --git a/endpoints/requisitions/requisitions_endpoints_test.go b/endpoints/requisitions/requisitions_endpoints_test.go
deleted file mode 100644
index 2be72d0..0000000
--- a/endpoints/requisitions/requisitions_endpoints_test.go
+++ /dev/null
@@ -1,273 +0,0 @@
-package requisitions_test
-
-import (
- "context"
- "github.com/stretchr/testify/assert"
- "github.com/weportfolio/go-nordigen"
- "github.com/weportfolio/go-nordigen/consts"
- "github.com/weportfolio/go-nordigen/endpoints/agreements"
- "github.com/weportfolio/go-nordigen/endpoints/requisitions"
- "math/rand"
- "strconv"
- "testing"
-)
-
-func TestClient_Post(t *testing.T) {
- t.Parallel()
-
- t.Run("create new requisition", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), token.Access, agreementRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, agreement)
- assert.Equal(t, consts.TestInstitutionID, agreement.InstitutionID)
-
- requisitionRequestBody := &requisitions.RequisitionRequestBody{
- Redirect: "https://example.com",
- InstitutionID: consts.TestInstitutionID,
- Agreement: agreement.ID,
- Reference: strconv.Itoa(rand.Intn(1000000000000000000)),
- UserLanguage: consts.LangEN,
- AccountSelection: false,
- RedirectImmediate: false,
- }
-
- requisition, err := client.Requisitions().Post(context.Background(), token.Access, requisitionRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, requisition)
- assert.Equal(t, consts.TestInstitutionID, requisition.InstitutionID)
- })
-
- t.Run("create new requisition with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- requisitionRequestBody := &requisitions.RequisitionRequestBody{
- Redirect: "https://example.com",
- InstitutionID: consts.TestInstitutionID,
- Agreement: "invalid",
- Reference: "12345",
- UserLanguage: consts.LangEN,
- AccountSelection: false,
- RedirectImmediate: false,
- }
-
- requisition, err := client.Requisitions().Post(context.Background(), "invalid", requisitionRequestBody)
- assert.Error(t, err)
- assert.Nil(t, requisition)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
-
-func TestClient_List(t *testing.T) {
- t.Parallel()
-
- t.Run("list requisitions", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), token.Access, agreementRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, agreement)
- assert.Equal(t, consts.TestInstitutionID, agreement.InstitutionID)
-
- requisitionRequestBody := &requisitions.RequisitionRequestBody{
- Redirect: "https://example.com",
- InstitutionID: consts.TestInstitutionID,
- Agreement: agreement.ID,
- Reference: strconv.Itoa(rand.Intn(1000000000000000000)),
- UserLanguage: consts.LangEN,
- AccountSelection: false,
- RedirectImmediate: false,
- }
-
- requisition, err := client.Requisitions().Post(context.Background(), token.Access, requisitionRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, requisition)
- assert.Equal(t, consts.TestInstitutionID, requisition.InstitutionID)
-
- responseRequisitions, err := client.Requisitions().List(context.Background(), token.Access, nil)
- assert.NoError(t, err)
- assert.NotNil(t, responseRequisitions)
- })
-
- t.Run("list requisitions with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- responseRequisitions, err := client.Requisitions().List(context.Background(), "invalid", nil)
- assert.Error(t, err)
- assert.Nil(t, responseRequisitions)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
-
-func TestClient_Fetch(t *testing.T) {
- t.Parallel()
-
- t.Run("fetch requisition", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), token.Access, agreementRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, agreement)
- assert.Equal(t, consts.TestInstitutionID, agreement.InstitutionID)
-
- requisitionRequestBody := &requisitions.RequisitionRequestBody{
- Redirect: "https://example.com",
- InstitutionID: consts.TestInstitutionID,
- Agreement: agreement.ID,
- Reference: strconv.Itoa(rand.Intn(1000000000000000000)),
- UserLanguage: consts.LangEN,
- AccountSelection: false,
- RedirectImmediate: false,
- }
-
- requisition, err := client.Requisitions().Post(context.Background(), token.Access, requisitionRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, requisition)
- assert.Equal(t, consts.TestInstitutionID, requisition.InstitutionID)
-
- responseRequisition, err := client.Requisitions().Fetch(context.Background(), token.Access, requisition.ID)
- assert.NoError(t, err)
- assert.NotNil(t, responseRequisition)
- })
-
- t.Run("fetch requisition with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- responseRequisition, err := client.Requisitions().Fetch(context.Background(), "invalid", "invalid")
- assert.Error(t, err)
- assert.Nil(t, responseRequisition)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
-
-func TestClient_Delete(t *testing.T) {
- t.Parallel()
-
- t.Run("delete requisition", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- token, err := client.Token().New(context.Background())
- assert.NoError(t, err)
- assert.NotNil(t, token)
-
- agreementRequestBody := agreements.AgreementRequestBody{
- InstitutionID: consts.TestInstitutionID,
- MaxHistoricalDays: "180",
- AccessValidForDays: "2",
- AccessScope: []string{"balances", "details", "transactions"},
- }
-
- agreement, err := client.Agreements().Post(context.Background(), token.Access, agreementRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, agreement)
- assert.Equal(t, consts.TestInstitutionID, agreement.InstitutionID)
-
- requisitionRequestBody := &requisitions.RequisitionRequestBody{
- Redirect: "https://example.com",
- InstitutionID: consts.TestInstitutionID,
- Agreement: agreement.ID,
- Reference: strconv.Itoa(rand.Intn(1000000000000000000)),
- UserLanguage: consts.LangEN,
- AccountSelection: false,
- RedirectImmediate: false,
- }
-
- requisition, err := client.Requisitions().Post(context.Background(), token.Access, requisitionRequestBody)
- assert.NoError(t, err)
- assert.NotNil(t, requisition)
- assert.Equal(t, consts.TestInstitutionID, requisition.InstitutionID)
-
- err = client.Requisitions().Delete(context.Background(), token.Access, requisition.ID)
- assert.NoError(t, err)
- })
-
- t.Run("delete requisition with invalid token", func(t *testing.T) {
- t.Parallel()
-
- client := nordigen.New(
- consts.GetSecrets(t),
- )
- assert.NotNil(t, client)
-
- err := client.Requisitions().Delete(context.Background(), "invalid", "invalid")
- assert.Error(t, err)
-
- checkErr := consts.ExtractError(err)
- assert.Equal(t, 401, checkErr.StatusCode)
- })
-}
diff --git a/endpoints/token/client.go b/endpoints/token/client.go
deleted file mode 100644
index ddd6872..0000000
--- a/endpoints/token/client.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package token
-
-import "github.com/weportfolio/go-nordigen/http"
-
-type Client struct {
- HTTP *http.Client
-}
-
-func New(secretID, secretKey string) *Client {
- return &Client{
- HTTP: http.New(secretID, secretKey),
- }
-}
diff --git a/endpoints/token/token_endpoints.go b/endpoints/token/token_endpoints.go
deleted file mode 100644
index 9a34951..0000000
--- a/endpoints/token/token_endpoints.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package token
-
-import (
- "context"
-
- "github.com/weportfolio/go-nordigen/consts"
-)
-
-// New creates a new token
-func (c Client) New(ctx context.Context) (*Token, error) {
- var token Token
- accessCreds := map[string]string{
- "secret_id": c.HTTP.APISecretID,
- "secret_key": c.HTTP.APISecretKey,
- }
-
- err := c.HTTP.Post(ctx, consts.TokenNewPath, consts.RequestHeaders(), accessCreds, &token)
- if err != nil {
- return nil, err
- }
-
- return &token, nil
-}
-
-// Refresh refreshes a token
-func (c Client) Refresh(ctx context.Context, refreshToken string) (*Token, error) {
- var token Token
- refreshCreds := map[string]string{
- "refresh": refreshToken,
- }
-
- err := c.HTTP.Post(ctx, consts.TokenRefreshPath, consts.RequestHeaders(), refreshCreds, &token)
- if err != nil {
- return nil, err
- }
-
- return &token, nil
-}
diff --git a/consts/error.go b/error.go
similarity index 97%
rename from consts/error.go
rename to error.go
index 881c407..6363244 100644
--- a/consts/error.go
+++ b/error.go
@@ -1,4 +1,4 @@
-package consts
+package nordigen
import (
"encoding/json"
diff --git a/go.mod b/go.mod
index f2a4134..c7f95f9 100644
--- a/go.mod
+++ b/go.mod
@@ -2,10 +2,7 @@ module github.com/weportfolio/go-nordigen
go 1.20
-require (
- github.com/golang/mock v1.6.0
- github.com/stretchr/testify v1.8.2
-)
+require github.com/stretchr/testify v1.8.2
require (
github.com/davecgh/go-spew v1.1.1 // indirect
diff --git a/go.sum b/go.sum
index 2d9ed42..6a56e69 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,6 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
-github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -12,29 +10,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/consts/helpers.go b/helpers.go
similarity index 67%
rename from consts/helpers.go
rename to helpers.go
index 59f8ab0..3f16425 100644
--- a/consts/helpers.go
+++ b/helpers.go
@@ -1,23 +1,9 @@
-package consts
+package nordigen
import (
"fmt"
- "os"
- "testing"
)
-func GetSecrets(t *testing.T) (string, string) {
- t.Helper()
-
- secretID := os.Getenv("NORDIGEN_SECRET_ID")
- secretKey := os.Getenv("NORDIGEN_SECRET_KEY")
- if secretID == "" || secretKey == "" {
- fmt.Println("NORDIGEN_SECRET_ID or NORDIGEN_SECRET_KEY is not set")
- }
-
- return secretID, secretKey
-}
-
func RequestHeadersWithAuth(token string) map[string]string {
authHeaders := map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", token),
diff --git a/http/http-client.go b/http-client.go
similarity index 58%
rename from http/http-client.go
rename to http-client.go
index 3a4066e..3961874 100644
--- a/http/http-client.go
+++ b/http-client.go
@@ -1,4 +1,4 @@
-package http
+package nordigen
import (
"bytes"
@@ -6,61 +6,50 @@ import (
"encoding/json"
"fmt"
"net/http"
-
- "github.com/weportfolio/go-nordigen/consts"
-)
-
-const (
- NordigenBaseURL = "https://ob.nordigen.com/api"
- APIVersion = "v2"
)
//go:generate mockgen -source=http-client.go -destination=mocks/mock_client.go -package=mocks -build_flags=-mod=mod
-type IClient interface {
+type IHTTPClient interface {
Get(ctx context.Context, path string, headers map[string]string, response interface{}) error
Post(ctx context.Context, path string, headers map[string]string, body interface{}, response interface{}) error
Put(ctx context.Context, path string, headers map[string]string, body interface{}, response interface{}) error
Delete(ctx context.Context, path string, headers map[string]string, response interface{}) error
}
-type Client struct {
- BaseURL string
- APIVersion string
- APISecretID string
- APISecretKey string
+type HTTPClient struct {
+ BaseURL string
+ APIVersion string
}
-func New(secretID, secretKey string) *Client {
- return &Client{
- BaseURL: NordigenBaseURL,
- APIVersion: APIVersion,
- APISecretID: secretID,
- APISecretKey: secretKey,
+func NewHTTPClient(baseURL, apiVersion string) IHTTPClient {
+ return &HTTPClient{
+ BaseURL: baseURL,
+ APIVersion: apiVersion,
}
}
// Get is a wrapper around request that performs a GET request
-func (c *Client) Get(ctx context.Context, path string, headers map[string]string, response interface{}) error {
- return c.request(ctx, http.MethodGet, path, headers, nil, response)
+func (h *HTTPClient) Get(ctx context.Context, path string, headers map[string]string, response interface{}) error {
+ return h.request(ctx, http.MethodGet, path, headers, nil, response)
}
// Post is a wrapper around request that performs a POST request
-func (c *Client) Post(ctx context.Context, path string, headers map[string]string, body interface{}, response interface{}) error {
- return c.request(ctx, http.MethodPost, path, headers, body, response)
+func (h *HTTPClient) Post(ctx context.Context, path string, headers map[string]string, body interface{}, response interface{}) error {
+ return h.request(ctx, http.MethodPost, path, headers, body, response)
}
// Put is a wrapper around request that performs a PUT request
-func (c *Client) Put(ctx context.Context, path string, headers map[string]string, body interface{}, response interface{}) error {
- return c.request(ctx, http.MethodPut, path, headers, body, response)
+func (h *HTTPClient) Put(ctx context.Context, path string, headers map[string]string, body interface{}, response interface{}) error {
+ return h.request(ctx, http.MethodPut, path, headers, body, response)
}
// Delete is a wrapper around request that performs a DELETE request
-func (c *Client) Delete(ctx context.Context, path string, headers map[string]string, response interface{}) error {
- return c.request(ctx, http.MethodDelete, path, headers, nil, response)
+func (h *HTTPClient) Delete(ctx context.Context, path string, headers map[string]string, response interface{}) error {
+ return h.request(ctx, http.MethodDelete, path, headers, nil, response)
}
-// request is a wrapper around http.Client.Do that performs a request and decodes the response into the response interface
-func (c *Client) request(ctx context.Context, method, path string, headers map[string]string, body interface{}, response interface{}) error {
+// request is a wrapper around http.HTTPClient.Do that performs a request and decodes the response into the response interface
+func (h *HTTPClient) request(ctx context.Context, method, path string, headers map[string]string, body interface{}, response interface{}) error {
var bytesBody []byte
var err error
if body != nil {
@@ -70,7 +59,7 @@ func (c *Client) request(ctx context.Context, method, path string, headers map[s
}
}
- req, err := c.newRequest(ctx, method, path, headers, bytesBody)
+ req, err := h.newRequest(ctx, method, path, headers, bytesBody)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
@@ -82,7 +71,7 @@ func (c *Client) request(ctx context.Context, method, path string, headers map[s
}
if resp.StatusCode > 300 {
- return consts.NewError(resp)
+ return NewError(resp)
}
if response != nil {
@@ -93,8 +82,8 @@ func (c *Client) request(ctx context.Context, method, path string, headers map[s
}
// newRequest creates a new http.Request with the given method, path, headers and body
-func (c *Client) newRequest(ctx context.Context, method, path string, headers map[string]string, body []byte) (*http.Request, error) {
- url := c.BaseURL + "/" + c.APIVersion + path
+func (h *HTTPClient) newRequest(ctx context.Context, method, path string, headers map[string]string, body []byte) (*http.Request, error) {
+ url := h.BaseURL + "/" + h.APIVersion + path
var req *http.Request
var err error
diff --git a/http/mocks/mock_client.go b/http/mocks/mock_client.go
deleted file mode 100644
index c5326ee..0000000
--- a/http/mocks/mock_client.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: http-client.go
-
-// Package mocks is a generated GoMock package.
-package mocks
-
-import (
- reflect "reflect"
-
- gomock "github.com/golang/mock/gomock"
-)
-
-// MockIClient is a mock of IClient interface.
-type MockIClient struct {
- ctrl *gomock.Controller
- recorder *MockIClientMockRecorder
-}
-
-// MockIClientMockRecorder is the mock recorder for MockIClient.
-type MockIClientMockRecorder struct {
- mock *MockIClient
-}
-
-// NewMockIClient creates a new mock instance.
-func NewMockIClient(ctrl *gomock.Controller) *MockIClient {
- mock := &MockIClient{ctrl: ctrl}
- mock.recorder = &MockIClientMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockIClient) EXPECT() *MockIClientMockRecorder {
- return m.recorder
-}
-
-// Delete mocks base method.
-func (m *MockIClient) Delete(path string, params map[string]string, response interface{}) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Delete", path, params, response)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockIClientMockRecorder) Delete(path, params, response interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockIClient)(nil).Delete), path, params, response)
-}
-
-// Get mocks base method.
-func (m *MockIClient) Get(path string, params map[string]string, response interface{}) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", path, params, response)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockIClientMockRecorder) Get(path, params, response interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockIClient)(nil).Get), path, params, response)
-}
-
-// Post mocks base method.
-func (m *MockIClient) Post(path string, params map[string]string, body []byte, response interface{}) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Post", path, params, body, response)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Post indicates an expected call of Post.
-func (mr *MockIClientMockRecorder) Post(path, params, body, response interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Post", reflect.TypeOf((*MockIClient)(nil).Post), path, params, body, response)
-}
-
-// Put mocks base method.
-func (m *MockIClient) Put(path string, params map[string]string, body []byte, response interface{}) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Put", path, params, body, response)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Put indicates an expected call of Put.
-func (mr *MockIClientMockRecorder) Put(path, params, body, response interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockIClient)(nil).Put), path, params, body, response)
-}
diff --git a/endpoints/institutions/institutions.go b/institutions.go
similarity index 96%
rename from endpoints/institutions/institutions.go
rename to institutions.go
index 150f322..7fc6944 100644
--- a/endpoints/institutions/institutions.go
+++ b/institutions.go
@@ -1,4 +1,4 @@
-package institutions
+package nordigen
type Institution struct {
ID string `json:"id"`
diff --git a/institutions_endpoints.go b/institutions_endpoints.go
new file mode 100644
index 0000000..ddbcbdf
--- /dev/null
+++ b/institutions_endpoints.go
@@ -0,0 +1,37 @@
+package nordigen
+
+import (
+ "context"
+)
+
+// 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)
+ var institutions []Institution
+
+ err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &institutions)
+ if err != nil {
+ return nil, err
+ }
+
+ return institutions, nil
+}
+
+// FetchInstitution returns an institution
+func (c Client) FetchInstitution(ctx context.Context, token, id string) (*Institution, error) {
+ var institution Institution
+ err := c.HTTP.Get(ctx, InstitutionsPath+id, RequestHeadersWithAuth(token), &institution)
+ if err != nil {
+ return nil, err
+ }
+
+ return &institution, nil
+}
+
+// boolToString converts a bool to a string
+func boolToString(b bool) string {
+ if b {
+ return "true"
+ }
+ return "false"
+}
diff --git a/institutions_endpoints_test.go b/institutions_endpoints_test.go
new file mode 100644
index 0000000..f9a6a50
--- /dev/null
+++ b/institutions_endpoints_test.go
@@ -0,0 +1,76 @@
+package nordigen_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/weportfolio/go-nordigen"
+)
+
+func TestClient_ListInstitutions(t *testing.T) {
+ t.Parallel()
+
+ t.Run("list institutions", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ institutions, err := client.ListInstitutions(context.Background(), token.Access, nordigen.NetherlandsInstitution, true)
+ assert.NoError(t, err)
+ assert.NotNil(t, institutions)
+ })
+
+ t.Run("list institutions with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ institutions, err := client.ListInstitutions(context.Background(), "invalid", nordigen.NetherlandsInstitution, true)
+ assert.Error(t, err)
+ assert.Nil(t, institutions)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
+
+func TestClient_FetchInstitution(t *testing.T) {
+ t.Parallel()
+
+ t.Run("fetch institution", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ institution, err := client.FetchInstitution(context.Background(), token.Access, nordigen.TestInstitutionID)
+ assert.NoError(t, err)
+ assert.NotNil(t, institution)
+ assert.Equal(t, nordigen.TestInstitutionID, institution.ID)
+ })
+
+ t.Run("fetch institution with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ institution, err := client.FetchInstitution(context.Background(), "invalid", nordigen.TestInstitutionID)
+ assert.Error(t, err)
+ assert.Nil(t, institution)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
diff --git a/payments.go b/payments.go
new file mode 100644
index 0000000..573f3cd
--- /dev/null
+++ b/payments.go
@@ -0,0 +1,37 @@
+package nordigen
+
+type Payment struct {
+ PaymentID string `json:"payment_id"`
+ PaymentStatus string `json:"payment_status"`
+ PaymentProduct string `json:"payment_product"`
+ PaymentType string `json:"payment_type"`
+ Redirect string `json:"redirect"`
+ Description string `json:"description"`
+ CustomPaymentID string `json:"custom_payment_id"`
+ CreditorAccount string `json:"creditor_account"`
+ CreditorObject PaymentAccount `json:"creditor_object"`
+ DebtorObject PaymentAccount `json:"debtor_object"`
+ InstructedAmount Amount `json:"instructed_amount"`
+}
+
+type Payments struct {
+ Code int `json:"code"`
+ Next string `json:"next"`
+ Previous string `json:"previous"`
+ Results []Payment `json:"results"`
+}
+
+type PaymentAccount struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Account string `json:"account"`
+ Currency string `json:"currency"`
+ AddressCountry string `json:"address_country"`
+ InstitutionID string `json:"institution_id"`
+ Agent string `json:"agent"`
+ AgentName string `json:"agent_name"`
+ AddressStreet string `json:"address_street"`
+ PostCode string `json:"post_code"`
+ TypeNumber string `json:"type_number"`
+}
diff --git a/payments_endpoints.go b/payments_endpoints.go
new file mode 100644
index 0000000..9d3da2b
--- /dev/null
+++ b/payments_endpoints.go
@@ -0,0 +1,23 @@
+package nordigen
+
+import (
+ "context"
+ "strconv"
+)
+
+// ListPayments returns a list of payments
+func (c Client) ListPayments(ctx context.Context, token string, limit, offset int) (*Payments, error) {
+ var response Payments
+
+ params := map[string]string{
+ "limit": strconv.Itoa(limit),
+ "offset": strconv.Itoa(offset),
+ }
+
+ err := c.HTTP.Get(ctx, BuildQueryURL(PaymentsPath, params), RequestHeadersWithAuth(token), &response)
+ if err != nil {
+ return nil, err
+ }
+
+ return &response, nil
+}
diff --git a/endpoints/payments/payments_endpoints_test.go b/payments_endpoints_test.go
similarity index 50%
rename from endpoints/payments/payments_endpoints_test.go
rename to payments_endpoints_test.go
index 683b4c7..6af1a57 100644
--- a/endpoints/payments/payments_endpoints_test.go
+++ b/payments_endpoints_test.go
@@ -1,30 +1,27 @@
-package payments_test
+package nordigen_test
import (
"context"
- "github.com/stretchr/testify/assert"
- "github.com/weportfolio/go-nordigen"
- "github.com/weportfolio/go-nordigen/consts"
"testing"
+
+ "github.com/stretchr/testify/assert"
)
-func TestClient_List(t *testing.T) {
+func TestClient_ListPayments(t *testing.T) {
t.Parallel()
// Payments are not enabled for the account used in the tests
t.Run("list payments", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
- payments, err := client.Payments().List(context.Background(), token.Access, 10, 0)
+ payments, err := client.ListPayments(context.Background(), token.Access, 10, 0)
assert.Error(t, err)
assert.Nil(t, payments)
})
diff --git a/premium.go b/premium.go
new file mode 100644
index 0000000..d5ccf13
--- /dev/null
+++ b/premium.go
@@ -0,0 +1,73 @@
+package nordigen
+
+import "time"
+
+type PremiumTransactions struct {
+ Transactions PremiumTransactionList `json:"transactions"`
+}
+
+type PremiumTransactionList struct {
+ Booked []PremiumTransaction `json:"booked"`
+ Pending []PremiumTransaction `json:"pending"`
+}
+
+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"`
+
+ AdditionalInformation string `json:"additionalInformation"`
+ AdditionalInformationStructured string `json:"additionalInformationStructured"`
+ BalanceAfterTransaction Balance `json:"balanceAfterTransaction"`
+ CheckID string `json:"checkId"`
+ CreditorID string `json:"creditorId"`
+ // CurrencyExchange []string `json:"currencyExchange"`
+ DebtorAgent string `json:"debtorAgent"`
+ EndToEndID string `json:"endToEndId"`
+ EntryReference string `json:"entryReference"`
+ MandateID string `json:"mandateId"`
+ MerchantCategoryCode string `json:"merchantCategoryCode"`
+ RemittanceInformationStructured string `json:"remittanceInformationStructured"`
+ RemittanceInformationStructuredArray []string `json:"remittanceInformationStructuredArray"`
+ UltimateCollector string `json:"ultimateCreditor"`
+ UltimateDebtor string `json:"ultimateDebtor"`
+ Enrichment Enrichment `json:"enrichment"`
+}
+
+type Enrichment struct {
+ DisplayName string `json:"displayName"`
+ BranchDisplayName string `json:"branchDisplayName"`
+ Location Location `json:"location"`
+ URLs URLs `json:"urls"`
+ TransactionType string `json:"transactionType"`
+ PurposeCategory []string `json:"purposeCategory"`
+ PurposeCategoryID string `json:"purposeCategoryID"`
+}
+
+type Location struct {
+ Address string `json:"address"`
+ City string `json:"city"`
+ Region string `json:"region"`
+ PostalCode string `json:"postalCode"`
+ Country string `json:"country"`
+ Latitude float64 `json:"lat"`
+ Longitude float64 `json:"lon"`
+}
+
+type URLs struct {
+ Website string `json:"website"`
+ Favicon string `json:"favicon"`
+ Logo string `json:"logo"`
+}
diff --git a/premium_endpoints.go b/premium_endpoints.go
new file mode 100644
index 0000000..5a6948a
--- /dev/null
+++ b/premium_endpoints.go
@@ -0,0 +1,15 @@
+package nordigen
+
+import "context"
+
+// ListPremiumTransactions retrieves transactions for an account by ID
+func (c Client) ListPremiumTransactions(ctx context.Context, token string, accountID string) (*PremiumTransactions, error) {
+ var transactions PremiumTransactions
+ endpointURL := AccountsTransactionPremiumPath + accountID + "/transactions"
+ err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &transactions)
+ if err != nil {
+ return nil, err
+ }
+
+ return &transactions, nil
+}
diff --git a/endpoints/requisitions/requisitions.go b/requisitions.go
similarity index 96%
rename from endpoints/requisitions/requisitions.go
rename to requisitions.go
index adb661d..a2d3e02 100644
--- a/endpoints/requisitions/requisitions.go
+++ b/requisitions.go
@@ -1,8 +1,8 @@
-package requisitions
+package nordigen
import "time"
-type ListRequestParams struct {
+type ListRequisitionsParams struct {
Limit int `url:"limit,omitempty" json:"limit,omitempty"`
Offset int `url:"offset,omitempty" json:"offset,omitempty"`
}
diff --git a/requisitions_endpoints.go b/requisitions_endpoints.go
new file mode 100644
index 0000000..22920a3
--- /dev/null
+++ b/requisitions_endpoints.go
@@ -0,0 +1,60 @@
+package nordigen
+
+import (
+ "context"
+ "strconv"
+)
+
+// CreateRequisition creates a new requisition
+func (c Client) CreateRequisition(ctx context.Context, token string, requisitionRequestBody *RequisitionRequestBody) (*Requisition, error) {
+ var requisition Requisition
+ err := c.HTTP.Post(ctx, RequisitionsPath, RequestHeadersWithAuth(token), requisitionRequestBody, &requisition)
+ if err != nil {
+ return nil, err
+ }
+
+ return &requisition, nil
+}
+
+// ListRequisition lists all requisitions
+func (c Client) ListRequisitions(ctx context.Context, token string, requestParams *ListRequisitionsParams) (*Requisitions, error) {
+ var requisitions Requisitions
+
+ endpointURL := RequisitionsPath
+ if requestParams != nil {
+ if requestParams.Limit != 0 {
+ endpointURL = endpointURL + "?" + strconv.Itoa(requestParams.Limit)
+ }
+ if requestParams.Offset != 0 {
+ endpointURL = endpointURL + "&" + strconv.Itoa(requestParams.Offset)
+ }
+ }
+
+ err := c.HTTP.Get(ctx, endpointURL, RequestHeadersWithAuth(token), &requisitions)
+ if err != nil {
+ return nil, err
+ }
+
+ return &requisitions, nil
+}
+
+// FetchRequisition retrieves a requisition by requisitionID
+func (c Client) FetchRequisition(ctx context.Context, token string, requisitionID string) (*Requisition, error) {
+ var requisition Requisition
+ err := c.HTTP.Get(ctx, RequisitionsPath+requisitionID, RequestHeadersWithAuth(token), &requisition)
+ if err != nil {
+ return nil, err
+ }
+
+ return &requisition, nil
+}
+
+// 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)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/requisitions_endpoints_test.go b/requisitions_endpoints_test.go
new file mode 100644
index 0000000..c1a868b
--- /dev/null
+++ b/requisitions_endpoints_test.go
@@ -0,0 +1,255 @@
+package nordigen_test
+
+import (
+ "context"
+ "math/rand"
+ "strconv"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/weportfolio/go-nordigen"
+)
+
+func TestClient_CreateRequisition(t *testing.T) {
+ t.Parallel()
+
+ t.Run("create new requisition", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, agreement)
+ assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID)
+
+ requisitionRequestBody := &nordigen.RequisitionRequestBody{
+ Redirect: "https://example.com",
+ InstitutionID: nordigen.TestInstitutionID,
+ Agreement: agreement.ID,
+ Reference: strconv.Itoa(rand.Intn(1000000000000000000)),
+ UserLanguage: nordigen.LangEN,
+ AccountSelection: false,
+ RedirectImmediate: false,
+ }
+
+ requisition, err := client.CreateRequisition(context.Background(), token.Access, requisitionRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, requisition)
+ assert.Equal(t, nordigen.TestInstitutionID, requisition.InstitutionID)
+ })
+
+ t.Run("create new requisition with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ requisitionRequestBody := &nordigen.RequisitionRequestBody{
+ Redirect: "https://example.com",
+ InstitutionID: nordigen.TestInstitutionID,
+ Agreement: "invalid",
+ Reference: "12345",
+ UserLanguage: nordigen.LangEN,
+ AccountSelection: false,
+ RedirectImmediate: false,
+ }
+
+ requisition, err := client.CreateRequisition(context.Background(), "invalid", requisitionRequestBody)
+ assert.Error(t, err)
+ assert.Nil(t, requisition)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
+
+func TestClient_ListRequisitions(t *testing.T) {
+ t.Parallel()
+
+ t.Run("list requisitions", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, agreement)
+ assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID)
+
+ requisitionRequestBody := &nordigen.RequisitionRequestBody{
+ Redirect: "https://example.com",
+ InstitutionID: nordigen.TestInstitutionID,
+ Agreement: agreement.ID,
+ Reference: strconv.Itoa(rand.Intn(1000000000000000000)),
+ UserLanguage: nordigen.LangEN,
+ AccountSelection: false,
+ RedirectImmediate: false,
+ }
+
+ requisition, err := client.CreateRequisition(context.Background(), token.Access, requisitionRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, requisition)
+ assert.Equal(t, nordigen.TestInstitutionID, requisition.InstitutionID)
+
+ responseRequisitions, err := client.ListRequisitions(context.Background(), token.Access, nil)
+ assert.NoError(t, err)
+ assert.NotNil(t, responseRequisitions)
+ })
+
+ t.Run("list requisitions with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ responseRequisitions, err := client.ListRequisitions(context.Background(), "invalid", nil)
+ assert.Error(t, err)
+ assert.Nil(t, responseRequisitions)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
+
+func TestClient_FetchRequisition(t *testing.T) {
+ t.Parallel()
+
+ t.Run("fetch requisition", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, agreement)
+ assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID)
+
+ requisitionRequestBody := &nordigen.RequisitionRequestBody{
+ Redirect: "https://example.com",
+ InstitutionID: nordigen.TestInstitutionID,
+ Agreement: agreement.ID,
+ Reference: strconv.Itoa(rand.Intn(1000000000000000000)),
+ UserLanguage: nordigen.LangEN,
+ AccountSelection: false,
+ RedirectImmediate: false,
+ }
+
+ requisition, err := client.CreateRequisition(context.Background(), token.Access, requisitionRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, requisition)
+ assert.Equal(t, nordigen.TestInstitutionID, requisition.InstitutionID)
+
+ responseRequisition, err := client.FetchRequisition(context.Background(), token.Access, requisition.ID)
+ assert.NoError(t, err)
+ assert.NotNil(t, responseRequisition)
+ })
+
+ t.Run("fetch requisition with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ responseRequisition, err := client.FetchRequisition(context.Background(), "invalid", "invalid")
+ assert.Error(t, err)
+ assert.Nil(t, responseRequisition)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
+
+func TestClient_DeleteRequisition(t *testing.T) {
+ t.Parallel()
+
+ t.Run("delete requisition", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ token, err := client.NewToken(context.Background())
+ assert.NoError(t, err)
+ assert.NotNil(t, token)
+
+ agreementRequestBody := nordigen.AgreementRequestBody{
+ InstitutionID: nordigen.TestInstitutionID,
+ MaxHistoricalDays: "180",
+ AccessValidForDays: "2",
+ AccessScope: []string{"balances", "details", "transactions"},
+ }
+
+ agreement, err := client.CreateAgreement(context.Background(), token.Access, agreementRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, agreement)
+ assert.Equal(t, nordigen.TestInstitutionID, agreement.InstitutionID)
+
+ requisitionRequestBody := &nordigen.RequisitionRequestBody{
+ Redirect: "https://example.com",
+ InstitutionID: nordigen.TestInstitutionID,
+ Agreement: agreement.ID,
+ Reference: strconv.Itoa(rand.Intn(1000000000000000000)),
+ UserLanguage: nordigen.LangEN,
+ AccountSelection: false,
+ RedirectImmediate: false,
+ }
+
+ requisition, err := client.CreateRequisition(context.Background(), token.Access, requisitionRequestBody)
+ assert.NoError(t, err)
+ assert.NotNil(t, requisition)
+ assert.Equal(t, nordigen.TestInstitutionID, requisition.InstitutionID)
+
+ err = client.DeleteRequisition(context.Background(), token.Access, requisition.ID)
+ assert.NoError(t, err)
+ })
+
+ t.Run("delete requisition with invalid token", func(t *testing.T) {
+ t.Parallel()
+
+ client := getTestClient(t)
+ assert.NotNil(t, client)
+
+ err := client.DeleteRequisition(context.Background(), "invalid", "invalid")
+ assert.Error(t, err)
+
+ checkErr := nordigen.ExtractError(err)
+ assert.Equal(t, 401, checkErr.StatusCode)
+ })
+}
diff --git a/endpoints/token/token.go b/token.go
similarity index 92%
rename from endpoints/token/token.go
rename to token.go
index dde174b..bd84858 100644
--- a/endpoints/token/token.go
+++ b/token.go
@@ -1,4 +1,4 @@
-package token
+package nordigen
type Token struct {
Access string `json:"access"`
diff --git a/token_endpoints.go b/token_endpoints.go
new file mode 100644
index 0000000..34d8184
--- /dev/null
+++ b/token_endpoints.go
@@ -0,0 +1,36 @@
+package nordigen
+
+import (
+ "context"
+)
+
+// NewToken creates a new token
+func (c Client) NewToken(ctx context.Context) (*Token, error) {
+ var token Token
+ accessCreds := map[string]string{
+ "secret_id": c.SecretID,
+ "secret_key": c.SecretKey,
+ }
+
+ err := c.HTTP.Post(ctx, TokenNewPath, RequestHeaders(), accessCreds, &token)
+ if err != nil {
+ return nil, err
+ }
+
+ return &token, nil
+}
+
+// RefreshToken refreshes a token
+func (c Client) RefreshToken(ctx context.Context, refreshToken string) (*Token, error) {
+ var token Token
+ refreshCreds := map[string]string{
+ "refresh": refreshToken,
+ }
+
+ err := c.HTTP.Post(ctx, TokenRefreshPath, RequestHeaders(), refreshCreds, &token)
+ if err != nil {
+ return nil, err
+ }
+
+ return &token, nil
+}
diff --git a/endpoints/token/token_endpoints_test.go b/token_endpoints_test.go
similarity index 57%
rename from endpoints/token/token_endpoints_test.go
rename to token_endpoints_test.go
index 807ba92..89d1fbd 100644
--- a/endpoints/token/token_endpoints_test.go
+++ b/token_endpoints_test.go
@@ -1,28 +1,24 @@
-package token_test
+package nordigen_test
import (
"context"
"net/http"
"testing"
- "github.com/weportfolio/go-nordigen/consts"
-
"github.com/stretchr/testify/assert"
"github.com/weportfolio/go-nordigen"
)
-func TestClient_New(t *testing.T) {
+func TestClient_NewToken(t *testing.T) {
t.Parallel()
t.Run("create a new client token", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
})
@@ -30,17 +26,14 @@ func TestClient_New(t *testing.T) {
t.Run("create a new client token with invalid secret id", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- "invalid",
- "invalid",
- )
- assert.NotNil(t, client)
+ invalidClient := getInvalidTestClient(t)
+ assert.NotNil(t, invalidClient)
- token, err := client.Token().New(context.Background())
+ token, err := invalidClient.NewToken(context.Background())
assert.Error(t, err)
assert.Nil(t, token)
- checkErr := consts.ExtractError(err)
+ checkErr := nordigen.ExtractError(err)
assert.Equal(t, http.StatusUnauthorized, checkErr.StatusCode)
})
}
@@ -51,16 +44,14 @@ func TestClient_Refresh(t *testing.T) {
t.Run("refresh a client token", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- token, err := client.Token().New(context.Background())
+ token, err := client.NewToken(context.Background())
assert.NoError(t, err)
assert.NotNil(t, token)
- refreshedToken, err := client.Token().Refresh(context.Background(), token.Refresh)
+ refreshedToken, err := client.RefreshToken(context.Background(), token.Refresh)
assert.NoError(t, err)
assert.NotNil(t, refreshedToken)
})
@@ -68,12 +59,10 @@ func TestClient_Refresh(t *testing.T) {
t.Run("refresh a client token with invalid refresh token", func(t *testing.T) {
t.Parallel()
- client := nordigen.New(
- consts.GetSecrets(t),
- )
+ client := getTestClient(t)
assert.NotNil(t, client)
- refreshedToken, err := client.Token().Refresh(context.Background(), "invalid")
+ refreshedToken, err := client.RefreshToken(context.Background(), "invalid")
assert.Error(t, err)
assert.Nil(t, refreshedToken)
})