update
This commit is contained in:
@@ -162,8 +162,6 @@ tap.test('should create and download PDF statement', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should create CSV statement with custom date range', async () => {
|
tap.test('should create CSV statement with custom date range', async () => {
|
||||||
// Wait to avoid rate limiting
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 3500));
|
|
||||||
console.log('Creating CSV statement export...');
|
console.log('Creating CSV statement export...');
|
||||||
|
|
||||||
// Use last month's date range to ensure it's in the past
|
// Use last month's date range to ensure it's in the past
|
||||||
@@ -200,8 +198,6 @@ tap.test('should create CSV statement with custom date range', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should create MT940 statement', async () => {
|
tap.test('should create MT940 statement', async () => {
|
||||||
// Wait to avoid rate limiting
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 3500));
|
|
||||||
console.log('Creating MT940 statement export...');
|
console.log('Creating MT940 statement export...');
|
||||||
|
|
||||||
const exportBuilder = primaryAccount.getAccountStatement({
|
const exportBuilder = primaryAccount.getAccountStatement({
|
||||||
|
@@ -24,9 +24,46 @@ export class BunqHttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make an API request to bunq
|
* Make an API request to bunq with automatic retry on rate limit
|
||||||
*/
|
*/
|
||||||
public async request<T = any>(options: IBunqRequestOptions): Promise<T> {
|
public async request<T = any>(options: IBunqRequestOptions): Promise<T> {
|
||||||
|
const maxRetries = 3;
|
||||||
|
let lastError: Error;
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
|
return await this.makeRequest<T>(options);
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error as Error;
|
||||||
|
|
||||||
|
// Check if it's a rate limit error
|
||||||
|
if (error instanceof BunqApiError) {
|
||||||
|
const isRateLimitError = error.errors.some(e =>
|
||||||
|
e.error_description.includes('Too many requests') ||
|
||||||
|
e.error_description.includes('rate limit')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isRateLimitError && attempt < maxRetries) {
|
||||||
|
// Exponential backoff: 1s, 2s, 4s
|
||||||
|
const backoffMs = Math.pow(2, attempt) * 1000;
|
||||||
|
console.log(`Rate limit hit, backing off for ${backoffMs}ms (attempt ${attempt + 1}/${maxRetries + 1})`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, backoffMs));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For non-rate-limit errors or if we've exhausted retries, throw immediately
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lastError!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method to make the actual request
|
||||||
|
*/
|
||||||
|
private async makeRequest<T = any>(options: IBunqRequestOptions): Promise<T> {
|
||||||
let url = `${this.context.baseUrl}${options.endpoint}`;
|
let url = `${this.context.baseUrl}${options.endpoint}`;
|
||||||
|
|
||||||
// Prepare headers
|
// Prepare headers
|
||||||
|
Reference in New Issue
Block a user