netlify 12.0.1 → 12.0.3
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/LICENSE +22 -0
- package/README.md +1 -21
- package/lib/index.d.ts +11 -0
- package/lib/index.js +65 -0
- package/lib/methods/body.d.ts +1 -0
- package/lib/methods/body.js +25 -0
- package/lib/methods/index.d.ts +6 -0
- package/lib/methods/index.js +84 -0
- package/lib/methods/params.d.ts +1 -0
- package/lib/methods/params.js +14 -0
- package/lib/methods/response.d.ts +2 -0
- package/lib/methods/response.js +44 -0
- package/lib/methods/retry.d.ts +7 -0
- package/lib/methods/retry.js +40 -0
- package/lib/methods/url.d.ts +4 -0
- package/lib/methods/url.js +24 -0
- package/lib/open_api.d.ts +1 -0
- package/lib/open_api.js +5 -0
- package/lib/operations.d.ts +1 -0
- package/lib/operations.js +19 -0
- package/package.json +17 -38
- package/src/index.js +0 -92
- package/src/methods/body.js +0 -30
- package/src/methods/index.js +0 -100
- package/src/methods/params.js +0 -18
- package/src/methods/response.js +0 -54
- package/src/methods/retry.js +0 -49
- package/src/methods/url.js +0 -31
- package/src/open_api.js +0 -7
- package/src/operations.js +0 -23
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2020 Netlify <team@netlify.com>
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-

|
|
2
|
-
|
|
3
|
-
[![npm version][npm-img]][npm] [![build status][build-img]][build] [![coverage][coverage-img]][coverage]
|
|
4
|
-
[![dependencies][david-img]][david] [![downloads][dl-img]][dl]
|
|
5
|
-
|
|
6
1
|
A Netlify [OpenAPI](https://github.com/netlify/open-api) client that works in the browser and Node.js.
|
|
7
2
|
|
|
8
3
|
## Usage
|
|
@@ -182,21 +177,6 @@ const client = new NetlifyAPI('1234myAccessToken', { agent })
|
|
|
182
177
|
Support for site deployment has been removed from this package in version 7.0.0. You should consider using the
|
|
183
178
|
[`deploy` command](https://cli.netlify.com/commands/deploy/) of Netlify CLI.
|
|
184
179
|
|
|
185
|
-
## Contributing
|
|
186
|
-
|
|
187
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for more info on how to make contributions to this project.
|
|
188
|
-
|
|
189
180
|
## License
|
|
190
181
|
|
|
191
|
-
MIT. See [LICENSE](LICENSE) for more details.
|
|
192
|
-
|
|
193
|
-
[npm-img]: https://img.shields.io/npm/v/netlify.svg
|
|
194
|
-
[npm]: https://npmjs.org/package/netlify
|
|
195
|
-
[build-img]: https://github.com/netlify/js-client/workflows/Build/badge.svg
|
|
196
|
-
[build]: https://github.com/netlify/js-client/actions
|
|
197
|
-
[dl-img]: https://img.shields.io/npm/dm/netlify.svg
|
|
198
|
-
[dl]: https://npmjs.org/package/netlify
|
|
199
|
-
[coverage-img]: https://codecov.io/gh/netlify/js-client/branch/main/graph/badge.svg
|
|
200
|
-
[coverage]: https://codecov.io/gh/netlify/js-client
|
|
201
|
-
[david-img]: https://david-dm.org/netlify/js-client/status.svg
|
|
202
|
-
[david]: https://david-dm.org/netlify/js-client
|
|
182
|
+
MIT. See [LICENSE](./LICENSE) for more details.
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class NetlifyAPI {
|
|
2
|
+
constructor(firstArg: any, secondArg: any);
|
|
3
|
+
set accessToken(arg: string);
|
|
4
|
+
get accessToken(): string;
|
|
5
|
+
get basePath(): string;
|
|
6
|
+
getAccessToken(ticket: any, { poll, timeout }?: {
|
|
7
|
+
poll?: number;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
}): Promise<any>;
|
|
10
|
+
}
|
|
11
|
+
export const methods: any[];
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import pWaitFor from 'p-wait-for';
|
|
2
|
+
import { getMethods } from './methods/index.js';
|
|
3
|
+
import { openApiSpec } from './open_api.js';
|
|
4
|
+
import { getOperations } from './operations.js';
|
|
5
|
+
export class NetlifyAPI {
|
|
6
|
+
constructor(firstArg, secondArg) {
|
|
7
|
+
// variadic arguments
|
|
8
|
+
const [accessTokenInput, opts = {}] = typeof firstArg === 'object' ? [null, firstArg] : [firstArg, secondArg];
|
|
9
|
+
// default opts
|
|
10
|
+
const { userAgent = 'netlify/js-client', scheme = openApiSpec.schemes[0], host = openApiSpec.host, pathPrefix = openApiSpec.basePath, accessToken = accessTokenInput, globalParams = {}, agent, } = opts;
|
|
11
|
+
const defaultHeaders = {
|
|
12
|
+
'User-agent': userAgent,
|
|
13
|
+
accept: 'application/json',
|
|
14
|
+
};
|
|
15
|
+
const basePath = getBasePath({ scheme, host, pathPrefix });
|
|
16
|
+
const methods = getMethods({ basePath, defaultHeaders, agent, globalParams });
|
|
17
|
+
Object.assign(this, { ...methods, defaultHeaders, scheme, host, pathPrefix, globalParams, accessToken, agent });
|
|
18
|
+
}
|
|
19
|
+
get accessToken() {
|
|
20
|
+
const { defaultHeaders: { Authorization }, } = this;
|
|
21
|
+
if (typeof Authorization !== 'string' || !Authorization.startsWith('Bearer ')) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return Authorization.replace('Bearer ', '');
|
|
25
|
+
}
|
|
26
|
+
set accessToken(token) {
|
|
27
|
+
if (!token) {
|
|
28
|
+
delete this.defaultHeaders.Authorization;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.defaultHeaders.Authorization = `Bearer ${token}`;
|
|
32
|
+
}
|
|
33
|
+
get basePath() {
|
|
34
|
+
return getBasePath({ scheme: this.scheme, host: this.host, pathPrefix: this.pathPrefix });
|
|
35
|
+
}
|
|
36
|
+
async getAccessToken(ticket, { poll = DEFAULT_TICKET_POLL, timeout = DEFAULT_TICKET_TIMEOUT } = {}) {
|
|
37
|
+
const { id } = ticket;
|
|
38
|
+
// ticket capture
|
|
39
|
+
let authorizedTicket;
|
|
40
|
+
const checkTicket = async () => {
|
|
41
|
+
const t = await this.showTicket({ ticketId: id });
|
|
42
|
+
if (t.authorized) {
|
|
43
|
+
authorizedTicket = t;
|
|
44
|
+
}
|
|
45
|
+
return Boolean(t.authorized);
|
|
46
|
+
};
|
|
47
|
+
await pWaitFor(checkTicket, {
|
|
48
|
+
interval: poll,
|
|
49
|
+
timeout,
|
|
50
|
+
message: 'Timeout while waiting for ticket grant',
|
|
51
|
+
});
|
|
52
|
+
const accessTokenResponse = await this.exchangeTicket({ ticketId: authorizedTicket.id });
|
|
53
|
+
// See https://open-api.netlify.com/#/default/exchangeTicket for shape
|
|
54
|
+
this.accessToken = accessTokenResponse.access_token;
|
|
55
|
+
return accessTokenResponse.access_token;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const getBasePath = function ({ scheme, host, pathPrefix }) {
|
|
59
|
+
return `${scheme}://${host}${pathPrefix}`;
|
|
60
|
+
};
|
|
61
|
+
// 1 second
|
|
62
|
+
const DEFAULT_TICKET_POLL = 1e3;
|
|
63
|
+
// 1 hour
|
|
64
|
+
const DEFAULT_TICKET_TIMEOUT = 3.6e6;
|
|
65
|
+
export const methods = getOperations();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function addBody(body: any, parameters: any, opts: any): any;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Handle request body
|
|
2
|
+
export const addBody = function (body, parameters, opts) {
|
|
3
|
+
if (!body) {
|
|
4
|
+
return opts;
|
|
5
|
+
}
|
|
6
|
+
const bodyA = typeof body === 'function' ? body() : body;
|
|
7
|
+
if (isBinaryBody(parameters)) {
|
|
8
|
+
return {
|
|
9
|
+
...opts,
|
|
10
|
+
body: bodyA,
|
|
11
|
+
headers: { 'Content-Type': 'application/octet-stream', ...opts.headers },
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
...opts,
|
|
16
|
+
body: JSON.stringify(bodyA),
|
|
17
|
+
headers: { 'Content-Type': 'application/json', ...opts.headers },
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const isBinaryBody = function (parameters) {
|
|
21
|
+
return Object.values(parameters.body).some(isBodyParam);
|
|
22
|
+
};
|
|
23
|
+
const isBodyParam = function ({ schema }) {
|
|
24
|
+
return schema && schema.format === 'binary';
|
|
25
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
import { getOperations } from '../operations.js';
|
|
3
|
+
import { addBody } from './body.js';
|
|
4
|
+
import { getRequestParams } from './params.js';
|
|
5
|
+
import { parseResponse, getFetchError } from './response.js';
|
|
6
|
+
import { shouldRetry, waitForRetry, MAX_RETRY } from './retry.js';
|
|
7
|
+
import { getUrl } from './url.js';
|
|
8
|
+
// For each OpenAPI operation, add a corresponding method.
|
|
9
|
+
// The `operationId` is the method name.
|
|
10
|
+
export const getMethods = function ({ basePath, defaultHeaders, agent, globalParams }) {
|
|
11
|
+
const operations = getOperations();
|
|
12
|
+
const methods = operations.map((method) => getMethod({ method, basePath, defaultHeaders, agent, globalParams }));
|
|
13
|
+
return Object.assign({}, ...methods);
|
|
14
|
+
};
|
|
15
|
+
const getMethod = function ({ method, basePath, defaultHeaders, agent, globalParams }) {
|
|
16
|
+
return {
|
|
17
|
+
[method.operationId](params, opts) {
|
|
18
|
+
return callMethod({ method, basePath, defaultHeaders, agent, globalParams, params, opts });
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
const callMethod = async function ({ method, basePath, defaultHeaders, agent, globalParams, params, opts }) {
|
|
23
|
+
const requestParams = { ...globalParams, ...params };
|
|
24
|
+
const url = getUrl(method, basePath, requestParams);
|
|
25
|
+
const response = await makeRequestOrRetry({ url, method, defaultHeaders, agent, requestParams, opts });
|
|
26
|
+
const parsedResponse = await parseResponse(response);
|
|
27
|
+
return parsedResponse;
|
|
28
|
+
};
|
|
29
|
+
const getOpts = function ({ method: { verb, parameters }, defaultHeaders, agent, requestParams, opts }) {
|
|
30
|
+
const { body } = requestParams;
|
|
31
|
+
const optsA = addHttpMethod(verb, opts);
|
|
32
|
+
const optsB = addHeaderParams(parameters, requestParams, optsA);
|
|
33
|
+
const optsC = addDefaultHeaders(defaultHeaders, optsB);
|
|
34
|
+
const optsD = addBody(body, parameters, optsC);
|
|
35
|
+
const optsE = addAgent(agent, optsD);
|
|
36
|
+
return optsE;
|
|
37
|
+
};
|
|
38
|
+
// Add header parameters
|
|
39
|
+
const addHeaderParams = function (parameters, requestParams, opts) {
|
|
40
|
+
if (parameters.header === undefined) {
|
|
41
|
+
return opts;
|
|
42
|
+
}
|
|
43
|
+
return { ...opts, headers: getRequestParams(parameters.header, requestParams, 'header parameter') };
|
|
44
|
+
};
|
|
45
|
+
// Add the HTTP method based on the OpenAPI definition
|
|
46
|
+
const addHttpMethod = function (verb, opts) {
|
|
47
|
+
return { ...opts, method: verb.toUpperCase() };
|
|
48
|
+
};
|
|
49
|
+
// Assign default HTTP headers
|
|
50
|
+
const addDefaultHeaders = function (defaultHeaders, opts) {
|
|
51
|
+
return { ...opts, headers: { ...defaultHeaders, ...opts.headers } };
|
|
52
|
+
};
|
|
53
|
+
// Assign fetch agent (like for example HttpsProxyAgent) if there is one
|
|
54
|
+
const addAgent = function (agent, opts) {
|
|
55
|
+
if (agent) {
|
|
56
|
+
return { ...opts, agent };
|
|
57
|
+
}
|
|
58
|
+
return opts;
|
|
59
|
+
};
|
|
60
|
+
const makeRequestOrRetry = async function ({ url, method, defaultHeaders, agent, requestParams, opts }) {
|
|
61
|
+
// Using a loop is simpler here
|
|
62
|
+
for (let index = 0; index <= MAX_RETRY; index++) {
|
|
63
|
+
const optsA = getOpts({ method, defaultHeaders, agent, requestParams, opts });
|
|
64
|
+
const { response, error } = await makeRequest(url, optsA);
|
|
65
|
+
if (shouldRetry({ response, error, method }) && index !== MAX_RETRY) {
|
|
66
|
+
await waitForRetry(response);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (error !== undefined) {
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
return response;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const makeRequest = async function (url, opts) {
|
|
76
|
+
try {
|
|
77
|
+
const response = await fetch(url, opts);
|
|
78
|
+
return { response };
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const errorA = getFetchError(error, url, opts);
|
|
82
|
+
return { error: errorA };
|
|
83
|
+
}
|
|
84
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function getRequestParams(params: any, requestParams: any, name: any): any;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { camelCase } from 'lodash-es';
|
|
2
|
+
export const getRequestParams = function (params, requestParams, name) {
|
|
3
|
+
const entries = Object.values(params).map((param) => getRequestParam(param, requestParams, name));
|
|
4
|
+
return Object.assign({}, ...entries);
|
|
5
|
+
};
|
|
6
|
+
const getRequestParam = function (param, requestParams, name) {
|
|
7
|
+
const value = requestParams[param.name] || requestParams[camelCase(param.name)];
|
|
8
|
+
if (value !== undefined) {
|
|
9
|
+
return { [param.name]: value };
|
|
10
|
+
}
|
|
11
|
+
if (param.required) {
|
|
12
|
+
throw new Error(`Missing required ${name} '${param.name}'`);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { JSONHTTPError, TextHTTPError } from 'micro-api-client';
|
|
2
|
+
import omit from 'omit.js';
|
|
3
|
+
// Read and parse the HTTP response
|
|
4
|
+
export const parseResponse = async function (response) {
|
|
5
|
+
const responseType = getResponseType(response);
|
|
6
|
+
const textResponse = await response.text();
|
|
7
|
+
const parsedResponse = parseJsonResponse(response, textResponse, responseType);
|
|
8
|
+
if (!response.ok) {
|
|
9
|
+
const ErrorType = responseType === 'json' ? JSONHTTPError : TextHTTPError;
|
|
10
|
+
throw addFallbackErrorMessage(new ErrorType(response, parsedResponse), textResponse);
|
|
11
|
+
}
|
|
12
|
+
return parsedResponse;
|
|
13
|
+
};
|
|
14
|
+
const getResponseType = function ({ headers }) {
|
|
15
|
+
const contentType = headers.get('Content-Type');
|
|
16
|
+
if (contentType != null && contentType.includes('json')) {
|
|
17
|
+
return 'json';
|
|
18
|
+
}
|
|
19
|
+
return 'text';
|
|
20
|
+
};
|
|
21
|
+
const parseJsonResponse = function (response, textResponse, responseType) {
|
|
22
|
+
if (responseType === 'text') {
|
|
23
|
+
return textResponse;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(textResponse);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw addFallbackErrorMessage(new TextHTTPError(response, textResponse), textResponse);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const addFallbackErrorMessage = function (error, textResponse) {
|
|
33
|
+
error.message = error.message || textResponse;
|
|
34
|
+
return error;
|
|
35
|
+
};
|
|
36
|
+
export const getFetchError = function (error, url, opts) {
|
|
37
|
+
const data = omit.default(opts, ['Authorization']);
|
|
38
|
+
if (error.name !== 'FetchError') {
|
|
39
|
+
error.name = 'FetchError';
|
|
40
|
+
}
|
|
41
|
+
error.url = url;
|
|
42
|
+
error.data = data;
|
|
43
|
+
return error;
|
|
44
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// We retry:
|
|
2
|
+
// - when receiving a rate limiting response
|
|
3
|
+
// - on network failures due to timeouts
|
|
4
|
+
export const shouldRetry = function ({ response = {}, error = {}, method = {} }) {
|
|
5
|
+
if (response.status === RATE_LIMIT_STATUS || RETRY_ERROR_CODES.has(error.code)) {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
// Special case for the `getLatestPluginRuns` endpoint.
|
|
9
|
+
// See https://github.com/netlify/bitballoon/issues/9616.
|
|
10
|
+
if (method.operationId === 'getLatestPluginRuns' && response.status === 500) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
};
|
|
15
|
+
export const waitForRetry = async function (response) {
|
|
16
|
+
const delay = getDelay(response);
|
|
17
|
+
await sleep(delay);
|
|
18
|
+
};
|
|
19
|
+
const getDelay = function (response) {
|
|
20
|
+
if (response === undefined) {
|
|
21
|
+
return DEFAULT_RETRY_DELAY;
|
|
22
|
+
}
|
|
23
|
+
const rateLimitReset = response.headers.get(RATE_LIMIT_HEADER);
|
|
24
|
+
if (!rateLimitReset) {
|
|
25
|
+
return DEFAULT_RETRY_DELAY;
|
|
26
|
+
}
|
|
27
|
+
return Math.max(Number(rateLimitReset) * SECS_TO_MSECS - Date.now(), MIN_RETRY_DELAY);
|
|
28
|
+
};
|
|
29
|
+
const sleep = function (ms) {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
setTimeout(resolve, ms);
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
const DEFAULT_RETRY_DELAY = 5e3;
|
|
35
|
+
const MIN_RETRY_DELAY = 1e3;
|
|
36
|
+
const SECS_TO_MSECS = 1e3;
|
|
37
|
+
export const MAX_RETRY = 5;
|
|
38
|
+
const RATE_LIMIT_STATUS = 429;
|
|
39
|
+
const RATE_LIMIT_HEADER = 'X-RateLimit-Reset';
|
|
40
|
+
const RETRY_ERROR_CODES = new Set(['ETIMEDOUT', 'ECONNRESET']);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import queryString from 'qs';
|
|
2
|
+
import { getRequestParams } from './params.js';
|
|
3
|
+
// Replace path parameters and query parameters in the URI, using the OpenAPI
|
|
4
|
+
// definition
|
|
5
|
+
export const getUrl = function ({ path, parameters }, basePath, requestParams) {
|
|
6
|
+
const url = `${basePath}${path}`;
|
|
7
|
+
const urlA = addPathParams(url, parameters, requestParams);
|
|
8
|
+
const urlB = addQueryParams(urlA, parameters, requestParams);
|
|
9
|
+
return urlB;
|
|
10
|
+
};
|
|
11
|
+
const addPathParams = function (url, parameters, requestParams) {
|
|
12
|
+
const pathParams = getRequestParams(parameters.path, requestParams, 'path variable');
|
|
13
|
+
return Object.entries(pathParams).reduce(addPathParam, url);
|
|
14
|
+
};
|
|
15
|
+
const addPathParam = function (url, [name, value]) {
|
|
16
|
+
return url.replace(`{${name}}`, value);
|
|
17
|
+
};
|
|
18
|
+
const addQueryParams = function (url, parameters, requestParams) {
|
|
19
|
+
const queryParams = getRequestParams(parameters.query, requestParams, 'query variable');
|
|
20
|
+
if (Object.keys(queryParams).length === 0) {
|
|
21
|
+
return url;
|
|
22
|
+
}
|
|
23
|
+
return `${url}?${queryString.stringify(queryParams, { arrayFormat: 'brackets' })}`;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const openApiSpec: any;
|
package/lib/open_api.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function getOperations(): any[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import omit from 'omit.js';
|
|
2
|
+
import { openApiSpec } from './open_api.js';
|
|
3
|
+
// Retrieve all OpenAPI operations
|
|
4
|
+
export const getOperations = function () {
|
|
5
|
+
return Object.entries(openApiSpec.paths).flatMap(([path, pathItem]) => {
|
|
6
|
+
const operations = omit.default(pathItem, ['parameters']);
|
|
7
|
+
return Object.entries(operations).map(([method, operation]) => {
|
|
8
|
+
const parameters = getParameters(pathItem.parameters, operation.parameters);
|
|
9
|
+
return { ...operation, verb: method, path, parameters };
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
const getParameters = function (pathParameters = [], operationParameters = []) {
|
|
14
|
+
const parameters = [...pathParameters, ...operationParameters];
|
|
15
|
+
return parameters.reduce(addParameter, { path: {}, query: {}, body: {} });
|
|
16
|
+
};
|
|
17
|
+
const addParameter = function (parameters, param) {
|
|
18
|
+
return { ...parameters, [param.in]: { ...parameters[param.in], [param.name]: param } };
|
|
19
|
+
};
|
package/package.json
CHANGED
|
@@ -1,33 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify",
|
|
3
3
|
"description": "Netlify Node.js API client",
|
|
4
|
-
"version": "12.0.
|
|
4
|
+
"version": "12.0.3",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"exports": "./lib/index.js",
|
|
7
|
+
"main": "./lib/index.js",
|
|
8
|
+
"types": "./lib/index.d.ts",
|
|
6
9
|
"files": [
|
|
7
|
-
"
|
|
8
|
-
"!src/**/*.test.js"
|
|
10
|
+
"lib/**"
|
|
9
11
|
],
|
|
10
|
-
"exports": "./src/index.js",
|
|
11
|
-
"main": "./src/index.js",
|
|
12
12
|
"scripts": {
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"test": "
|
|
16
|
-
"
|
|
17
|
-
"format:ci": "run-s format:check:*",
|
|
18
|
-
"format:check-fix:lint": "run-e format:check:lint format:fix:lint",
|
|
19
|
-
"format:check:lint": "cross-env-shell eslint $npm_package_config_eslint",
|
|
20
|
-
"format:fix:lint": "cross-env-shell eslint --fix $npm_package_config_eslint",
|
|
21
|
-
"format:check-fix:prettier": "run-e format:check:prettier format:fix:prettier",
|
|
22
|
-
"format:check:prettier": "cross-env-shell prettier --check $npm_package_config_prettier",
|
|
23
|
-
"format:fix:prettier": "cross-env-shell prettier --write $npm_package_config_prettier",
|
|
24
|
-
"test:dev": "ava",
|
|
25
|
-
"test:ci": "c8 -r lcovonly -r text -r json ava",
|
|
26
|
-
"update-snapshots": "ava -u"
|
|
27
|
-
},
|
|
28
|
-
"config": {
|
|
29
|
-
"eslint": "--ignore-path .gitignore --cache --format=codeframe --max-warnings=0 \"{src,tests,.github}/**/*.{cjs,mjs,js,md,html}\" \"*.{cjs,mjs,js,md,html}\" \".*.{cjs,mjs,js,md,html}\"",
|
|
30
|
-
"prettier": "--ignore-path .gitignore --loglevel=warn \"{src,tests,.github}/**/*.{cjs,mjs,js,md,yml,json,html}\" \"*.{cjs,mjs,js,yml,json,html}\" \".*.{cjs,mjs,js,yml,json,html}\" \"!package-lock.json\" \"!CHANGELOG.md\""
|
|
13
|
+
"prebuild": "rm -rf lib",
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "ava",
|
|
16
|
+
"test:ci": "c8 -r lcovonly -r text -r json ava"
|
|
31
17
|
},
|
|
32
18
|
"license": "MIT",
|
|
33
19
|
"author": "Netlify Inc.",
|
|
@@ -35,15 +21,16 @@
|
|
|
35
21
|
"Mathias Biilmann <matt@netlify.com> (https://twitter.com/biilmann)",
|
|
36
22
|
"David Calavera <david@netlify.com> (https://twitter.com/calavera)",
|
|
37
23
|
"David Wells <david.wells@netlify.com> (https://davidwells.io/)",
|
|
38
|
-
"Bret Comnes <bcomnes@gmail.com> (https://bret.io)"
|
|
24
|
+
"Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
|
|
25
|
+
"Lukas Holzer <lukas.holzer@netlify.com> (https://twitter.com/luka5c0m)"
|
|
39
26
|
],
|
|
40
|
-
"homepage": "https://github.com/netlify/
|
|
27
|
+
"homepage": "https://github.com/netlify/build",
|
|
41
28
|
"repository": {
|
|
42
29
|
"type": "git",
|
|
43
|
-
"url": "https://github.com/netlify/
|
|
30
|
+
"url": "https://github.com/netlify/build.git"
|
|
44
31
|
},
|
|
45
32
|
"bugs": {
|
|
46
|
-
"url": "https://github.com/netlify/
|
|
33
|
+
"url": "https://github.com/netlify/build/issues"
|
|
47
34
|
},
|
|
48
35
|
"keywords": [
|
|
49
36
|
"api client",
|
|
@@ -53,7 +40,7 @@
|
|
|
53
40
|
],
|
|
54
41
|
"dependencies": {
|
|
55
42
|
"@netlify/open-api": "^2.12.0",
|
|
56
|
-
"lodash
|
|
43
|
+
"lodash-es": "^4.17.21",
|
|
57
44
|
"micro-api-client": "^3.3.0",
|
|
58
45
|
"node-fetch": "^3.0.0",
|
|
59
46
|
"omit.js": "^2.0.2",
|
|
@@ -61,23 +48,15 @@
|
|
|
61
48
|
"qs": "^6.9.6"
|
|
62
49
|
},
|
|
63
50
|
"devDependencies": {
|
|
64
|
-
"@
|
|
51
|
+
"@types/lodash-es": "^4.17.6",
|
|
65
52
|
"ava": "^4.0.0",
|
|
66
53
|
"c8": "^7.11.0",
|
|
67
54
|
"from2-string": "^1.1.0",
|
|
68
|
-
"husky": "^8.0.0",
|
|
69
55
|
"nock": "^13.0.0",
|
|
70
|
-
"npm-run-all": "^4.1.5",
|
|
71
56
|
"uuid": "^8.3.2"
|
|
72
57
|
},
|
|
73
58
|
"engines": {
|
|
74
59
|
"node": "^12.20.0 || ^14.14.0 || >=16.0.0"
|
|
75
60
|
},
|
|
76
|
-
"
|
|
77
|
-
"files": [
|
|
78
|
-
"src/**/*.test.js"
|
|
79
|
-
],
|
|
80
|
-
"verbose": true,
|
|
81
|
-
"workerThreads": false
|
|
82
|
-
}
|
|
61
|
+
"gitHead": "b75d4a07f41dd18f5314920e0cef644c118677b4"
|
|
83
62
|
}
|
package/src/index.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import pWaitFor from 'p-wait-for'
|
|
2
|
-
|
|
3
|
-
import { getMethods } from './methods/index.js'
|
|
4
|
-
import { openApiSpec } from './open_api.js'
|
|
5
|
-
import { getOperations } from './operations.js'
|
|
6
|
-
|
|
7
|
-
export class NetlifyAPI {
|
|
8
|
-
constructor(firstArg, secondArg) {
|
|
9
|
-
// variadic arguments
|
|
10
|
-
const [accessTokenInput, opts = {}] = typeof firstArg === 'object' ? [null, firstArg] : [firstArg, secondArg]
|
|
11
|
-
|
|
12
|
-
// default opts
|
|
13
|
-
const {
|
|
14
|
-
userAgent = 'netlify/js-client',
|
|
15
|
-
scheme = openApiSpec.schemes[0],
|
|
16
|
-
host = openApiSpec.host,
|
|
17
|
-
pathPrefix = openApiSpec.basePath,
|
|
18
|
-
accessToken = accessTokenInput,
|
|
19
|
-
globalParams = {},
|
|
20
|
-
agent,
|
|
21
|
-
} = opts
|
|
22
|
-
|
|
23
|
-
const defaultHeaders = {
|
|
24
|
-
'User-agent': userAgent,
|
|
25
|
-
accept: 'application/json',
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const basePath = getBasePath({ scheme, host, pathPrefix })
|
|
29
|
-
const methods = getMethods({ basePath, defaultHeaders, agent, globalParams })
|
|
30
|
-
Object.assign(this, { ...methods, defaultHeaders, scheme, host, pathPrefix, globalParams, accessToken, agent })
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get accessToken() {
|
|
34
|
-
const {
|
|
35
|
-
defaultHeaders: { Authorization },
|
|
36
|
-
} = this
|
|
37
|
-
if (typeof Authorization !== 'string' || !Authorization.startsWith('Bearer ')) {
|
|
38
|
-
return null
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return Authorization.replace('Bearer ', '')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
set accessToken(token) {
|
|
45
|
-
if (!token) {
|
|
46
|
-
delete this.defaultHeaders.Authorization
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
this.defaultHeaders.Authorization = `Bearer ${token}`
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
get basePath() {
|
|
54
|
-
return getBasePath({ scheme: this.scheme, host: this.host, pathPrefix: this.pathPrefix })
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async getAccessToken(ticket, { poll = DEFAULT_TICKET_POLL, timeout = DEFAULT_TICKET_TIMEOUT } = {}) {
|
|
58
|
-
const { id } = ticket
|
|
59
|
-
|
|
60
|
-
// ticket capture
|
|
61
|
-
let authorizedTicket
|
|
62
|
-
const checkTicket = async () => {
|
|
63
|
-
const t = await this.showTicket({ ticketId: id })
|
|
64
|
-
if (t.authorized) {
|
|
65
|
-
authorizedTicket = t
|
|
66
|
-
}
|
|
67
|
-
return Boolean(t.authorized)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
await pWaitFor(checkTicket, {
|
|
71
|
-
interval: poll,
|
|
72
|
-
timeout,
|
|
73
|
-
message: 'Timeout while waiting for ticket grant',
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
const accessTokenResponse = await this.exchangeTicket({ ticketId: authorizedTicket.id })
|
|
77
|
-
// See https://open-api.netlify.com/#/default/exchangeTicket for shape
|
|
78
|
-
this.accessToken = accessTokenResponse.access_token
|
|
79
|
-
return accessTokenResponse.access_token
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const getBasePath = function ({ scheme, host, pathPrefix }) {
|
|
84
|
-
return `${scheme}://${host}${pathPrefix}`
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 1 second
|
|
88
|
-
const DEFAULT_TICKET_POLL = 1e3
|
|
89
|
-
// 1 hour
|
|
90
|
-
const DEFAULT_TICKET_TIMEOUT = 3.6e6
|
|
91
|
-
|
|
92
|
-
export const methods = getOperations()
|
package/src/methods/body.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// Handle request body
|
|
2
|
-
export const addBody = function (body, parameters, opts) {
|
|
3
|
-
if (!body) {
|
|
4
|
-
return opts
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const bodyA = typeof body === 'function' ? body() : body
|
|
8
|
-
|
|
9
|
-
if (isBinaryBody(parameters)) {
|
|
10
|
-
return {
|
|
11
|
-
...opts,
|
|
12
|
-
body: bodyA,
|
|
13
|
-
headers: { 'Content-Type': 'application/octet-stream', ...opts.headers },
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
...opts,
|
|
19
|
-
body: JSON.stringify(bodyA),
|
|
20
|
-
headers: { 'Content-Type': 'application/json', ...opts.headers },
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const isBinaryBody = function (parameters) {
|
|
25
|
-
return Object.values(parameters.body).some(isBodyParam)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const isBodyParam = function ({ schema }) {
|
|
29
|
-
return schema && schema.format === 'binary'
|
|
30
|
-
}
|
package/src/methods/index.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import fetch from 'node-fetch'
|
|
2
|
-
|
|
3
|
-
import { getOperations } from '../operations.js'
|
|
4
|
-
|
|
5
|
-
import { addBody } from './body.js'
|
|
6
|
-
import { getRequestParams } from './params.js'
|
|
7
|
-
import { parseResponse, getFetchError } from './response.js'
|
|
8
|
-
import { shouldRetry, waitForRetry, MAX_RETRY } from './retry.js'
|
|
9
|
-
import { getUrl } from './url.js'
|
|
10
|
-
|
|
11
|
-
// For each OpenAPI operation, add a corresponding method.
|
|
12
|
-
// The `operationId` is the method name.
|
|
13
|
-
export const getMethods = function ({ basePath, defaultHeaders, agent, globalParams }) {
|
|
14
|
-
const operations = getOperations()
|
|
15
|
-
const methods = operations.map((method) => getMethod({ method, basePath, defaultHeaders, agent, globalParams }))
|
|
16
|
-
return Object.assign({}, ...methods)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const getMethod = function ({ method, basePath, defaultHeaders, agent, globalParams }) {
|
|
20
|
-
return {
|
|
21
|
-
[method.operationId](params, opts) {
|
|
22
|
-
return callMethod({ method, basePath, defaultHeaders, agent, globalParams, params, opts })
|
|
23
|
-
},
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const callMethod = async function ({ method, basePath, defaultHeaders, agent, globalParams, params, opts }) {
|
|
28
|
-
const requestParams = { ...globalParams, ...params }
|
|
29
|
-
const url = getUrl(method, basePath, requestParams)
|
|
30
|
-
const response = await makeRequestOrRetry({ url, method, defaultHeaders, agent, requestParams, opts })
|
|
31
|
-
|
|
32
|
-
const parsedResponse = await parseResponse(response)
|
|
33
|
-
return parsedResponse
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const getOpts = function ({ method: { verb, parameters }, defaultHeaders, agent, requestParams, opts }) {
|
|
37
|
-
const { body } = requestParams
|
|
38
|
-
const optsA = addHttpMethod(verb, opts)
|
|
39
|
-
const optsB = addHeaderParams(parameters, requestParams, optsA)
|
|
40
|
-
const optsC = addDefaultHeaders(defaultHeaders, optsB)
|
|
41
|
-
const optsD = addBody(body, parameters, optsC)
|
|
42
|
-
const optsE = addAgent(agent, optsD)
|
|
43
|
-
return optsE
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Add header parameters
|
|
47
|
-
const addHeaderParams = function (parameters, requestParams, opts) {
|
|
48
|
-
if (parameters.header === undefined) {
|
|
49
|
-
return opts
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return { ...opts, headers: getRequestParams(parameters.header, requestParams, 'header parameter') }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Add the HTTP method based on the OpenAPI definition
|
|
56
|
-
const addHttpMethod = function (verb, opts) {
|
|
57
|
-
return { ...opts, method: verb.toUpperCase() }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Assign default HTTP headers
|
|
61
|
-
const addDefaultHeaders = function (defaultHeaders, opts) {
|
|
62
|
-
return { ...opts, headers: { ...defaultHeaders, ...opts.headers } }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Assign fetch agent (like for example HttpsProxyAgent) if there is one
|
|
66
|
-
const addAgent = function (agent, opts) {
|
|
67
|
-
if (agent) {
|
|
68
|
-
return { ...opts, agent }
|
|
69
|
-
}
|
|
70
|
-
return opts
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const makeRequestOrRetry = async function ({ url, method, defaultHeaders, agent, requestParams, opts }) {
|
|
74
|
-
// Using a loop is simpler here
|
|
75
|
-
for (let index = 0; index <= MAX_RETRY; index++) {
|
|
76
|
-
const optsA = getOpts({ method, defaultHeaders, agent, requestParams, opts })
|
|
77
|
-
const { response, error } = await makeRequest(url, optsA)
|
|
78
|
-
|
|
79
|
-
if (shouldRetry({ response, error, method }) && index !== MAX_RETRY) {
|
|
80
|
-
await waitForRetry(response)
|
|
81
|
-
continue
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (error !== undefined) {
|
|
85
|
-
throw error
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return response
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const makeRequest = async function (url, opts) {
|
|
93
|
-
try {
|
|
94
|
-
const response = await fetch(url, opts)
|
|
95
|
-
return { response }
|
|
96
|
-
} catch (error) {
|
|
97
|
-
const errorA = getFetchError(error, url, opts)
|
|
98
|
-
return { error: errorA }
|
|
99
|
-
}
|
|
100
|
-
}
|
package/src/methods/params.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import camelCase from 'lodash.camelcase'
|
|
2
|
-
|
|
3
|
-
export const getRequestParams = function (params, requestParams, name) {
|
|
4
|
-
const entries = Object.values(params).map((param) => getRequestParam(param, requestParams, name))
|
|
5
|
-
return Object.assign({}, ...entries)
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const getRequestParam = function (param, requestParams, name) {
|
|
9
|
-
const value = requestParams[param.name] || requestParams[camelCase(param.name)]
|
|
10
|
-
|
|
11
|
-
if (value !== undefined) {
|
|
12
|
-
return { [param.name]: value }
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (param.required) {
|
|
16
|
-
throw new Error(`Missing required ${name} '${param.name}'`)
|
|
17
|
-
}
|
|
18
|
-
}
|
package/src/methods/response.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { JSONHTTPError, TextHTTPError } from 'micro-api-client'
|
|
2
|
-
import omit from 'omit.js'
|
|
3
|
-
|
|
4
|
-
// Read and parse the HTTP response
|
|
5
|
-
export const parseResponse = async function (response) {
|
|
6
|
-
const responseType = getResponseType(response)
|
|
7
|
-
const textResponse = await response.text()
|
|
8
|
-
|
|
9
|
-
const parsedResponse = parseJsonResponse(response, textResponse, responseType)
|
|
10
|
-
|
|
11
|
-
if (!response.ok) {
|
|
12
|
-
const ErrorType = responseType === 'json' ? JSONHTTPError : TextHTTPError
|
|
13
|
-
throw addFallbackErrorMessage(new ErrorType(response, parsedResponse), textResponse)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return parsedResponse
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const getResponseType = function ({ headers }) {
|
|
20
|
-
const contentType = headers.get('Content-Type')
|
|
21
|
-
|
|
22
|
-
if (contentType != null && contentType.includes('json')) {
|
|
23
|
-
return 'json'
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return 'text'
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const parseJsonResponse = function (response, textResponse, responseType) {
|
|
30
|
-
if (responseType === 'text') {
|
|
31
|
-
return textResponse
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
return JSON.parse(textResponse)
|
|
36
|
-
} catch {
|
|
37
|
-
throw addFallbackErrorMessage(new TextHTTPError(response, textResponse), textResponse)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const addFallbackErrorMessage = function (error, textResponse) {
|
|
42
|
-
error.message = error.message || textResponse
|
|
43
|
-
return error
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const getFetchError = function (error, url, opts) {
|
|
47
|
-
const data = omit.default(opts, ['Authorization'])
|
|
48
|
-
if (error.name !== 'FetchError') {
|
|
49
|
-
error.name = 'FetchError'
|
|
50
|
-
}
|
|
51
|
-
error.url = url
|
|
52
|
-
error.data = data
|
|
53
|
-
return error
|
|
54
|
-
}
|
package/src/methods/retry.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// We retry:
|
|
2
|
-
// - when receiving a rate limiting response
|
|
3
|
-
// - on network failures due to timeouts
|
|
4
|
-
export const shouldRetry = function ({ response = {}, error = {}, method = {} }) {
|
|
5
|
-
if (response.status === RATE_LIMIT_STATUS || RETRY_ERROR_CODES.has(error.code)) {
|
|
6
|
-
return true
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// Special case for the `getLatestPluginRuns` endpoint.
|
|
10
|
-
// See https://github.com/netlify/bitballoon/issues/9616.
|
|
11
|
-
if (method.operationId === 'getLatestPluginRuns' && response.status === 500) {
|
|
12
|
-
return true
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return false
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const waitForRetry = async function (response) {
|
|
19
|
-
const delay = getDelay(response)
|
|
20
|
-
await sleep(delay)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const getDelay = function (response) {
|
|
24
|
-
if (response === undefined) {
|
|
25
|
-
return DEFAULT_RETRY_DELAY
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const rateLimitReset = response.headers.get(RATE_LIMIT_HEADER)
|
|
29
|
-
|
|
30
|
-
if (!rateLimitReset) {
|
|
31
|
-
return DEFAULT_RETRY_DELAY
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return Math.max(Number(rateLimitReset) * SECS_TO_MSECS - Date.now(), MIN_RETRY_DELAY)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const sleep = function (ms) {
|
|
38
|
-
return new Promise((resolve) => {
|
|
39
|
-
setTimeout(resolve, ms)
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const DEFAULT_RETRY_DELAY = 5e3
|
|
44
|
-
const MIN_RETRY_DELAY = 1e3
|
|
45
|
-
const SECS_TO_MSECS = 1e3
|
|
46
|
-
export const MAX_RETRY = 5
|
|
47
|
-
const RATE_LIMIT_STATUS = 429
|
|
48
|
-
const RATE_LIMIT_HEADER = 'X-RateLimit-Reset'
|
|
49
|
-
const RETRY_ERROR_CODES = new Set(['ETIMEDOUT', 'ECONNRESET'])
|
package/src/methods/url.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import queryString from 'qs'
|
|
2
|
-
|
|
3
|
-
import { getRequestParams } from './params.js'
|
|
4
|
-
|
|
5
|
-
// Replace path parameters and query parameters in the URI, using the OpenAPI
|
|
6
|
-
// definition
|
|
7
|
-
export const getUrl = function ({ path, parameters }, basePath, requestParams) {
|
|
8
|
-
const url = `${basePath}${path}`
|
|
9
|
-
const urlA = addPathParams(url, parameters, requestParams)
|
|
10
|
-
const urlB = addQueryParams(urlA, parameters, requestParams)
|
|
11
|
-
return urlB
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const addPathParams = function (url, parameters, requestParams) {
|
|
15
|
-
const pathParams = getRequestParams(parameters.path, requestParams, 'path variable')
|
|
16
|
-
return Object.entries(pathParams).reduce(addPathParam, url)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const addPathParam = function (url, [name, value]) {
|
|
20
|
-
return url.replace(`{${name}}`, value)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const addQueryParams = function (url, parameters, requestParams) {
|
|
24
|
-
const queryParams = getRequestParams(parameters.query, requestParams, 'query variable')
|
|
25
|
-
|
|
26
|
-
if (Object.keys(queryParams).length === 0) {
|
|
27
|
-
return url
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return `${url}?${queryString.stringify(queryParams, { arrayFormat: 'brackets' })}`
|
|
31
|
-
}
|
package/src/open_api.js
DELETED
package/src/operations.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import omit from 'omit.js'
|
|
2
|
-
|
|
3
|
-
import { openApiSpec } from './open_api.js'
|
|
4
|
-
|
|
5
|
-
// Retrieve all OpenAPI operations
|
|
6
|
-
export const getOperations = function () {
|
|
7
|
-
return Object.entries(openApiSpec.paths).flatMap(([path, pathItem]) => {
|
|
8
|
-
const operations = omit.default(pathItem, ['parameters'])
|
|
9
|
-
return Object.entries(operations).map(([method, operation]) => {
|
|
10
|
-
const parameters = getParameters(pathItem.parameters, operation.parameters)
|
|
11
|
-
return { ...operation, verb: method, path, parameters }
|
|
12
|
-
})
|
|
13
|
-
})
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const getParameters = function (pathParameters = [], operationParameters = []) {
|
|
17
|
-
const parameters = [...pathParameters, ...operationParameters]
|
|
18
|
-
return parameters.reduce(addParameter, { path: {}, query: {}, body: {} })
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const addParameter = function (parameters, param) {
|
|
22
|
-
return { ...parameters, [param.in]: { ...parameters[param.in], [param.name]: param } }
|
|
23
|
-
}
|