feat(#251): add optional bapi rate limit parsing to REST clients
This commit is contained in:
@@ -52,8 +52,8 @@ export interface GetOrderbookParamsV5 {
|
|||||||
limit?: number;
|
limit?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetTickersParamsV5 {
|
export interface GetTickersParamsV5<TCategory = CategoryV5> {
|
||||||
category: CategoryV5;
|
category: TCategory;
|
||||||
symbol?: string;
|
symbol?: string;
|
||||||
baseCoin?: string;
|
baseCoin?: string;
|
||||||
expDate?: string;
|
expDate?: string;
|
||||||
|
|||||||
@@ -61,18 +61,33 @@ export interface APIResponse<T> {
|
|||||||
result: 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> {
|
export interface APIResponseV3<T> {
|
||||||
retCode: number;
|
retCode: number;
|
||||||
retMsg: 'OK' | string;
|
retMsg: 'OK' | string;
|
||||||
result: T;
|
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> {
|
export type APIResponseV3WithTime<T> = APIResponseV3<T> & { time: number };
|
||||||
retCode: number;
|
|
||||||
retMsg: 'OK' | string;
|
|
||||||
result: T;
|
|
||||||
time: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface APIResponseWithTime<T = {}> extends APIResponse<T> {
|
export interface APIResponseWithTime<T = {}> extends APIResponse<T> {
|
||||||
/** UTC timestamp */
|
/** UTC timestamp */
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
RestClientOptions,
|
RestClientOptions,
|
||||||
RestClientType,
|
RestClientType,
|
||||||
getRestBaseUrl,
|
getRestBaseUrl,
|
||||||
|
parseRateLimitHeaders,
|
||||||
serializeParams,
|
serializeParams,
|
||||||
} from './requestUtils';
|
} from './requestUtils';
|
||||||
import { signMessage } from './node-support';
|
import { signMessage } from './node-support';
|
||||||
@@ -323,7 +324,17 @@ export default abstract class BaseRestClient {
|
|||||||
return axios(options)
|
return axios(options)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status == 200) {
|
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;
|
throw response;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { APIRateLimit } from '../types';
|
||||||
import { WebsocketSucceededTopicSubscriptionConfirmationEvent } from '../types/ws-events/succeeded-topic-subscription-confirmation';
|
import { WebsocketSucceededTopicSubscriptionConfirmationEvent } from '../types/ws-events/succeeded-topic-subscription-confirmation';
|
||||||
import { WebsocketTopicSubscriptionConfirmationEvent } from '../types/ws-events/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. */
|
/** Default: true. whether to try and post-process request exceptions. */
|
||||||
parse_exceptions?: boolean;
|
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 =
|
export type RestClientType =
|
||||||
(typeof REST_CLIENT_TYPE_ENUM)[keyof typeof REST_CLIENT_TYPE_ENUM];
|
(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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user