Skip to content

Commit

Permalink
Merge pull request #20 from RobinTTY/v10.1.0
Browse files Browse the repository at this point in the history
v10.1.0
  • Loading branch information
RobinTTY authored Aug 19, 2024
2 parents 8c4d580 + 5b7a16e commit 2d3bb6a
Show file tree
Hide file tree
Showing 21 changed files with 266 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<img src="docs/static/img/nordigen-api-client-logo.png" width="30%">
<img src="https://raw.githubusercontent.com/RobinTTY/NordigenApiClient/main/docs/static/img/nordigen-api-client-logo.png" width="30%">
</p>

# NordigenApiClient
Expand Down
64 changes: 64 additions & 0 deletions docs/docs/api-reference/responses/api-rate-limits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: ApiRateLimits
---

The `ApiRateLimits` class represents the rate limits of the GoCardless API.

## Properties

### `RequestLimit` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)

Indicates the maximum number of allowed requests within the defined time window. Usually populated in every response.

### `RemainingRequests` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)

Indicates the number of remaining requests you can make in the current time window. Usually populated in every response.

### `RemainingSecondsInTimeWindow` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)

Indicates the time remaining in the current time window (in seconds). Usually populated in every response.

### `RequestLimitAccountsEndpoint` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)

Indicates the maximum number of allowed requests to the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint) within the defined time window. Only populated in responses from the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint).

### `RemainingRequestsAccountsEndpoint` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)

Indicates the number of remaining requests to the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint) you can make in the current time window. Only populated in responses from the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint).

### `RemainingSecondsInTimeWindowAccountsEndpoint` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)

Indicates the time remaining in the current time window (in seconds) for requests to the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint). Only populated in responses from the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint).

## Constructor

```csharp
public ApiRateLimits(int requestLimit, int remainingRequests, int remainingTimeInTimeWindow,
int maxAccountRequests, int remainingAccountRequests, int remainingTimeInAccountTimeWindow)
```

### Parameters

#### `requestLimit` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)?

Indicates the maximum number of allowed requests within the defined time window. Usually populated in every response.

#### `remainingRequests` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)?

Indicates the number of remaining requests you can make in the current time window. Usually populated in every response.

#### `remainingSecondsInTimeWindow` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)?

Indicates the time remaining in the current time window (in seconds). Usually populated in every response.

#### `requestLimitAccountsEndpoint` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)?

Indicates the maximum number of allowed requests to the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint) within the defined time window. Only populated in responses from the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint).

#### `remainingRequestsAccountsEndpoint` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)?

Indicates the number of remaining requests to the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint) you can make in the current time window. Only populated in responses from the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint).

#### `remainingSecondsInTimeWindowAccountsEndpoint` - [int](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-int32)?

Indicates the time remaining in the current time window (in seconds) for requests to the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint). Only populated in responses from the [AccountsEndpoint](/docs/api-reference/endpoints/accounts-endpoint).
11 changes: 10 additions & 1 deletion docs/docs/api-reference/responses/nordigen-api-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ The result returned by the API. Null if the the HTTP response was not successful

The error returned by the API. Null if the HTTP response was successful.

### `RateLimits` - [ApiRateLimits](/docs/api-reference/responses/api-rate-limits)

The rate limits of the GoCardless API.

## Constructor

```csharp
public NordigenApiResponse(HttpStatusCode statusCode, bool isSuccess, TResult? result, TError? apiError)
public NordigenApiResponse(HttpStatusCode statusCode, bool isSuccess,
TResult? result, TError? apiError, ApiRateLimits rateLimits)
```

### Parameters
Expand All @@ -46,6 +51,10 @@ The result returned by the API. Null if the the HTTP response was not successful

The error returned by the API. Null if the HTTP response was successful.

#### `rateLimits` - [ApiRateLimits](/docs/api-reference/responses/api-rate-limits)

The rate limits of the GoCardless API.

## Handling the `NordigenApiResponse` type

To learn more about how to best work with the `NordigenApiResponse` type see the [Handling API responses](/docs/handling-api-responses) guide.
31 changes: 31 additions & 0 deletions docs/docs/handling-rate-limits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: Handling Rate Limits
---

The GoCardless API has [rate limits](https://bankaccountdata.zendesk.com/hc/en-gb/articles/11529637772188-Bank-Account-Data-API-Rate-Limits) depending on the endpoint you are using. With most endpoints you shouldn't run into problems since these limits are usually high enough for most uses.

The [Accounts endpoint](/docs/api-reference/endpoints/accounts-endpoint) has very tight limitations though. It currently has a limit of 10 requests a day per access scope. That means 10 requests to each of the [details](/docs/api-reference/endpoints/accounts-endpoint#getaccountdetails), [balances](/docs/api-reference/endpoints/accounts-endpoint#getbalances) and [transactions](/docs/api-reference/endpoints/accounts-endpoint#gettransactions) endpoints. In the future this limit will be lowered to 4 per day (no date on this as of now).

Unsuccessful requests to the API will count towards the general rate limits but not the more stringent limits of the [Accounts endpoint](/docs/api-reference/endpoints/accounts-endpoint). Only successful requests will count towards those.

## Checking API rate limits

You can check the rate limits as well as remaining requests with each request you make to the API. The property [`NordigenApiResponse.RateLimits`](/docs/api-reference/responses/nordigen-api-response#ratelimits---apiratelimits) will hold the returned information about the current API limits.

You can check the limits as follows:

```csharp
// Execute the request you want to make:
var bankAccountDetailsResponse = await client.AccountsEndpoint.GetAccountDetails(accountId);

Console.WriteLine(bankAccountDetailsResponse.RateLimits.RequestLimit);
Console.WriteLine(bankAccountDetailsResponse.RateLimits.RemainingRequests);
Console.WriteLine(bankAccountDetailsResponse.RateLimits.RemainingSecondsInTimeWindow);
Console.WriteLine(bankAccountDetailsResponse.RateLimits.RequestLimitAccountsEndpoint);
Console.WriteLine(bankAccountDetailsResponse.RateLimits.RemainingRequestsAccountsEndpoint);
Console.WriteLine(bankAccountDetailsResponse.RateLimits.RemainingSecondsInTimeWindowAccountsEndpoint);
```

It's important to handle these limits so your applications user experience isn't degraded. Refreshing data from bank accounts should be handled consciously, so you don't use up all your requests in a short time frame and aren't able to update for the rest of the day.

If you are a company with a contract with GoCardless you can probably get in contact with their team about increasing these limits.
4 changes: 2 additions & 2 deletions docs/release-notes/v10_0_0.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public async Task<NordigenApiResponse<List<Institution>, BasicResponse>> GetInst

The `TokenPairUpdated` event is now raised whenever the `JsonWebTokenPair` property is successfully updated. Not only when it was automatically updated but also when done so by the user.

**Full Changelog**: [v9.0.0...v10.0.0](https://github.com/RobinTTY/NordigenApiClient/compare/v9.0.0...v10.0.0)

## Other improvements

A full documentation of the library is now available at: [https://robintty.github.io/NordigenApiClient/](https://robintty.github.io/NordigenApiClient/)

**Full Changelog**: [v9.0.0...v10.0.0](https://github.com/RobinTTY/NordigenApiClient/compare/v9.0.0...v10.0.0)
11 changes: 11 additions & 0 deletions docs/release-notes/v10_1_0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
slug: v10.1.0
title: v10.1.0
date: 2024-08-19T00:00
---

Added the ability to check newly introduced rate limits in the GoCardless API.

Please refer to the [documentation](/docs/handling-rate-limits) for more information. You can also see the [announcement from GoCardless](https://bankaccountdata.zendesk.com/hc/en-gb/articles/11529584398236-Bank-API-Rate-Limits-and-Rate-Limit-Headers).

**Full Changelog**: [v10.0.0...v10.1.0](https://github.com/RobinTTY/NordigenApiClient/compare/v10.0.0...v10.1.0)
2 changes: 2 additions & 0 deletions docs/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const sidebars: SidebarsConfig = {
"quickstart-guide",
"handling-api-responses",
"handling-authentication-tokens",
"handling-rate-limits",
"using-a-different-base-address",
"usage-with-older-dotnet-versions",
"testing",
Expand Down Expand Up @@ -38,6 +39,7 @@ const sidebars: SidebarsConfig = {
"api-reference/responses/address",
"api-reference/responses/agreement",
"api-reference/responses/amount-currency-pair",
"api-reference/responses/api-rate-limits",
"api-reference/responses/balance",
"api-reference/responses/balance-type",
"api-reference/responses/bank-account",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions src/RobinTTY.NordigenApiClient/Endpoints/AccountsEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ private async Task<NordigenApiResponse<List<Balance>, AccountsError>> GetBalance
{
var response = await _nordigenClient.MakeRequest<BalanceJsonWrapper, AccountsError>(
$"{NordigenEndpointUrls.AccountsEndpoint}{accountId}/balances/", HttpMethod.Get, cancellationToken);

return new NordigenApiResponse<List<Balance>, AccountsError>(response.StatusCode, response.IsSuccess,
response.Result?.Balances, response.Error);
response.Result?.Balances, response.Error, response.RateLimits);
}

/// <inheritdoc />
Expand All @@ -65,8 +66,9 @@ private async Task<NordigenApiResponse<BankAccountDetails, AccountsError>> GetAc
{
var response = await _nordigenClient.MakeRequest<BankAccountDetailsWrapper, AccountsError>(
$"{NordigenEndpointUrls.AccountsEndpoint}{id}/details/", HttpMethod.Get, cancellationToken);

return new NordigenApiResponse<BankAccountDetails, AccountsError>(response.StatusCode, response.IsSuccess,
response.Result?.Account, response.Error);
response.Result?.Account, response.Error, response.RateLimits);
}

/// <inheritdoc />
Expand Down Expand Up @@ -111,8 +113,9 @@ private async Task<NordigenApiResponse<AccountTransactions, AccountsError>> GetT

var response = await _nordigenClient.MakeRequest<AccountTransactionsWrapper, AccountsError>(
$"{NordigenEndpointUrls.AccountsEndpoint}{id}/transactions/", HttpMethod.Get, cancellationToken, query);

return new NordigenApiResponse<AccountTransactions, AccountsError>(response.StatusCode, response.IsSuccess,
response.Result?.Transactions, response.Error);
response.Result?.Transactions, response.Error, response.RateLimits);
}

#if NET6_0_OR_GREATER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ public async Task<NordigenApiResponse<List<Institution>, BasicResponse>> GetInst
NordigenEndpointUrls.InstitutionsEndpoint, HttpMethod.Get, cancellationToken, query);

return new NordigenApiResponse<List<Institution>, BasicResponse>(response.StatusCode, response.IsSuccess,
response.Result,
response.Error);
response.Result, response.Error, response.RateLimits);
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Text.Json.Serialization;
using RobinTTY.NordigenApiClient.JsonConverters;
using RobinTTY.NordigenApiClient.Models.Requests;
using RobinTTY.NordigenApiClient.Models.Responses;

namespace RobinTTY.NordigenApiClient.Models.Errors;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Text.Json.Serialization;
using RobinTTY.NordigenApiClient.JsonConverters;
using RobinTTY.NordigenApiClient.Models.Requests;
using RobinTTY.NordigenApiClient.Models.Responses;

namespace RobinTTY.NordigenApiClient.Models.Errors;
Expand Down
87 changes: 87 additions & 0 deletions src/RobinTTY.NordigenApiClient/Models/Responses/ApiRateLimits.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Net.Http.Headers;
using RobinTTY.NordigenApiClient.Endpoints;

namespace RobinTTY.NordigenApiClient.Models.Responses;

/// <summary>
/// The rate limits of the GoCardless API.
/// </summary>
public class ApiRateLimits
{
/// <summary>
/// Indicates the maximum number of allowed requests within the defined time window.
/// Usually populated in every response.
/// </summary>
public int? RequestLimit { get; set; }
/// <summary>
/// Indicates the number of remaining requests you can make in the current time window.
/// Usually populated in every response.
/// </summary>
public int? RemainingRequests { get; set; }
/// <summary>
/// Indicates the time remaining in the current time window (in seconds).
/// Usually populated in every response.
/// </summary>
public int? RemainingSecondsInTimeWindow { get; set; }
/// <summary>
/// Indicates the maximum number of allowed requests to the <see cref="AccountsEndpoint"/>
/// within the defined time window. Only populated in responses from the <see cref="AccountsEndpoint"/>.
/// </summary>
public int? RequestLimitAccountsEndpoint { get; set; }
/// <summary>
/// Indicates the number of remaining requests to the <see cref="AccountsEndpoint"/>
/// you can make in the current time window. Only populated in responses from the <see cref="AccountsEndpoint"/>.
/// </summary>
public int? RemainingRequestsAccountsEndpoint { get; set; }
/// <summary>
/// Indicates the time remaining in the current time window (in seconds) for requests
/// to the <see cref="AccountsEndpoint"/>. Only populated in responses from the <see cref="AccountsEndpoint"/>.
/// </summary>
public int? RemainingSecondsInTimeWindowAccountsEndpoint { get; set; }

/// <summary>
/// Creates a new instance of <see cref="ApiRateLimits" />.
/// </summary>
/// <param name="requestLimit">Indicates the maximum number of allowed requests within the defined time window.</param>
/// <param name="remainingRequests">Indicates the number of remaining requests you can make in the current time window.</param>
/// <param name="remainingSecondsInTimeWindow">Indicates the time remaining in the current time window (in seconds).</param>
/// <param name="requestLimitAccountsEndpoint">Indicates the maximum number of allowed requests to the <see cref="AccountsEndpoint"/>
/// within the defined time window.</param>
/// <param name="remainingRequestsAccountsEndpoint">Indicates the number of remaining requests to the <see cref="AccountsEndpoint"/>
/// you can make in the current time window.</param>
/// <param name="remainingSecondsInTimeWindowAccountsEndpoint">Indicates the time remaining in the current time window (in seconds) for requests
/// to the <see cref="AccountsEndpoint"/>.</param>
public ApiRateLimits(int? requestLimit, int? remainingRequests, int? remainingSecondsInTimeWindow, int? requestLimitAccountsEndpoint,
int? remainingRequestsAccountsEndpoint, int? remainingSecondsInTimeWindowAccountsEndpoint)
{
RequestLimit = requestLimit;
RemainingRequests = remainingRequests;
RemainingSecondsInTimeWindow = remainingSecondsInTimeWindow;
RequestLimitAccountsEndpoint = requestLimitAccountsEndpoint;
RemainingRequestsAccountsEndpoint = remainingRequestsAccountsEndpoint;
RemainingSecondsInTimeWindowAccountsEndpoint = remainingSecondsInTimeWindowAccountsEndpoint;
}

/// <summary>
/// Creates a new instance of <see cref="ApiRateLimits" />.
/// </summary>
/// <param name="headers">The headers of the HTTP response containing the rate limit information.</param>
public ApiRateLimits(HttpHeaders headers)
{
RequestLimit = TryParseApiLimit(headers, "HTTP_X_RATELIMIT_LIMIT");
RemainingRequests = TryParseApiLimit(headers, "HTTP_X_RATELIMIT_REMAINING");
RemainingSecondsInTimeWindow = TryParseApiLimit(headers, "HTTP_X_RATELIMIT_RESET");
RequestLimitAccountsEndpoint = TryParseApiLimit(headers, "HTTP_X_RATELIMIT_ACCOUNT_SUCCESS_LIMIT");
RemainingRequestsAccountsEndpoint = TryParseApiLimit(headers, "HTTP_X_RATELIMIT_ACCOUNT_SUCCESS_REMAINING");
RemainingSecondsInTimeWindowAccountsEndpoint = TryParseApiLimit(headers, "HTTP_X_RATELIMIT_ACCOUNT_SUCCESS_RESET");
}

private static int? TryParseApiLimit(HttpHeaders headers, string headerName)
{
headers.TryGetValues(headerName, out var values);
var firstHeaderValue = values?.FirstOrDefault();
var parseSuccess = int.TryParse(firstHeaderValue, out var limitValue);

return parseSuccess ? limitValue : null;
}
}
Loading

0 comments on commit 2d3bb6a

Please sign in to comment.