@jupiterone/integration-sdk-http-client 12.1.0 → 12.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/client.d.ts +189 -0
- package/dist/src/client.js +370 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/errors.d.ts +28 -9
- package/dist/src/errors.js +86 -12
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +3 -14
- package/dist/src/index.js +16 -74
- package/dist/src/index.js.map +1 -1
- package/dist/src/types.d.ts +45 -131
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +9 -5
- package/dist/src/util.d.ts +0 -8
- package/dist/src/util.js +0 -25
- package/dist/src/util.js.map +0 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Response } from 'node-fetch';
|
|
2
|
+
import { IntegrationLogger } from '@jupiterone/integration-sdk-core';
|
|
3
|
+
import { AttemptContext } from '@lifeomic/attempt';
|
|
4
|
+
import { ClientConfig, OptionalPromise, RetryOptions, RequestOptions, RateLimitThrottlingOptions, IterateCallbackResult, TokenBucketOptions, NextPageData } from './types';
|
|
5
|
+
import { HierarchicalTokenBucket } from '@jupiterone/hierarchical-token-bucket';
|
|
6
|
+
export declare const defaultErrorHandler: (err: any, context: AttemptContext, logger: IntegrationLogger) => Promise<void>;
|
|
7
|
+
export declare abstract class BaseAPIClient {
|
|
8
|
+
protected baseUrl: string;
|
|
9
|
+
protected logger: IntegrationLogger;
|
|
10
|
+
protected retryOptions: RetryOptions;
|
|
11
|
+
protected logErrorBody: boolean;
|
|
12
|
+
protected rateLimitThrottling: RateLimitThrottlingOptions | undefined;
|
|
13
|
+
protected baseTokenBucket?: HierarchicalTokenBucket;
|
|
14
|
+
protected tokenBucketInitialConfig?: TokenBucketOptions | undefined;
|
|
15
|
+
protected endpointTokenBuckets: Record<string, HierarchicalTokenBucket>;
|
|
16
|
+
/**
|
|
17
|
+
* The authorization headers for the API requests
|
|
18
|
+
*/
|
|
19
|
+
protected authorizationHeaders: Record<string, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Create a new API client
|
|
22
|
+
*
|
|
23
|
+
* @param {ClientConfig} config - The configuration for the client
|
|
24
|
+
* @param {string} config.baseUrl - The base URL for the API
|
|
25
|
+
* @param {IntegrationLogger} config.logger - The logger to use
|
|
26
|
+
* @param {Partial<RetryOptions>} [config.retryOptions] - The retry options for the client,
|
|
27
|
+
* if not provided, the default retry options will be used
|
|
28
|
+
* @param {boolean} [config.logErrorBody] - Whether to log the error body,
|
|
29
|
+
* if true, the error body will be logged when a request fails
|
|
30
|
+
* @param {RateLimitThrottlingOptions} [config.rateLimitThrottling] - The rate limit throttling options,
|
|
31
|
+
* if not provided, rate limit throttling will not be enabled
|
|
32
|
+
* @param {number} [config.rateLimitThrottling.threshold] - The threshold at which to throttle requests
|
|
33
|
+
* @param {RateLimitHeaders} [config.rateLimitThrottling.rateLimitHeaders] - The headers to use for rate limiting
|
|
34
|
+
* @param {string} [config.rateLimitThrottling.rateLimitHeaders.limit='x-rate-limit-limit'] - The header for the rate limit limit
|
|
35
|
+
* @param {string} [config.rateLimitThrottling.rateLimitHeaders.remaining='x-rate-limit-remaining'] - The header for the rate limit remaining
|
|
36
|
+
* @param {string} [config.rateLimitThrottling.rateLimitHeaders.reset='x-rate-limit-reset'] - The header for the rate limit reset
|
|
37
|
+
* @param {TokenBucketOptions} [config.tokenBucket] - The token bucket options,
|
|
38
|
+
* if not provided, token bucket will not be enabled
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const client = new APIClient({
|
|
43
|
+
* baseUrl: 'https://api.example.com',
|
|
44
|
+
* logger: context.logger,
|
|
45
|
+
* rateLimitThrottling: {
|
|
46
|
+
* threshold: 0.3,
|
|
47
|
+
* rateLimitHeaders: {
|
|
48
|
+
* limit: 'x-ratelimit-limit',
|
|
49
|
+
* remaining: 'x-ratelimit-remaining',
|
|
50
|
+
* reset: 'x-ratelimit-reset',
|
|
51
|
+
* },
|
|
52
|
+
* },
|
|
53
|
+
* })
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
constructor(config: ClientConfig);
|
|
57
|
+
protected withBaseUrl(endpoint: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Get the authorization headers for the API requests.
|
|
60
|
+
*
|
|
61
|
+
* @return {Promise<Record<string, string>>} - The authorization headers
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* async getAuthorizationHeaders(): Record<string, string> {
|
|
66
|
+
* return {
|
|
67
|
+
* Authorization: `Bearer ${this.config.apiKey}`,
|
|
68
|
+
* };
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* protected async getAuthorizationHeaders(): Promise<Record<string, string>> {
|
|
74
|
+
* const response = await this.request('/token', {
|
|
75
|
+
* method: 'POST',
|
|
76
|
+
* body: {
|
|
77
|
+
* email: this.config.email,
|
|
78
|
+
* password: this.config.password,
|
|
79
|
+
* },
|
|
80
|
+
* authorize: false, // don't try to do authorization on this request, it will go into an infinite loop.
|
|
81
|
+
* });
|
|
82
|
+
* const data = await response.json();
|
|
83
|
+
* return {
|
|
84
|
+
* Authorization: `Bearer ${data.token}`,
|
|
85
|
+
* };
|
|
86
|
+
* }
|
|
87
|
+
*/
|
|
88
|
+
protected abstract getAuthorizationHeaders(): OptionalPromise<Record<string, string>>;
|
|
89
|
+
/**
|
|
90
|
+
* Perform a request to the API.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} endpoint - The endpoint to request
|
|
93
|
+
* @param {RequestOptions} options - The options for the request
|
|
94
|
+
* @param {string} [options.method=GET] - The HTTP method to use
|
|
95
|
+
* @param {Record<string, unknown>} [options.body] - The body of the request
|
|
96
|
+
* @param {Record<string, string>} [options.headers] - The headers for the request
|
|
97
|
+
* @param {boolean} [options.authorize=true] - Whether to include authorization headers
|
|
98
|
+
* @return {Promise<Response>} - The response from the API
|
|
99
|
+
*/
|
|
100
|
+
protected request(endpoint: string, options?: RequestOptions): Promise<Response>;
|
|
101
|
+
/**
|
|
102
|
+
* Perform a request to the API with retries and error handling.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} endpoint - The endpoint path to request
|
|
105
|
+
* @param {RequestOptions} options - The options for the request
|
|
106
|
+
* @param {string} [options.method=GET] - The HTTP method to use
|
|
107
|
+
* @param {Record<string, unknown>} [options.body] - The body of the request
|
|
108
|
+
* @param {Record<string, string>} [options.headers] - The headers for the request
|
|
109
|
+
* @param {boolean} [options.authorize=true] - Whether to include authorization headers
|
|
110
|
+
* @return {Promise<Response>} - The response from the API
|
|
111
|
+
*/
|
|
112
|
+
protected retryableRequest(endpoint: string, options?: RequestOptions): Promise<Response>;
|
|
113
|
+
/**
|
|
114
|
+
* Iteratively performs API requests based on the initial request and subsequent requests defined by a callback function.
|
|
115
|
+
* This method is designed to facilitate paginated API requests where each request's response determines the parameters of the next request.
|
|
116
|
+
*
|
|
117
|
+
* @param {object} initialRequest - The initial request parameters
|
|
118
|
+
* @param {string} initialRequest.endpoint - The endpoint path to request
|
|
119
|
+
* @param {RequestOptions} [initialRequest.options] - The options for the request
|
|
120
|
+
* @param {string|string[]|undefined} dataPath - The path to the data in the response to iterate over
|
|
121
|
+
* @param {function} nextPageCallback - The callback function to determine the parameters of the next request
|
|
122
|
+
*
|
|
123
|
+
* @return {AsyncGenerator<T, void, unknown>} - An async generator that yields the items from the paginated requests
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* async iterateUsers(iteratee: (user: User) => Promise<void>): Promise<void>{
|
|
128
|
+
* const baseEndpoint = '/users?limit=100';
|
|
129
|
+
* const iterator = this.paginate({
|
|
130
|
+
* endpoint: baseEndpoint,
|
|
131
|
+
* }, 'users', this.getNextPageCallback(baseEndpoint));
|
|
132
|
+
*
|
|
133
|
+
* for await (const user of iterator) {
|
|
134
|
+
* await iteratee(user);
|
|
135
|
+
* }
|
|
136
|
+
* };
|
|
137
|
+
*
|
|
138
|
+
* private getNextPageCallback(baseEndpoint: string) {
|
|
139
|
+
* return (data: NextPageData): IterateCallbackResult | undefined => {
|
|
140
|
+
* const { body } = data;
|
|
141
|
+
* const nextCursor = body.metadata?.cursor;
|
|
142
|
+
* if (!nextCursor) {
|
|
143
|
+
* return;
|
|
144
|
+
* }
|
|
145
|
+
* const nextUrl = `${baseEndpoint}&cursor=${nextCursor}`;
|
|
146
|
+
* return {
|
|
147
|
+
* nextUrl,
|
|
148
|
+
* };
|
|
149
|
+
* }
|
|
150
|
+
* };
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
protected paginate<T>(initialRequest: {
|
|
154
|
+
endpoint: string;
|
|
155
|
+
options?: RequestOptions;
|
|
156
|
+
}, dataPath: string | string[] | undefined, nextPageCallback: (data: NextPageData) => IterateCallbackResult | undefined): AsyncGenerator<T, void, unknown>;
|
|
157
|
+
/**
|
|
158
|
+
* Get the token bucket for the given endpoint
|
|
159
|
+
*
|
|
160
|
+
* @param {string} endpoint - The endpoint to get the token bucket for
|
|
161
|
+
* @param {number} [tokens] - The number of tokens to use for the token bucket
|
|
162
|
+
*/
|
|
163
|
+
private getTokenBucket;
|
|
164
|
+
/**
|
|
165
|
+
* Wait until the rate limit reset time before sending the next request
|
|
166
|
+
* if the rate limit threshold is exceeded.
|
|
167
|
+
*
|
|
168
|
+
* @param {function} fn - The function to rate limit
|
|
169
|
+
* @return {Promise<Response>}
|
|
170
|
+
*/
|
|
171
|
+
protected withRateLimiting(fn: () => Promise<Response>): Promise<Response>;
|
|
172
|
+
private parseRateLimitHeaders;
|
|
173
|
+
/**
|
|
174
|
+
* Determine if the next request should be throttled based on the rate limit headers
|
|
175
|
+
*
|
|
176
|
+
* @param {object} params - The parameters for the function
|
|
177
|
+
* @param {number|undefined} params.rateLimitLimit - The rate limit limit
|
|
178
|
+
* @param {number|undefined} params.rateLimitRemaining - The rate limit remaining
|
|
179
|
+
* @param {number} params.threshold - The threshold at which to throttle requests
|
|
180
|
+
* @return {boolean} - Whether the next request should be throttled
|
|
181
|
+
*/
|
|
182
|
+
private shouldThrottleNextRequest;
|
|
183
|
+
/**
|
|
184
|
+
* Determine wait time by getting the delta X-Rate-Limit-Reset and the Date header
|
|
185
|
+
* Add 1 second to account for sub second differences between the clocks that create these headers
|
|
186
|
+
*/
|
|
187
|
+
private getRetryDelayMs;
|
|
188
|
+
private getRateLimitHeaderValue;
|
|
189
|
+
}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BaseAPIClient = exports.defaultErrorHandler = void 0;
|
|
7
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
const attempt_1 = require("@lifeomic/attempt");
|
|
9
|
+
const errors_1 = require("./errors");
|
|
10
|
+
const get_1 = __importDefault(require("lodash/get"));
|
|
11
|
+
const hierarchical_token_bucket_1 = require("@jupiterone/hierarchical-token-bucket");
|
|
12
|
+
const defaultErrorHandler = async (err, context, logger) => {
|
|
13
|
+
if (err.code === 'ECONNRESET' || err.message.includes('ECONNRESET')) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!err.retryable) {
|
|
17
|
+
// can't retry this? just abort
|
|
18
|
+
context.abort();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (err.status === 429) {
|
|
22
|
+
const retryAfter = err.retryAfter ? err.retryAfter * 1000 : 5000;
|
|
23
|
+
logger.warn({ retryAfter }, 'Received a rate limit error. Waiting before retrying.');
|
|
24
|
+
await (0, attempt_1.sleep)(retryAfter);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
exports.defaultErrorHandler = defaultErrorHandler;
|
|
28
|
+
const DEFAULT_RETRY_OPTIONS = {
|
|
29
|
+
maxAttempts: 3,
|
|
30
|
+
delay: 30000,
|
|
31
|
+
timeout: 180000,
|
|
32
|
+
factor: 2,
|
|
33
|
+
handleError: exports.defaultErrorHandler,
|
|
34
|
+
};
|
|
35
|
+
const DEFAULT_RATE_LIMIT_HEADERS = {
|
|
36
|
+
limit: 'x-rate-limit-limit',
|
|
37
|
+
remaining: 'x-rate-limit-remaining',
|
|
38
|
+
reset: 'x-rate-limit-reset',
|
|
39
|
+
};
|
|
40
|
+
class BaseAPIClient {
|
|
41
|
+
baseUrl;
|
|
42
|
+
logger;
|
|
43
|
+
retryOptions;
|
|
44
|
+
logErrorBody;
|
|
45
|
+
rateLimitThrottling;
|
|
46
|
+
baseTokenBucket;
|
|
47
|
+
tokenBucketInitialConfig;
|
|
48
|
+
endpointTokenBuckets = {};
|
|
49
|
+
/**
|
|
50
|
+
* The authorization headers for the API requests
|
|
51
|
+
*/
|
|
52
|
+
authorizationHeaders;
|
|
53
|
+
/**
|
|
54
|
+
* Create a new API client
|
|
55
|
+
*
|
|
56
|
+
* @param {ClientConfig} config - The configuration for the client
|
|
57
|
+
* @param {string} config.baseUrl - The base URL for the API
|
|
58
|
+
* @param {IntegrationLogger} config.logger - The logger to use
|
|
59
|
+
* @param {Partial<RetryOptions>} [config.retryOptions] - The retry options for the client,
|
|
60
|
+
* if not provided, the default retry options will be used
|
|
61
|
+
* @param {boolean} [config.logErrorBody] - Whether to log the error body,
|
|
62
|
+
* if true, the error body will be logged when a request fails
|
|
63
|
+
* @param {RateLimitThrottlingOptions} [config.rateLimitThrottling] - The rate limit throttling options,
|
|
64
|
+
* if not provided, rate limit throttling will not be enabled
|
|
65
|
+
* @param {number} [config.rateLimitThrottling.threshold] - The threshold at which to throttle requests
|
|
66
|
+
* @param {RateLimitHeaders} [config.rateLimitThrottling.rateLimitHeaders] - The headers to use for rate limiting
|
|
67
|
+
* @param {string} [config.rateLimitThrottling.rateLimitHeaders.limit='x-rate-limit-limit'] - The header for the rate limit limit
|
|
68
|
+
* @param {string} [config.rateLimitThrottling.rateLimitHeaders.remaining='x-rate-limit-remaining'] - The header for the rate limit remaining
|
|
69
|
+
* @param {string} [config.rateLimitThrottling.rateLimitHeaders.reset='x-rate-limit-reset'] - The header for the rate limit reset
|
|
70
|
+
* @param {TokenBucketOptions} [config.tokenBucket] - The token bucket options,
|
|
71
|
+
* if not provided, token bucket will not be enabled
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const client = new APIClient({
|
|
76
|
+
* baseUrl: 'https://api.example.com',
|
|
77
|
+
* logger: context.logger,
|
|
78
|
+
* rateLimitThrottling: {
|
|
79
|
+
* threshold: 0.3,
|
|
80
|
+
* rateLimitHeaders: {
|
|
81
|
+
* limit: 'x-ratelimit-limit',
|
|
82
|
+
* remaining: 'x-ratelimit-remaining',
|
|
83
|
+
* reset: 'x-ratelimit-reset',
|
|
84
|
+
* },
|
|
85
|
+
* },
|
|
86
|
+
* })
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
constructor(config) {
|
|
90
|
+
this.baseUrl = config.baseUrl;
|
|
91
|
+
this.logger = config.logger;
|
|
92
|
+
this.retryOptions = {
|
|
93
|
+
...DEFAULT_RETRY_OPTIONS,
|
|
94
|
+
...config.retryOptions,
|
|
95
|
+
};
|
|
96
|
+
this.logErrorBody = config.logErrorBody ?? false;
|
|
97
|
+
this.rateLimitThrottling = config.rateLimitThrottling;
|
|
98
|
+
if (config.tokenBucket) {
|
|
99
|
+
this.tokenBucketInitialConfig = config.tokenBucket;
|
|
100
|
+
this.baseTokenBucket = new hierarchical_token_bucket_1.HierarchicalTokenBucket({
|
|
101
|
+
maximumCapacity: config.tokenBucket.maximumCapacity,
|
|
102
|
+
refillRate: config.tokenBucket.refillRate,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
withBaseUrl(endpoint) {
|
|
107
|
+
return new URL(endpoint, this.baseUrl).toString();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Perform a request to the API.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} endpoint - The endpoint to request
|
|
113
|
+
* @param {RequestOptions} options - The options for the request
|
|
114
|
+
* @param {string} [options.method=GET] - The HTTP method to use
|
|
115
|
+
* @param {Record<string, unknown>} [options.body] - The body of the request
|
|
116
|
+
* @param {Record<string, string>} [options.headers] - The headers for the request
|
|
117
|
+
* @param {boolean} [options.authorize=true] - Whether to include authorization headers
|
|
118
|
+
* @return {Promise<Response>} - The response from the API
|
|
119
|
+
*/
|
|
120
|
+
async request(endpoint, options) {
|
|
121
|
+
const tokenBucket = this.getTokenBucket(endpoint, options?.bucketTokens);
|
|
122
|
+
if (tokenBucket) {
|
|
123
|
+
const timeToWaitInMs = tokenBucket.take();
|
|
124
|
+
await (0, attempt_1.sleep)(timeToWaitInMs);
|
|
125
|
+
}
|
|
126
|
+
const { method = 'GET', body, headers, authorize = true } = options ?? {};
|
|
127
|
+
if (authorize && !this.authorizationHeaders) {
|
|
128
|
+
this.authorizationHeaders = await this.getAuthorizationHeaders();
|
|
129
|
+
}
|
|
130
|
+
let url;
|
|
131
|
+
try {
|
|
132
|
+
url = new URL(endpoint).toString();
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
// If the path is not a valid URL, assume it's a path and prepend the base URL
|
|
136
|
+
url = this.withBaseUrl(endpoint);
|
|
137
|
+
}
|
|
138
|
+
const response = await (0, node_fetch_1.default)(url, {
|
|
139
|
+
method,
|
|
140
|
+
headers: {
|
|
141
|
+
'Content-Type': 'application/json',
|
|
142
|
+
Accept: 'application/json',
|
|
143
|
+
...(authorize && this.authorizationHeaders),
|
|
144
|
+
...headers,
|
|
145
|
+
},
|
|
146
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
147
|
+
});
|
|
148
|
+
return response;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Perform a request to the API with retries and error handling.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} endpoint - The endpoint path to request
|
|
154
|
+
* @param {RequestOptions} options - The options for the request
|
|
155
|
+
* @param {string} [options.method=GET] - The HTTP method to use
|
|
156
|
+
* @param {Record<string, unknown>} [options.body] - The body of the request
|
|
157
|
+
* @param {Record<string, string>} [options.headers] - The headers for the request
|
|
158
|
+
* @param {boolean} [options.authorize=true] - Whether to include authorization headers
|
|
159
|
+
* @return {Promise<Response>} - The response from the API
|
|
160
|
+
*/
|
|
161
|
+
async retryableRequest(endpoint, options) {
|
|
162
|
+
const { method = 'GET', body, headers, authorize = true } = options ?? {};
|
|
163
|
+
return (0, attempt_1.retry)(async () => {
|
|
164
|
+
return this.withRateLimiting(async () => {
|
|
165
|
+
let response;
|
|
166
|
+
try {
|
|
167
|
+
response = await this.request(endpoint, {
|
|
168
|
+
method,
|
|
169
|
+
body,
|
|
170
|
+
headers,
|
|
171
|
+
authorize,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
this.logger.error({ code: err.code, err, endpoint }, 'Error sending request');
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
if (response.ok) {
|
|
179
|
+
return response;
|
|
180
|
+
}
|
|
181
|
+
let error;
|
|
182
|
+
const requestErrorParams = {
|
|
183
|
+
endpoint,
|
|
184
|
+
response,
|
|
185
|
+
logger: this.logger,
|
|
186
|
+
logErrorBody: this.logErrorBody,
|
|
187
|
+
};
|
|
188
|
+
if ((0, errors_1.isRetryableRequest)(response.status)) {
|
|
189
|
+
error = await (0, errors_1.retryableRequestError)(requestErrorParams);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
error = await (0, errors_1.fatalRequestError)(requestErrorParams);
|
|
193
|
+
}
|
|
194
|
+
for await (const _chunk of response.body) {
|
|
195
|
+
// force consumption of body to avoid memory leaks
|
|
196
|
+
// https://github.com/node-fetch/node-fetch/issues/83
|
|
197
|
+
}
|
|
198
|
+
throw error;
|
|
199
|
+
});
|
|
200
|
+
}, {
|
|
201
|
+
maxAttempts: this.retryOptions.maxAttempts,
|
|
202
|
+
delay: this.retryOptions.delay,
|
|
203
|
+
timeout: this.retryOptions.timeout,
|
|
204
|
+
factor: this.retryOptions.factor,
|
|
205
|
+
handleError: async (err, context) => {
|
|
206
|
+
await this.retryOptions.handleError(err, context, this.logger);
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Iteratively performs API requests based on the initial request and subsequent requests defined by a callback function.
|
|
212
|
+
* This method is designed to facilitate paginated API requests where each request's response determines the parameters of the next request.
|
|
213
|
+
*
|
|
214
|
+
* @param {object} initialRequest - The initial request parameters
|
|
215
|
+
* @param {string} initialRequest.endpoint - The endpoint path to request
|
|
216
|
+
* @param {RequestOptions} [initialRequest.options] - The options for the request
|
|
217
|
+
* @param {string|string[]|undefined} dataPath - The path to the data in the response to iterate over
|
|
218
|
+
* @param {function} nextPageCallback - The callback function to determine the parameters of the next request
|
|
219
|
+
*
|
|
220
|
+
* @return {AsyncGenerator<T, void, unknown>} - An async generator that yields the items from the paginated requests
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* async iterateUsers(iteratee: (user: User) => Promise<void>): Promise<void>{
|
|
225
|
+
* const baseEndpoint = '/users?limit=100';
|
|
226
|
+
* const iterator = this.paginate({
|
|
227
|
+
* endpoint: baseEndpoint,
|
|
228
|
+
* }, 'users', this.getNextPageCallback(baseEndpoint));
|
|
229
|
+
*
|
|
230
|
+
* for await (const user of iterator) {
|
|
231
|
+
* await iteratee(user);
|
|
232
|
+
* }
|
|
233
|
+
* };
|
|
234
|
+
*
|
|
235
|
+
* private getNextPageCallback(baseEndpoint: string) {
|
|
236
|
+
* return (data: NextPageData): IterateCallbackResult | undefined => {
|
|
237
|
+
* const { body } = data;
|
|
238
|
+
* const nextCursor = body.metadata?.cursor;
|
|
239
|
+
* if (!nextCursor) {
|
|
240
|
+
* return;
|
|
241
|
+
* }
|
|
242
|
+
* const nextUrl = `${baseEndpoint}&cursor=${nextCursor}`;
|
|
243
|
+
* return {
|
|
244
|
+
* nextUrl,
|
|
245
|
+
* };
|
|
246
|
+
* }
|
|
247
|
+
* };
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
async *paginate(initialRequest, dataPath, nextPageCallback) {
|
|
251
|
+
let nextUrl;
|
|
252
|
+
let nextRequestOptions;
|
|
253
|
+
let isInitialRequest = true;
|
|
254
|
+
do {
|
|
255
|
+
const response = await this.retryableRequest(isInitialRequest ? initialRequest.endpoint : nextUrl, isInitialRequest ? initialRequest.options : nextRequestOptions);
|
|
256
|
+
if (response.status === 204) {
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
const data = await response.json();
|
|
260
|
+
let items = dataPath ? (0, get_1.default)(data, dataPath) : data;
|
|
261
|
+
items = Array.isArray(items) ? items : [];
|
|
262
|
+
for (const item of items) {
|
|
263
|
+
yield item;
|
|
264
|
+
}
|
|
265
|
+
const cbOptions = nextPageCallback({
|
|
266
|
+
body: data,
|
|
267
|
+
headers: response.headers.raw(),
|
|
268
|
+
});
|
|
269
|
+
nextUrl = cbOptions?.nextUrl;
|
|
270
|
+
nextRequestOptions = cbOptions?.nextRequestOptions;
|
|
271
|
+
isInitialRequest = false;
|
|
272
|
+
} while (nextUrl);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get the token bucket for the given endpoint
|
|
276
|
+
*
|
|
277
|
+
* @param {string} endpoint - The endpoint to get the token bucket for
|
|
278
|
+
* @param {number} [tokens] - The number of tokens to use for the token bucket
|
|
279
|
+
*/
|
|
280
|
+
getTokenBucket(endpoint, tokens) {
|
|
281
|
+
if (!this.baseTokenBucket) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const path = endpoint.split('?')[0];
|
|
285
|
+
if (this.endpointTokenBuckets[path]) {
|
|
286
|
+
return this.endpointTokenBuckets[path];
|
|
287
|
+
}
|
|
288
|
+
this.endpointTokenBuckets[path] = new hierarchical_token_bucket_1.HierarchicalTokenBucket({
|
|
289
|
+
parent: this.baseTokenBucket,
|
|
290
|
+
maximumCapacity: tokens ?? this.tokenBucketInitialConfig?.maximumCapacity ?? 10000,
|
|
291
|
+
refillRate: tokens ?? this.tokenBucketInitialConfig?.refillRate ?? 10000,
|
|
292
|
+
});
|
|
293
|
+
return this.endpointTokenBuckets[path];
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Wait until the rate limit reset time before sending the next request
|
|
297
|
+
* if the rate limit threshold is exceeded.
|
|
298
|
+
*
|
|
299
|
+
* @param {function} fn - The function to rate limit
|
|
300
|
+
* @return {Promise<Response>}
|
|
301
|
+
*/
|
|
302
|
+
async withRateLimiting(fn) {
|
|
303
|
+
const response = await fn();
|
|
304
|
+
if (!this.rateLimitThrottling) {
|
|
305
|
+
return response;
|
|
306
|
+
}
|
|
307
|
+
const { rateLimitLimit, rateLimitRemaining } = this.parseRateLimitHeaders(response.headers);
|
|
308
|
+
if (this.shouldThrottleNextRequest({
|
|
309
|
+
rateLimitLimit,
|
|
310
|
+
rateLimitRemaining,
|
|
311
|
+
threshold: this.rateLimitThrottling.threshold,
|
|
312
|
+
})) {
|
|
313
|
+
const timeToSleepInMs = this.getRetryDelayMs(response.headers);
|
|
314
|
+
const thresholdPercentage = this.rateLimitThrottling.threshold * 100;
|
|
315
|
+
const resetHeaderName = this.rateLimitThrottling.rateLimitHeaders?.reset ??
|
|
316
|
+
'x-rate-limit-reset';
|
|
317
|
+
this.logger.warn({ rateLimitLimit, rateLimitRemaining, timeToSleepInMs }, `Exceeded ${thresholdPercentage}% of rate limit. Sleeping until ${resetHeaderName}`);
|
|
318
|
+
await (0, attempt_1.sleep)(timeToSleepInMs);
|
|
319
|
+
}
|
|
320
|
+
return response;
|
|
321
|
+
}
|
|
322
|
+
parseRateLimitHeaders(headers) {
|
|
323
|
+
const strRateLimitLimit = this.getRateLimitHeaderValue(headers, 'limit');
|
|
324
|
+
const strRateLimitRemaining = this.getRateLimitHeaderValue(headers, 'remaining');
|
|
325
|
+
return {
|
|
326
|
+
rateLimitLimit: strRateLimitLimit
|
|
327
|
+
? parseInt(strRateLimitLimit, 10)
|
|
328
|
+
: undefined,
|
|
329
|
+
rateLimitRemaining: strRateLimitRemaining
|
|
330
|
+
? parseInt(strRateLimitRemaining, 10)
|
|
331
|
+
: undefined,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Determine if the next request should be throttled based on the rate limit headers
|
|
336
|
+
*
|
|
337
|
+
* @param {object} params - The parameters for the function
|
|
338
|
+
* @param {number|undefined} params.rateLimitLimit - The rate limit limit
|
|
339
|
+
* @param {number|undefined} params.rateLimitRemaining - The rate limit remaining
|
|
340
|
+
* @param {number} params.threshold - The threshold at which to throttle requests
|
|
341
|
+
* @return {boolean} - Whether the next request should be throttled
|
|
342
|
+
*/
|
|
343
|
+
shouldThrottleNextRequest(params) {
|
|
344
|
+
const { rateLimitLimit, rateLimitRemaining } = params;
|
|
345
|
+
if (rateLimitLimit === undefined || rateLimitRemaining === undefined)
|
|
346
|
+
return false;
|
|
347
|
+
const rateLimitConsumed = rateLimitLimit - rateLimitRemaining;
|
|
348
|
+
return rateLimitConsumed / rateLimitLimit > params.threshold;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Determine wait time by getting the delta X-Rate-Limit-Reset and the Date header
|
|
352
|
+
* Add 1 second to account for sub second differences between the clocks that create these headers
|
|
353
|
+
*/
|
|
354
|
+
getRetryDelayMs(headers) {
|
|
355
|
+
const resetValue = this.getRateLimitHeaderValue(headers, 'reset');
|
|
356
|
+
if (!resetValue) {
|
|
357
|
+
// If the header is not present, we can't determine the wait time, so we'll just wait 60 seconds
|
|
358
|
+
return 60000;
|
|
359
|
+
}
|
|
360
|
+
const nowDate = new Date(headers.get('date') ?? Date.now());
|
|
361
|
+
const retryDate = new Date(parseInt(resetValue, 10) * 1000);
|
|
362
|
+
return retryDate.getTime() - nowDate.getTime() + 1000;
|
|
363
|
+
}
|
|
364
|
+
getRateLimitHeaderValue(headers, header) {
|
|
365
|
+
return headers.get(this.rateLimitThrottling?.rateLimitHeaders?.[header] ??
|
|
366
|
+
DEFAULT_RATE_LIMIT_HEADERS[header]);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
exports.BaseAPIClient = BaseAPIClient;
|
|
370
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { IntegrationLogger, IntegrationProviderAPIError } from '@jupiterone/integration-sdk-core';
|
|
2
|
+
import { Response } from 'node-fetch';
|
|
3
|
+
type RateLimitErrorParams = ConstructorParameters<typeof IntegrationProviderAPIError>[0] & {
|
|
4
|
+
retryAfter: number;
|
|
5
|
+
};
|
|
6
|
+
interface RequestErrorParams {
|
|
7
|
+
endpoint: string;
|
|
8
|
+
response: Response;
|
|
9
|
+
logger: IntegrationLogger;
|
|
10
|
+
logErrorBody: boolean;
|
|
4
11
|
}
|
|
5
|
-
export declare class
|
|
6
|
-
|
|
7
|
-
* Code associated with the error.
|
|
8
|
-
*/
|
|
9
|
-
readonly code: number;
|
|
10
|
-
constructor(config: APIErrorParams);
|
|
12
|
+
export declare class RetryableIntegrationProviderApiError extends IntegrationProviderAPIError {
|
|
13
|
+
retryable: boolean;
|
|
11
14
|
}
|
|
15
|
+
export declare class RateLimitError extends RetryableIntegrationProviderApiError {
|
|
16
|
+
constructor(options: RateLimitErrorParams);
|
|
17
|
+
retryAfter: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class ResponseBodyError extends Error {
|
|
20
|
+
bodyError: string;
|
|
21
|
+
constructor(bodyError: string);
|
|
22
|
+
}
|
|
23
|
+
export declare function retryableRequestError({ endpoint, response, logger, logErrorBody, }: RequestErrorParams): Promise<RetryableIntegrationProviderApiError>;
|
|
24
|
+
export declare function fatalRequestError({ endpoint, response, logger, logErrorBody, }: RequestErrorParams): Promise<IntegrationProviderAPIError>;
|
|
25
|
+
/**
|
|
26
|
+
* Function for determining if a request is retryable
|
|
27
|
+
* based on the returned status.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isRetryableRequest(status: number): boolean;
|
|
30
|
+
export {};
|