feat(#251): add optional bapi rate limit parsing to REST clients

This commit is contained in:
tiagosiebler
2023-10-30 13:47:40 +00:00
parent eb33084b7f
commit a790fcaf04
4 changed files with 93 additions and 9 deletions

View File

@@ -52,8 +52,8 @@ export interface GetOrderbookParamsV5 {
limit?: number;
}
export interface GetTickersParamsV5 {
category: CategoryV5;
export interface GetTickersParamsV5<TCategory = CategoryV5> {
category: TCategory;
symbol?: string;
baseCoin?: string;
expDate?: string;

View File

@@ -61,18 +61,33 @@ export interface APIResponse<T> {
result: T;
}
export interface APIRateLimit {
/** Remaining requests to this endpoint before the next reset */
remainingRequests: number;
/** Max requests for this endpoint per rollowing window (before next reset) */
maxRequests: number;
/**
* Timestamp when the rate limit resets if you have exceeded your current maxRequests.
* Otherwise, this is approximately your current timestamp.
*/
resetAtTimestamp: number;
}
export interface APIResponseV3<T> {
retCode: number;
retMsg: 'OK' | string;
result: T;
/**
* These are per-UID per-endpoint rate limits, automatically parsed from response headers if available.
*
* Note:
* - this is primarily for V5 (or newer) APIs.
* - these rate limits are per-endpoint per-account, so will not appear for public API calls
*/
rateLimitApi?: APIRateLimit;
}
export interface APIResponseV3WithTime<T> {
retCode: number;
retMsg: 'OK' | string;
result: T;
time: number;
}
export type APIResponseV3WithTime<T> = APIResponseV3<T> & { time: number };
export interface APIResponseWithTime<T = {}> extends APIResponse<T> {
/** UTC timestamp */

View File

@@ -7,6 +7,7 @@ import {
RestClientOptions,
RestClientType,
getRestBaseUrl,
parseRateLimitHeaders,
serializeParams,
} from './requestUtils';
import { signMessage } from './node-support';
@@ -323,7 +324,17 @@ export default abstract class BaseRestClient {
return axios(options)
.then((response) => {
if (response.status == 200) {
return response.data;
const perAPIRateLimits = this.options.parseAPIRateLimits
? parseRateLimitHeaders(
response.headers,
this.options.throwOnFailedRateLimitParse === true,
)
: undefined;
return {
rateLimitApi: perAPIRateLimits,
...response.data,
};
}
throw response;

View File

@@ -1,3 +1,4 @@
import { APIRateLimit } from '../types';
import { WebsocketSucceededTopicSubscriptionConfirmationEvent } from '../types/ws-events/succeeded-topic-subscription-confirmation';
import { WebsocketTopicSubscriptionConfirmationEvent } from '../types/ws-events/topic-subscription-confirmation';
@@ -46,6 +47,12 @@ export interface RestClientOptions {
/** Default: true. whether to try and post-process request exceptions. */
parse_exceptions?: boolean;
/** Default: false. Enable to parse/include per-API/endpoint rate limits in responses. */
parseAPIRateLimits?: boolean;
/** Default: false. Enable to throw error if rate limit parser fails */
throwOnFailedRateLimitParse?: boolean;
}
/**
@@ -169,3 +176,54 @@ export const REST_CLIENT_TYPE_ENUM = {
export type RestClientType =
(typeof REST_CLIENT_TYPE_ENUM)[keyof typeof REST_CLIENT_TYPE_ENUM];
/** Parse V5 rate limit response headers, if enabled */
export function parseRateLimitHeaders(
headers: Record<string, string | undefined> = {},
throwOnFailedRateLimitParse: boolean,
): APIRateLimit | undefined {
try {
const remaining = headers['x-bapi-limit-status'];
const max = headers['x-bapi-limit'];
const resetAt = headers['x-bapi-limit-reset-timestamp'];
if (
typeof remaining === 'undefined' ||
typeof max === 'undefined' ||
typeof resetAt === 'undefined'
) {
return;
}
const result: APIRateLimit = {
remainingRequests: Number(remaining),
maxRequests: Number(max),
resetAtTimestamp: Number(resetAt),
};
if (
isNaN(result.remainingRequests) ||
isNaN(result.maxRequests) ||
isNaN(result.resetAtTimestamp)
) {
return;
}
return result;
} catch (e) {
if (throwOnFailedRateLimitParse) {
console.log(
new Date(),
'parseRateLimitHeaders()',
'Failed to parse rate limit headers',
{
headers,
exception: e,
},
);
throw e;
}
}
return undefined;
}