@gitbeaker/rest 39.18.0 → 39.19.0

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/README.md CHANGED
@@ -76,6 +76,7 @@
76
76
  - [API Client](#api-client)
77
77
  - [Expanded Payloads](#expanded-payloads)
78
78
  - [Pagination](#pagination)
79
+ - [Rate Limits](#rate-limits)
79
80
  - [Error Handling](#error-handling)
80
81
  - [Examples](#examples)
81
82
  - [Testing](../../docs/TESTING.md)
@@ -155,6 +156,7 @@ Available instantiating options:
155
156
  | `queryTimeout` | Yes | `300000` | Query Timeout in ms |
156
157
  | `profileToken` | Yes | N/A | [Requests Profiles Token](https://docs.gitlab.com/ee/administration/monitoring/performance/request_profiling.html) |
157
158
  | `profileMode` | Yes | `execution` | [Requests Profiles Token](https://docs.gitlab.com/ee/administration/monitoring/performance/request_profiling.html) |
159
+ | `rateLimits` | No | [DEFAULT_RATE_LIMITS](#rate-limits) | Global and endpoint specific adjustable rate limits |
158
160
 
159
161
  > \*One of these options must be supplied.
160
162
 
@@ -267,6 +269,72 @@ const { data } = await api.Projects.all({
267
269
  });
268
270
  ```
269
271
 
272
+ ### Rate Limits
273
+
274
+ Rate limits are completely customizable, and are used to limit the request rate between consecutive API requests within the library. By default, all non-specified endpoints use a 3000 rps rate limit, while some endpoints have much smaller rates as dictated by the [Gitlab Docs](https://docs.gitlab.com/ee/security/rate_limits.html). See below for the default values:
275
+
276
+ ```js
277
+ const DEFAULT_RATE_LIMITS = Object.freeze({
278
+ // Default rate limit
279
+ '**': 3000,
280
+
281
+ // Import/Export
282
+ 'projects/import': 6,
283
+ 'projects/*/export': 6,
284
+ 'projects/*/download': 1,
285
+ 'groups/import': 6,
286
+ 'groups/*/export': 6,
287
+ 'groups/*/download': 1,
288
+
289
+ // Note creation
290
+ 'projects/*/issues/*/notes': {
291
+ method: 'post',
292
+ limit: 300,
293
+ },
294
+ 'projects/*/snippets/*/notes': {
295
+ method: 'post',
296
+ limit: 300,
297
+ },
298
+ 'projects/*/merge_requests/*/notes': {
299
+ method: 'post',
300
+ limit: 300,
301
+ },
302
+ 'groups/*/epics/*/notes': {
303
+ method: 'post',
304
+ limit: 300,
305
+ },
306
+
307
+ // Repositories - get file archive
308
+ 'projects/*/repository/archive*': 5,
309
+
310
+ // Project Jobs
311
+ 'projects/*/jobs': 600,
312
+
313
+ // Member deletion
314
+ 'projects/*/members': 60,
315
+ 'groups/*/members': 60,
316
+ });
317
+ ```
318
+
319
+ Rate limits can be overridden when instantiating a API wrapper. For ease of use, these limits are configured using glob patterns, and can be formatted in two ways.
320
+
321
+ 1. The glob for the endpoint with the corresponding rate per second
322
+ 2. The glob for the endpoint, with an object specifying the specific method for the endpoint and the corresponding rate limit
323
+
324
+ ```js
325
+ const api = new Gitlab({
326
+ token: 'token',
327
+ rateLimits: {
328
+ '**': 30,
329
+ 'projects/import/*': 40,
330
+ 'projects/*/issues/*/notes': {
331
+ method: 'post',
332
+ limit: 300,
333
+ },
334
+ },
335
+ });
336
+ ```
337
+
270
338
  ### Error Handling
271
339
 
272
340
  Request errors are returned back within a plain Error instance, using the cause to hold the original response and a text description of the error pulled from the response's error or message fields if JSON, or its plain text value:
package/dist/index.js CHANGED
@@ -88,7 +88,8 @@ function getConditionalMode(endpoint) {
88
88
  async function defaultRequestHandler(endpoint, options) {
89
89
  const retryCodes = [429, 502];
90
90
  const maxRetries = 10;
91
- const { prefixUrl, asStream, searchParams, ...opts } = options || {};
91
+ const { prefixUrl, asStream, searchParams, rateLimiters, method, ...opts } = options || {};
92
+ const endpointRateLimit = requesterUtils.getMatchingRateLimiter(endpoint, rateLimiters, method);
92
93
  let baseUrl;
93
94
  if (prefixUrl)
94
95
  baseUrl = prefixUrl.endsWith("/") ? prefixUrl : `${prefixUrl}/`;
@@ -96,7 +97,8 @@ async function defaultRequestHandler(endpoint, options) {
96
97
  url.search = searchParams || "";
97
98
  const mode = getConditionalMode(endpoint);
98
99
  for (let i = 0; i < maxRetries; i += 1) {
99
- const request = new Request(url, { ...opts, mode });
100
+ const request = new Request(url, { ...opts, method, mode });
101
+ await endpointRateLimit();
100
102
  const response = await fetch(request).catch((e) => {
101
103
  if (e.name === "TimeoutError" || e.name === "AbortError") {
102
104
  throw new Error("Query timeout was reached");
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as Resources from '@gitbeaker/core';
2
- import { createRequesterFn, presetResourceArguments } from '@gitbeaker/requester-utils';
2
+ import { createRequesterFn, presetResourceArguments, getMatchingRateLimiter } from '@gitbeaker/requester-utils';
3
3
 
4
4
  // src/index.ts
5
5
  async function defaultOptionsHandler(resourceOptions, requestOptions) {
@@ -66,7 +66,8 @@ function getConditionalMode(endpoint) {
66
66
  async function defaultRequestHandler(endpoint, options) {
67
67
  const retryCodes = [429, 502];
68
68
  const maxRetries = 10;
69
- const { prefixUrl, asStream, searchParams, ...opts } = options || {};
69
+ const { prefixUrl, asStream, searchParams, rateLimiters, method, ...opts } = options || {};
70
+ const endpointRateLimit = getMatchingRateLimiter(endpoint, rateLimiters, method);
70
71
  let baseUrl;
71
72
  if (prefixUrl)
72
73
  baseUrl = prefixUrl.endsWith("/") ? prefixUrl : `${prefixUrl}/`;
@@ -74,7 +75,8 @@ async function defaultRequestHandler(endpoint, options) {
74
75
  url.search = searchParams || "";
75
76
  const mode = getConditionalMode(endpoint);
76
77
  for (let i = 0; i < maxRetries; i += 1) {
77
- const request = new Request(url, { ...opts, mode });
78
+ const request = new Request(url, { ...opts, method, mode });
79
+ await endpointRateLimit();
78
80
  const response = await fetch(request).catch((e) => {
79
81
  if (e.name === "TimeoutError" || e.name === "AbortError") {
80
82
  throw new Error("Query timeout was reached");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitbeaker/rest",
3
- "version": "39.18.0",
3
+ "version": "39.19.0",
4
4
  "description": "Cross Platform implementation of the GitLab API",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -40,7 +40,7 @@
40
40
  "test:types": "tsc",
41
41
  "test:e2e:browser": "playwright test",
42
42
  "test:e2e": "yarn test:e2e:browser",
43
- "test:integration:nodejs": "jest --maxWorkers=50% test/integration/nodejs",
43
+ "test:integration:nodejs": "jest --maxWorkers=50% test/integration/nodejs/resources/Issues.ts",
44
44
  "test:integration": "yarn test:integration:nodejs",
45
45
  "test:unit": "jest --maxWorkers=50% test/unit",
46
46
  "format:docs": "prettier '**/(*.json|.yml|.js|.md)' --ignore-path ../../.prettierignore",
@@ -56,8 +56,8 @@
56
56
  "release": "auto shipit"
57
57
  },
58
58
  "dependencies": {
59
- "@gitbeaker/core": "^39.18.0",
60
- "@gitbeaker/requester-utils": "^39.18.0"
59
+ "@gitbeaker/core": "^39.19.0",
60
+ "@gitbeaker/requester-utils": "^39.19.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@playwright/test": "^1.35.1",
@@ -65,5 +65,5 @@
65
65
  "tsup": "^7.1.0",
66
66
  "typescript": "^5.1.6"
67
67
  },
68
- "gitHead": "41b250e761aa15d8605c50b76d8439af827e06b5"
68
+ "gitHead": "b6ac7ead7ee50ced0f99f66ea0c212d2afcba3c2"
69
69
  }