@aws-amplify/api-rest 4.4.2-unstable.aae2834.0 → 4.5.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/dist/cjs/apis/common/internalPost.js +1 -1
- package/dist/cjs/apis/common/internalPost.js.map +1 -1
- package/dist/cjs/apis/common/publicApis.js +32 -22
- package/dist/cjs/apis/common/publicApis.js.map +1 -1
- package/dist/cjs/utils/createCancellableOperation.js +34 -24
- package/dist/cjs/utils/createCancellableOperation.js.map +1 -1
- package/dist/esm/apis/common/internalPost.mjs +1 -1
- package/dist/esm/apis/common/internalPost.mjs.map +1 -1
- package/dist/esm/apis/common/publicApis.mjs +32 -22
- package/dist/esm/apis/common/publicApis.mjs.map +1 -1
- package/dist/esm/types/index.d.ts +4 -0
- package/dist/esm/utils/createCancellableOperation.d.ts +2 -2
- package/dist/esm/utils/createCancellableOperation.mjs +34 -24
- package/dist/esm/utils/createCancellableOperation.mjs.map +1 -1
- package/package.json +93 -93
- package/src/apis/common/internalPost.ts +20 -16
- package/src/apis/common/publicApis.ts +53 -39
- package/src/types/index.ts +4 -0
- package/src/utils/createCancellableOperation.ts +44 -34
|
@@ -62,7 +62,7 @@ const post = (amplify, { url, options, abortController }) => {
|
|
|
62
62
|
},
|
|
63
63
|
}, isIamAuthApplicable_1.isIamAuthApplicableForGraphQL, options?.signingServiceInfo);
|
|
64
64
|
return response;
|
|
65
|
-
}, controller);
|
|
65
|
+
}, controller, 'internal');
|
|
66
66
|
const responseWithCleanUp = responsePromise.finally(() => {
|
|
67
67
|
cancelTokenMap.delete(responseWithCleanUp);
|
|
68
68
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internalPost.js","sources":["../../../../src/apis/common/internalPost.ts"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.updateRequestToBeCancellable = exports.cancel = exports.post = void 0;\nconst utils_1 = require(\"../../utils\");\nconst isIamAuthApplicable_1 = require(\"../../utils/isIamAuthApplicable\");\nconst transferHandler_1 = require(\"./transferHandler\");\n/**\n * This weak map provides functionality to cancel a request given the promise containing the `post` request.\n *\n * 1. For every GraphQL POST request, an abort controller is created and supplied to the request.\n * 2. The promise fulfilled by GraphGL POST request is then mapped to that abort controller.\n * 3. The promise is returned to the external caller.\n * 4. The caller can either wait for the promise to fulfill or call `cancel(promise)` to cancel the request.\n * 5. If `cancel(promise)` is called, then the corresponding abort controller is retrieved from the map below.\n * 6. GraphQL POST request will be rejected with the error message provided during cancel.\n * 7. Caller can check if the error is because of cancelling by calling `isCancelError(error)`.\n */\nconst cancelTokenMap = new WeakMap();\n/**\n * @internal\n *\n * REST POST handler to send GraphQL request to given endpoint. By default, it will use IAM to authorize\n * the request. In some auth modes, the IAM auth has to be disabled. Here's how to set up the request auth correctly:\n * * If auth mode is 'iam', you MUST NOT set 'authorization' header and 'x-api-key' header, since it would disable IAM\n * auth. You MUST also set 'input.options.signingServiceInfo' option.\n * * The including 'input.options.signingServiceInfo.service' and 'input.options.signingServiceInfo.region' are\n * optional. If omitted, the signing service and region will be inferred from url.\n * * If auth mode is 'none', you MUST NOT set 'options.signingServiceInfo' option.\n * * If auth mode is 'apiKey', you MUST set 'x-api-key' custom header.\n * * If auth mode is 'oidc' or 'lambda' or 'userPool', you MUST set 'authorization' header.\n *\n * To make the internal post cancellable, you must also call `updateRequestToBeCancellable()` with the promise from\n * internal post call and the abort controller supplied to the internal post call.\n *\n * @param amplify the AmplifyClassV6 instance - it may be the singleton used on Web, or an instance created within\n * a context created by `runWithAmplifyServerContext`\n * @param postInput an object of {@link InternalPostInput}\n * @param postInput.url The URL that the POST request sends to\n * @param postInput.options Options of the POST request\n * @param postInput.abortController The abort controller used to cancel the POST request\n * @returns a {@link RestApiResponse}\n *\n * @throws an {@link AmplifyError} with `Network Error` as the `message` when the external resource is unreachable due to one\n * of the following reasons:\n * 1. no network connection\n * 2. CORS error\n * @throws a {@link CanceledError} when the ongoing POST request get cancelled\n */\nconst post = (amplify, { url, options, abortController }) => {\n const controller = abortController ?? new AbortController();\n const responsePromise = (0, utils_1.createCancellableOperation)(async () => {\n const response = (0, transferHandler_1.transferHandler)(amplify, {\n url,\n method: 'POST',\n ...options,\n abortSignal: controller.signal,\n retryStrategy: {\n strategy: 'jittered-exponential-backoff',\n },\n }, isIamAuthApplicable_1.isIamAuthApplicableForGraphQL, options?.signingServiceInfo);\n return response;\n }, controller);\n const responseWithCleanUp = responsePromise.finally(() => {\n cancelTokenMap.delete(responseWithCleanUp);\n });\n return responseWithCleanUp;\n};\nexports.post = post;\n/**\n * Cancels a request given the promise returned by `post`.\n * If the request is already completed, this function does nothing.\n * It MUST be used after `updateRequestToBeCancellable` is called.\n */\nconst cancel = (promise, message) => {\n const controller = cancelTokenMap.get(promise);\n if (controller) {\n controller.abort(message);\n if (message && controller.signal.reason !== message) {\n // In runtimes where `AbortSignal.reason` is not supported, we track the reason ourselves.\n // @ts-expect-error reason is read-only property.\n controller.signal.reason = message;\n }\n return true;\n }\n return false;\n};\nexports.cancel = cancel;\n/**\n * MUST be used to make a promise including internal `post` API call cancellable.\n */\nconst updateRequestToBeCancellable = (promise, controller) => {\n cancelTokenMap.set(promise, controller);\n};\nexports.updateRequestToBeCancellable = updateRequestToBeCancellable;\n"],"names":[],"mappings":";;AACA;AACA;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7D,OAAO,CAAC,4BAA4B,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM;AAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;AACtC,MAAM,qBAAqB,GAAG,OAAO,CAAC,iCAAiC,CAAC;AACxE,MAAM,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC;AACtD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,cAAc,GAAG,IAAI,OAAO,EAAE;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK;AAC7D,IAAI,MAAM,UAAU,GAAG,eAAe,IAAI,IAAI,eAAe,EAAE;AAC/D,IAAI,MAAM,eAAe,GAAG,IAAI,OAAO,CAAC,0BAA0B,EAAE,YAAY;AAChF,QAAQ,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,eAAe,EAAE,OAAO,EAAE;AACzE,YAAY,GAAG;AACf,YAAY,MAAM,EAAE,MAAM;AAC1B,YAAY,GAAG,OAAO;AACtB,YAAY,WAAW,EAAE,UAAU,CAAC,MAAM;AAC1C,YAAY,aAAa,EAAE;AAC3B,gBAAgB,QAAQ,EAAE,8BAA8B;AACxD,aAAa;AACb,SAAS,EAAE,qBAAqB,CAAC,6BAA6B,EAAE,OAAO,EAAE,kBAAkB,CAAC;AAC5F,QAAQ,OAAO,QAAQ;AACvB,IAAI,CAAC,EAAE,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"internalPost.js","sources":["../../../../src/apis/common/internalPost.ts"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.updateRequestToBeCancellable = exports.cancel = exports.post = void 0;\nconst utils_1 = require(\"../../utils\");\nconst isIamAuthApplicable_1 = require(\"../../utils/isIamAuthApplicable\");\nconst transferHandler_1 = require(\"./transferHandler\");\n/**\n * This weak map provides functionality to cancel a request given the promise containing the `post` request.\n *\n * 1. For every GraphQL POST request, an abort controller is created and supplied to the request.\n * 2. The promise fulfilled by GraphGL POST request is then mapped to that abort controller.\n * 3. The promise is returned to the external caller.\n * 4. The caller can either wait for the promise to fulfill or call `cancel(promise)` to cancel the request.\n * 5. If `cancel(promise)` is called, then the corresponding abort controller is retrieved from the map below.\n * 6. GraphQL POST request will be rejected with the error message provided during cancel.\n * 7. Caller can check if the error is because of cancelling by calling `isCancelError(error)`.\n */\nconst cancelTokenMap = new WeakMap();\n/**\n * @internal\n *\n * REST POST handler to send GraphQL request to given endpoint. By default, it will use IAM to authorize\n * the request. In some auth modes, the IAM auth has to be disabled. Here's how to set up the request auth correctly:\n * * If auth mode is 'iam', you MUST NOT set 'authorization' header and 'x-api-key' header, since it would disable IAM\n * auth. You MUST also set 'input.options.signingServiceInfo' option.\n * * The including 'input.options.signingServiceInfo.service' and 'input.options.signingServiceInfo.region' are\n * optional. If omitted, the signing service and region will be inferred from url.\n * * If auth mode is 'none', you MUST NOT set 'options.signingServiceInfo' option.\n * * If auth mode is 'apiKey', you MUST set 'x-api-key' custom header.\n * * If auth mode is 'oidc' or 'lambda' or 'userPool', you MUST set 'authorization' header.\n *\n * To make the internal post cancellable, you must also call `updateRequestToBeCancellable()` with the promise from\n * internal post call and the abort controller supplied to the internal post call.\n *\n * @param amplify the AmplifyClassV6 instance - it may be the singleton used on Web, or an instance created within\n * a context created by `runWithAmplifyServerContext`\n * @param postInput an object of {@link InternalPostInput}\n * @param postInput.url The URL that the POST request sends to\n * @param postInput.options Options of the POST request\n * @param postInput.abortController The abort controller used to cancel the POST request\n * @returns a {@link RestApiResponse}\n *\n * @throws an {@link AmplifyError} with `Network Error` as the `message` when the external resource is unreachable due to one\n * of the following reasons:\n * 1. no network connection\n * 2. CORS error\n * @throws a {@link CanceledError} when the ongoing POST request get cancelled\n */\nconst post = (amplify, { url, options, abortController }) => {\n const controller = abortController ?? new AbortController();\n const responsePromise = (0, utils_1.createCancellableOperation)(async () => {\n const response = (0, transferHandler_1.transferHandler)(amplify, {\n url,\n method: 'POST',\n ...options,\n abortSignal: controller.signal,\n retryStrategy: {\n strategy: 'jittered-exponential-backoff',\n },\n }, isIamAuthApplicable_1.isIamAuthApplicableForGraphQL, options?.signingServiceInfo);\n return response;\n }, controller, 'internal');\n const responseWithCleanUp = responsePromise.finally(() => {\n cancelTokenMap.delete(responseWithCleanUp);\n });\n return responseWithCleanUp;\n};\nexports.post = post;\n/**\n * Cancels a request given the promise returned by `post`.\n * If the request is already completed, this function does nothing.\n * It MUST be used after `updateRequestToBeCancellable` is called.\n */\nconst cancel = (promise, message) => {\n const controller = cancelTokenMap.get(promise);\n if (controller) {\n controller.abort(message);\n if (message && controller.signal.reason !== message) {\n // In runtimes where `AbortSignal.reason` is not supported, we track the reason ourselves.\n // @ts-expect-error reason is read-only property.\n controller.signal.reason = message;\n }\n return true;\n }\n return false;\n};\nexports.cancel = cancel;\n/**\n * MUST be used to make a promise including internal `post` API call cancellable.\n */\nconst updateRequestToBeCancellable = (promise, controller) => {\n cancelTokenMap.set(promise, controller);\n};\nexports.updateRequestToBeCancellable = updateRequestToBeCancellable;\n"],"names":[],"mappings":";;AACA;AACA;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7D,OAAO,CAAC,4BAA4B,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM;AAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;AACtC,MAAM,qBAAqB,GAAG,OAAO,CAAC,iCAAiC,CAAC;AACxE,MAAM,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC;AACtD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,cAAc,GAAG,IAAI,OAAO,EAAE;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK;AAC7D,IAAI,MAAM,UAAU,GAAG,eAAe,IAAI,IAAI,eAAe,EAAE;AAC/D,IAAI,MAAM,eAAe,GAAG,IAAI,OAAO,CAAC,0BAA0B,EAAE,YAAY;AAChF,QAAQ,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,eAAe,EAAE,OAAO,EAAE;AACzE,YAAY,GAAG;AACf,YAAY,MAAM,EAAE,MAAM;AAC1B,YAAY,GAAG,OAAO;AACtB,YAAY,WAAW,EAAE,UAAU,CAAC,MAAM;AAC1C,YAAY,aAAa,EAAE;AAC3B,gBAAgB,QAAQ,EAAE,8BAA8B;AACxD,aAAa;AACb,SAAS,EAAE,qBAAqB,CAAC,6BAA6B,EAAE,OAAO,EAAE,kBAAkB,CAAC;AAC5F,QAAQ,OAAO,QAAQ;AACvB,IAAI,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC;AAC9B,IAAI,MAAM,mBAAmB,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM;AAC9D,QAAQ,cAAc,CAAC,MAAM,CAAC,mBAAmB,CAAC;AAClD,IAAI,CAAC,CAAC;AACN,IAAI,OAAO,mBAAmB;AAC9B,CAAC;AACD,OAAO,CAAC,IAAI,GAAG,IAAI;AACnB;AACA;AACA;AACA;AACA;AACA,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK;AACrC,IAAI,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC;AAClD,IAAI,IAAI,UAAU,EAAE;AACpB,QAAQ,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC;AACjC,QAAQ,IAAI,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE;AAC7D;AACA;AACA,YAAY,UAAU,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO;AAC9C,QAAQ;AACR,QAAQ,OAAO,IAAI;AACnB,IAAI;AACJ,IAAI,OAAO,KAAK;AAChB,CAAC;AACD,OAAO,CAAC,MAAM,GAAG,MAAM;AACvB;AACA;AACA;AACA,MAAM,4BAA4B,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK;AAC9D,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC;AAC3C,CAAC;AACD,OAAO,CAAC,4BAA4B,GAAG,4BAA4B;;"}
|
|
@@ -7,31 +7,41 @@ exports.patch = exports.head = exports.del = exports.put = exports.post = export
|
|
|
7
7
|
const utils_1 = require("../../utils");
|
|
8
8
|
const isIamAuthApplicable_1 = require("../../utils/isIamAuthApplicable");
|
|
9
9
|
const transferHandler_1 = require("./transferHandler");
|
|
10
|
-
const publicHandler = (amplify, options, method) =>
|
|
10
|
+
const publicHandler = (amplify, options, method) => {
|
|
11
11
|
const { apiName, options: apiOptions = {}, path: apiPath } = options;
|
|
12
|
-
const
|
|
13
|
-
const libraryConfigHeaders = await amplify.libraryOptions?.API?.REST?.headers?.({
|
|
12
|
+
const libraryConfigTimeout = amplify.libraryOptions?.API?.REST?.timeout?.({
|
|
14
13
|
apiName,
|
|
15
|
-
});
|
|
16
|
-
const { headers: invocationHeaders = {} } = apiOptions;
|
|
17
|
-
const headers = {
|
|
18
|
-
// custom headers from invocation options should precede library options
|
|
19
|
-
...libraryConfigHeaders,
|
|
20
|
-
...invocationHeaders,
|
|
21
|
-
};
|
|
22
|
-
const signingServiceInfo = (0, utils_1.parseSigningInfo)(url, {
|
|
23
|
-
amplify,
|
|
24
|
-
apiName,
|
|
25
|
-
});
|
|
26
|
-
utils_1.logger.debug(method, url, headers, `IAM signing options: ${JSON.stringify(signingServiceInfo)}`);
|
|
27
|
-
return (0, transferHandler_1.transferHandler)(amplify, {
|
|
28
|
-
...apiOptions,
|
|
29
|
-
url,
|
|
30
14
|
method,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
15
|
+
});
|
|
16
|
+
const timeout = apiOptions?.timeout || libraryConfigTimeout || undefined;
|
|
17
|
+
const publicApisAbortController = new AbortController();
|
|
18
|
+
const abortSignal = publicApisAbortController.signal;
|
|
19
|
+
return (0, utils_1.createCancellableOperation)(async () => {
|
|
20
|
+
const url = (0, utils_1.resolveApiUrl)(amplify, apiName, apiPath, apiOptions?.queryParams);
|
|
21
|
+
const libraryConfigHeaders = await amplify.libraryOptions?.API?.REST?.headers?.({
|
|
22
|
+
apiName,
|
|
23
|
+
});
|
|
24
|
+
const { headers: invocationHeaders = {} } = apiOptions;
|
|
25
|
+
const headers = {
|
|
26
|
+
// custom headers from invocation options should precede library options
|
|
27
|
+
...libraryConfigHeaders,
|
|
28
|
+
...invocationHeaders,
|
|
29
|
+
};
|
|
30
|
+
const signingServiceInfo = (0, utils_1.parseSigningInfo)(url, {
|
|
31
|
+
amplify,
|
|
32
|
+
apiName,
|
|
33
|
+
});
|
|
34
|
+
utils_1.logger.debug(method, url, headers, `IAM signing options: ${JSON.stringify(signingServiceInfo)}`);
|
|
35
|
+
return (0, transferHandler_1.transferHandler)(amplify, {
|
|
36
|
+
...apiOptions,
|
|
37
|
+
url,
|
|
38
|
+
method,
|
|
39
|
+
headers,
|
|
40
|
+
abortSignal,
|
|
41
|
+
}, isIamAuthApplicable_1.isIamAuthApplicableForRest, signingServiceInfo);
|
|
42
|
+
}, publicApisAbortController, 'public', // operation Type
|
|
43
|
+
timeout);
|
|
44
|
+
};
|
|
35
45
|
const get = (amplify, input) => publicHandler(amplify, input, 'GET');
|
|
36
46
|
exports.get = get;
|
|
37
47
|
const post = (amplify, input) => publicHandler(amplify, input, 'POST');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publicApis.js","sources":["../../../../src/apis/common/publicApis.ts"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.patch = exports.head = exports.del = exports.put = exports.post = exports.get = void 0;\nconst utils_1 = require(\"../../utils\");\nconst isIamAuthApplicable_1 = require(\"../../utils/isIamAuthApplicable\");\nconst transferHandler_1 = require(\"./transferHandler\");\nconst publicHandler = (amplify, options, method) =>
|
|
1
|
+
{"version":3,"file":"publicApis.js","sources":["../../../../src/apis/common/publicApis.ts"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.patch = exports.head = exports.del = exports.put = exports.post = exports.get = void 0;\nconst utils_1 = require(\"../../utils\");\nconst isIamAuthApplicable_1 = require(\"../../utils/isIamAuthApplicable\");\nconst transferHandler_1 = require(\"./transferHandler\");\nconst publicHandler = (amplify, options, method) => {\n const { apiName, options: apiOptions = {}, path: apiPath } = options;\n const libraryConfigTimeout = amplify.libraryOptions?.API?.REST?.timeout?.({\n apiName,\n method,\n });\n const timeout = apiOptions?.timeout || libraryConfigTimeout || undefined;\n const publicApisAbortController = new AbortController();\n const abortSignal = publicApisAbortController.signal;\n return (0, utils_1.createCancellableOperation)(async () => {\n const url = (0, utils_1.resolveApiUrl)(amplify, apiName, apiPath, apiOptions?.queryParams);\n const libraryConfigHeaders = await amplify.libraryOptions?.API?.REST?.headers?.({\n apiName,\n });\n const { headers: invocationHeaders = {} } = apiOptions;\n const headers = {\n // custom headers from invocation options should precede library options\n ...libraryConfigHeaders,\n ...invocationHeaders,\n };\n const signingServiceInfo = (0, utils_1.parseSigningInfo)(url, {\n amplify,\n apiName,\n });\n utils_1.logger.debug(method, url, headers, `IAM signing options: ${JSON.stringify(signingServiceInfo)}`);\n return (0, transferHandler_1.transferHandler)(amplify, {\n ...apiOptions,\n url,\n method,\n headers,\n abortSignal,\n }, isIamAuthApplicable_1.isIamAuthApplicableForRest, signingServiceInfo);\n }, publicApisAbortController, 'public', // operation Type\n timeout);\n};\nconst get = (amplify, input) => publicHandler(amplify, input, 'GET');\nexports.get = get;\nconst post = (amplify, input) => publicHandler(amplify, input, 'POST');\nexports.post = post;\nconst put = (amplify, input) => publicHandler(amplify, input, 'PUT');\nexports.put = put;\nconst del = (amplify, input) => publicHandler(amplify, input, 'DELETE');\nexports.del = del;\nconst head = (amplify, input) => publicHandler(amplify, input, 'HEAD');\nexports.head = head;\nconst patch = (amplify, input) => publicHandler(amplify, input, 'PATCH');\nexports.patch = patch;\n"],"names":[],"mappings":"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"}
|
|
@@ -10,53 +10,63 @@ const logger_1 = require("./logger");
|
|
|
10
10
|
/**
|
|
11
11
|
* @internal
|
|
12
12
|
*/
|
|
13
|
-
function createCancellableOperation(handler, abortController) {
|
|
14
|
-
const
|
|
15
|
-
// For creating a cancellable operation for public REST APIs, we need to create an AbortController
|
|
16
|
-
// internally. Whereas for internal POST APIs, we need to accept in the AbortController from the
|
|
17
|
-
// callers.
|
|
18
|
-
const publicApisAbortController = new AbortController();
|
|
19
|
-
const publicApisAbortSignal = publicApisAbortController.signal;
|
|
20
|
-
const internalPostAbortSignal = abortController?.signal;
|
|
13
|
+
function createCancellableOperation(handler, abortController, operationType, timeout) {
|
|
14
|
+
const abortSignal = abortController.signal;
|
|
21
15
|
let abortReason;
|
|
16
|
+
if (timeout != null) {
|
|
17
|
+
if (timeout < 0) {
|
|
18
|
+
throw new Error('Timeout must be a non-negative number');
|
|
19
|
+
}
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
abortReason = 'TimeoutError';
|
|
22
|
+
abortController.abort(abortReason);
|
|
23
|
+
}, timeout);
|
|
24
|
+
}
|
|
22
25
|
const job = async () => {
|
|
23
26
|
try {
|
|
24
|
-
const response = await (
|
|
25
|
-
? handler()
|
|
26
|
-
: handler(publicApisAbortSignal));
|
|
27
|
+
const response = await handler();
|
|
27
28
|
if (response.statusCode >= 300) {
|
|
28
29
|
throw await (0, serviceError_1.parseRestApiServiceError)(response);
|
|
29
30
|
}
|
|
30
31
|
return response;
|
|
31
32
|
}
|
|
32
33
|
catch (error) {
|
|
33
|
-
const abortSignal = internalPostAbortSignal ?? publicApisAbortSignal;
|
|
34
|
-
const message = abortReason ?? abortSignal.reason;
|
|
35
34
|
if (error.name === 'AbortError' || abortSignal?.aborted === true) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
// Check if timeout caused the abort
|
|
36
|
+
const isTimeout = abortReason && abortReason === 'TimeoutError';
|
|
37
|
+
if (isTimeout) {
|
|
38
|
+
const timeoutError = new Error(`Request timeout after ${timeout}ms`);
|
|
39
|
+
timeoutError.name = 'TimeoutError';
|
|
40
|
+
logger_1.logger.debug(timeoutError);
|
|
41
|
+
throw timeoutError;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const message = abortReason ?? abortSignal.reason;
|
|
45
|
+
const canceledError = new errors_1.CanceledError({
|
|
46
|
+
...(message && { message }),
|
|
47
|
+
underlyingError: error,
|
|
48
|
+
recoverySuggestion: 'The API request was explicitly canceled. If this is not intended, validate if you called the `cancel()` function on the API request erroneously.',
|
|
49
|
+
});
|
|
50
|
+
logger_1.logger.debug(canceledError);
|
|
51
|
+
throw canceledError;
|
|
52
|
+
}
|
|
43
53
|
}
|
|
44
54
|
logger_1.logger.debug(error);
|
|
45
55
|
throw error;
|
|
46
56
|
}
|
|
47
57
|
};
|
|
48
|
-
if (
|
|
58
|
+
if (operationType === 'internal') {
|
|
49
59
|
return job();
|
|
50
60
|
}
|
|
51
61
|
else {
|
|
52
62
|
const cancel = (abortMessage) => {
|
|
53
|
-
if (
|
|
63
|
+
if (abortSignal.aborted === true) {
|
|
54
64
|
return;
|
|
55
65
|
}
|
|
56
|
-
|
|
66
|
+
abortController.abort(abortMessage);
|
|
57
67
|
// If abort reason is not supported, set a scoped reasons instead. The reason property inside an
|
|
58
68
|
// AbortSignal is a readonly property and trying to set it would throw an error.
|
|
59
|
-
if (abortMessage &&
|
|
69
|
+
if (abortMessage && abortSignal.reason !== abortMessage) {
|
|
60
70
|
abortReason = abortMessage;
|
|
61
71
|
}
|
|
62
72
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createCancellableOperation.js","sources":["../../../src/utils/createCancellableOperation.ts"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.createCancellableOperation = createCancellableOperation;\nconst errors_1 = require(\"../errors\");\nconst serviceError_1 = require(\"./serviceError\");\nconst logger_1 = require(\"./logger\");\n/**\n * @internal\n */\nfunction createCancellableOperation(handler, abortController) {\n const
|
|
1
|
+
{"version":3,"file":"createCancellableOperation.js","sources":["../../../src/utils/createCancellableOperation.ts"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.createCancellableOperation = createCancellableOperation;\nconst errors_1 = require(\"../errors\");\nconst serviceError_1 = require(\"./serviceError\");\nconst logger_1 = require(\"./logger\");\n/**\n * @internal\n */\nfunction createCancellableOperation(handler, abortController, operationType, timeout) {\n const abortSignal = abortController.signal;\n let abortReason;\n if (timeout != null) {\n if (timeout < 0) {\n throw new Error('Timeout must be a non-negative number');\n }\n setTimeout(() => {\n abortReason = 'TimeoutError';\n abortController.abort(abortReason);\n }, timeout);\n }\n const job = async () => {\n try {\n const response = await handler();\n if (response.statusCode >= 300) {\n throw await (0, serviceError_1.parseRestApiServiceError)(response);\n }\n return response;\n }\n catch (error) {\n if (error.name === 'AbortError' || abortSignal?.aborted === true) {\n // Check if timeout caused the abort\n const isTimeout = abortReason && abortReason === 'TimeoutError';\n if (isTimeout) {\n const timeoutError = new Error(`Request timeout after ${timeout}ms`);\n timeoutError.name = 'TimeoutError';\n logger_1.logger.debug(timeoutError);\n throw timeoutError;\n }\n else {\n const message = abortReason ?? abortSignal.reason;\n const canceledError = new errors_1.CanceledError({\n ...(message && { message }),\n underlyingError: error,\n recoverySuggestion: 'The API request was explicitly canceled. If this is not intended, validate if you called the `cancel()` function on the API request erroneously.',\n });\n logger_1.logger.debug(canceledError);\n throw canceledError;\n }\n }\n logger_1.logger.debug(error);\n throw error;\n }\n };\n if (operationType === 'internal') {\n return job();\n }\n else {\n const cancel = (abortMessage) => {\n if (abortSignal.aborted === true) {\n return;\n }\n abortController.abort(abortMessage);\n // If abort reason is not supported, set a scoped reasons instead. The reason property inside an\n // AbortSignal is a readonly property and trying to set it would throw an error.\n if (abortMessage && abortSignal.reason !== abortMessage) {\n abortReason = abortMessage;\n }\n };\n return { response: job(), cancel };\n }\n}\n"],"names":[],"mappings":";;AACA;AACA;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7D,OAAO,CAAC,0BAA0B,GAAG,0BAA0B;AAC/D,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;AACrC,MAAM,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;AAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;AACpC;AACA;AACA;AACA,SAAS,0BAA0B,CAAC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,OAAO,EAAE;AACtF,IAAI,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM;AAC9C,IAAI,IAAI,WAAW;AACnB,IAAI,IAAI,OAAO,IAAI,IAAI,EAAE;AACzB,QAAQ,IAAI,OAAO,GAAG,CAAC,EAAE;AACzB,YAAY,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC;AACpE,QAAQ;AACR,QAAQ,UAAU,CAAC,MAAM;AACzB,YAAY,WAAW,GAAG,cAAc;AACxC,YAAY,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC;AAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;AACnB,IAAI;AACJ,IAAI,MAAM,GAAG,GAAG,YAAY;AAC5B,QAAQ,IAAI;AACZ,YAAY,MAAM,QAAQ,GAAG,MAAM,OAAO,EAAE;AAC5C,YAAY,IAAI,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE;AAC5C,gBAAgB,MAAM,MAAM,CAAC,CAAC,EAAE,cAAc,CAAC,wBAAwB,EAAE,QAAQ,CAAC;AAClF,YAAY;AACZ,YAAY,OAAO,QAAQ;AAC3B,QAAQ;AACR,QAAQ,OAAO,KAAK,EAAE;AACtB,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,WAAW,EAAE,OAAO,KAAK,IAAI,EAAE;AAC9E;AACA,gBAAgB,MAAM,SAAS,GAAG,WAAW,IAAI,WAAW,KAAK,cAAc;AAC/E,gBAAgB,IAAI,SAAS,EAAE;AAC/B,oBAAoB,MAAM,YAAY,GAAG,IAAI,KAAK,CAAC,CAAC,sBAAsB,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AACxF,oBAAoB,YAAY,CAAC,IAAI,GAAG,cAAc;AACtD,oBAAoB,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC;AACvD,oBAAoB,MAAM,YAAY;AACtC,gBAAgB;AAChB,qBAAqB;AACrB,oBAAoB,MAAM,OAAO,GAAG,WAAW,IAAI,WAAW,CAAC,MAAM;AACrE,oBAAoB,MAAM,aAAa,GAAG,IAAI,QAAQ,CAAC,aAAa,CAAC;AACrE,wBAAwB,IAAI,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;AACnD,wBAAwB,eAAe,EAAE,KAAK;AAC9C,wBAAwB,kBAAkB,EAAE,kJAAkJ;AAC9L,qBAAqB,CAAC;AACtB,oBAAoB,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC;AACxD,oBAAoB,MAAM,aAAa;AACvC,gBAAgB;AAChB,YAAY;AACZ,YAAY,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;AACxC,YAAY,MAAM,KAAK;AACvB,QAAQ;AACR,IAAI,CAAC;AACL,IAAI,IAAI,aAAa,KAAK,UAAU,EAAE;AACtC,QAAQ,OAAO,GAAG,EAAE;AACpB,IAAI;AACJ,SAAS;AACT,QAAQ,MAAM,MAAM,GAAG,CAAC,YAAY,KAAK;AACzC,YAAY,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI,EAAE;AAC9C,gBAAgB;AAChB,YAAY;AACZ,YAAY,eAAe,CAAC,KAAK,CAAC,YAAY,CAAC;AAC/C;AACA;AACA,YAAY,IAAI,YAAY,IAAI,WAAW,CAAC,MAAM,KAAK,YAAY,EAAE;AACrE,gBAAgB,WAAW,GAAG,YAAY;AAC1C,YAAY;AACZ,QAAQ,CAAC;AACT,QAAQ,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE;AAC1C,IAAI;AACJ;;"}
|
|
@@ -63,7 +63,7 @@ const post = (amplify, { url, options, abortController }) => {
|
|
|
63
63
|
},
|
|
64
64
|
}, isIamAuthApplicableForGraphQL, options?.signingServiceInfo);
|
|
65
65
|
return response;
|
|
66
|
-
}, controller);
|
|
66
|
+
}, controller, 'internal');
|
|
67
67
|
const responseWithCleanUp = responsePromise.finally(() => {
|
|
68
68
|
cancelTokenMap.delete(responseWithCleanUp);
|
|
69
69
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internalPost.mjs","sources":["../../../../src/apis/common/internalPost.ts"],"sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nimport { createCancellableOperation } from '../../utils';\nimport { isIamAuthApplicableForGraphQL } from '../../utils/isIamAuthApplicable';\nimport { transferHandler } from './transferHandler';\n/**\n * This weak map provides functionality to cancel a request given the promise containing the `post` request.\n *\n * 1. For every GraphQL POST request, an abort controller is created and supplied to the request.\n * 2. The promise fulfilled by GraphGL POST request is then mapped to that abort controller.\n * 3. The promise is returned to the external caller.\n * 4. The caller can either wait for the promise to fulfill or call `cancel(promise)` to cancel the request.\n * 5. If `cancel(promise)` is called, then the corresponding abort controller is retrieved from the map below.\n * 6. GraphQL POST request will be rejected with the error message provided during cancel.\n * 7. Caller can check if the error is because of cancelling by calling `isCancelError(error)`.\n */\nconst cancelTokenMap = new WeakMap();\n/**\n * @internal\n *\n * REST POST handler to send GraphQL request to given endpoint. By default, it will use IAM to authorize\n * the request. In some auth modes, the IAM auth has to be disabled. Here's how to set up the request auth correctly:\n * * If auth mode is 'iam', you MUST NOT set 'authorization' header and 'x-api-key' header, since it would disable IAM\n * auth. You MUST also set 'input.options.signingServiceInfo' option.\n * * The including 'input.options.signingServiceInfo.service' and 'input.options.signingServiceInfo.region' are\n * optional. If omitted, the signing service and region will be inferred from url.\n * * If auth mode is 'none', you MUST NOT set 'options.signingServiceInfo' option.\n * * If auth mode is 'apiKey', you MUST set 'x-api-key' custom header.\n * * If auth mode is 'oidc' or 'lambda' or 'userPool', you MUST set 'authorization' header.\n *\n * To make the internal post cancellable, you must also call `updateRequestToBeCancellable()` with the promise from\n * internal post call and the abort controller supplied to the internal post call.\n *\n * @param amplify the AmplifyClassV6 instance - it may be the singleton used on Web, or an instance created within\n * a context created by `runWithAmplifyServerContext`\n * @param postInput an object of {@link InternalPostInput}\n * @param postInput.url The URL that the POST request sends to\n * @param postInput.options Options of the POST request\n * @param postInput.abortController The abort controller used to cancel the POST request\n * @returns a {@link RestApiResponse}\n *\n * @throws an {@link AmplifyError} with `Network Error` as the `message` when the external resource is unreachable due to one\n * of the following reasons:\n * 1. no network connection\n * 2. CORS error\n * @throws a {@link CanceledError} when the ongoing POST request get cancelled\n */\nexport const post = (amplify, { url, options, abortController }) => {\n const controller = abortController ?? new AbortController();\n const responsePromise = createCancellableOperation(async () => {\n const response = transferHandler(amplify, {\n url,\n method: 'POST',\n ...options,\n abortSignal: controller.signal,\n retryStrategy: {\n strategy: 'jittered-exponential-backoff',\n },\n }, isIamAuthApplicableForGraphQL, options?.signingServiceInfo);\n return response;\n }, controller);\n const responseWithCleanUp = responsePromise.finally(() => {\n cancelTokenMap.delete(responseWithCleanUp);\n });\n return responseWithCleanUp;\n};\n/**\n * Cancels a request given the promise returned by `post`.\n * If the request is already completed, this function does nothing.\n * It MUST be used after `updateRequestToBeCancellable` is called.\n */\nexport const cancel = (promise, message) => {\n const controller = cancelTokenMap.get(promise);\n if (controller) {\n controller.abort(message);\n if (message && controller.signal.reason !== message) {\n // In runtimes where `AbortSignal.reason` is not supported, we track the reason ourselves.\n // @ts-expect-error reason is read-only property.\n controller.signal.reason = message;\n }\n return true;\n }\n return false;\n};\n/**\n * MUST be used to make a promise including internal `post` API call cancellable.\n */\nexport const updateRequestToBeCancellable = (promise, controller) => {\n cancelTokenMap.set(promise, controller);\n};\n"],"names":[],"mappings":";;;;;;;;AAAA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,cAAc,GAAG,IAAI,OAAO,EAAE;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,IAAI,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK;AACpE,IAAI,MAAM,UAAU,GAAG,eAAe,IAAI,IAAI,eAAe,EAAE;AAC/D,IAAI,MAAM,eAAe,GAAG,0BAA0B,CAAC,YAAY;AACnE,QAAQ,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,EAAE;AAClD,YAAY,GAAG;AACf,YAAY,MAAM,EAAE,MAAM;AAC1B,YAAY,GAAG,OAAO;AACtB,YAAY,WAAW,EAAE,UAAU,CAAC,MAAM;AAC1C,YAAY,aAAa,EAAE;AAC3B,gBAAgB,QAAQ,EAAE,8BAA8B;AACxD,aAAa;AACb,SAAS,EAAE,6BAA6B,EAAE,OAAO,EAAE,kBAAkB,CAAC;AACtE,QAAQ,OAAO,QAAQ;AACvB,IAAI,CAAC,EAAE,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"internalPost.mjs","sources":["../../../../src/apis/common/internalPost.ts"],"sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nimport { createCancellableOperation } from '../../utils';\nimport { isIamAuthApplicableForGraphQL } from '../../utils/isIamAuthApplicable';\nimport { transferHandler } from './transferHandler';\n/**\n * This weak map provides functionality to cancel a request given the promise containing the `post` request.\n *\n * 1. For every GraphQL POST request, an abort controller is created and supplied to the request.\n * 2. The promise fulfilled by GraphGL POST request is then mapped to that abort controller.\n * 3. The promise is returned to the external caller.\n * 4. The caller can either wait for the promise to fulfill or call `cancel(promise)` to cancel the request.\n * 5. If `cancel(promise)` is called, then the corresponding abort controller is retrieved from the map below.\n * 6. GraphQL POST request will be rejected with the error message provided during cancel.\n * 7. Caller can check if the error is because of cancelling by calling `isCancelError(error)`.\n */\nconst cancelTokenMap = new WeakMap();\n/**\n * @internal\n *\n * REST POST handler to send GraphQL request to given endpoint. By default, it will use IAM to authorize\n * the request. In some auth modes, the IAM auth has to be disabled. Here's how to set up the request auth correctly:\n * * If auth mode is 'iam', you MUST NOT set 'authorization' header and 'x-api-key' header, since it would disable IAM\n * auth. You MUST also set 'input.options.signingServiceInfo' option.\n * * The including 'input.options.signingServiceInfo.service' and 'input.options.signingServiceInfo.region' are\n * optional. If omitted, the signing service and region will be inferred from url.\n * * If auth mode is 'none', you MUST NOT set 'options.signingServiceInfo' option.\n * * If auth mode is 'apiKey', you MUST set 'x-api-key' custom header.\n * * If auth mode is 'oidc' or 'lambda' or 'userPool', you MUST set 'authorization' header.\n *\n * To make the internal post cancellable, you must also call `updateRequestToBeCancellable()` with the promise from\n * internal post call and the abort controller supplied to the internal post call.\n *\n * @param amplify the AmplifyClassV6 instance - it may be the singleton used on Web, or an instance created within\n * a context created by `runWithAmplifyServerContext`\n * @param postInput an object of {@link InternalPostInput}\n * @param postInput.url The URL that the POST request sends to\n * @param postInput.options Options of the POST request\n * @param postInput.abortController The abort controller used to cancel the POST request\n * @returns a {@link RestApiResponse}\n *\n * @throws an {@link AmplifyError} with `Network Error` as the `message` when the external resource is unreachable due to one\n * of the following reasons:\n * 1. no network connection\n * 2. CORS error\n * @throws a {@link CanceledError} when the ongoing POST request get cancelled\n */\nexport const post = (amplify, { url, options, abortController }) => {\n const controller = abortController ?? new AbortController();\n const responsePromise = createCancellableOperation(async () => {\n const response = transferHandler(amplify, {\n url,\n method: 'POST',\n ...options,\n abortSignal: controller.signal,\n retryStrategy: {\n strategy: 'jittered-exponential-backoff',\n },\n }, isIamAuthApplicableForGraphQL, options?.signingServiceInfo);\n return response;\n }, controller, 'internal');\n const responseWithCleanUp = responsePromise.finally(() => {\n cancelTokenMap.delete(responseWithCleanUp);\n });\n return responseWithCleanUp;\n};\n/**\n * Cancels a request given the promise returned by `post`.\n * If the request is already completed, this function does nothing.\n * It MUST be used after `updateRequestToBeCancellable` is called.\n */\nexport const cancel = (promise, message) => {\n const controller = cancelTokenMap.get(promise);\n if (controller) {\n controller.abort(message);\n if (message && controller.signal.reason !== message) {\n // In runtimes where `AbortSignal.reason` is not supported, we track the reason ourselves.\n // @ts-expect-error reason is read-only property.\n controller.signal.reason = message;\n }\n return true;\n }\n return false;\n};\n/**\n * MUST be used to make a promise including internal `post` API call cancellable.\n */\nexport const updateRequestToBeCancellable = (promise, controller) => {\n cancelTokenMap.set(promise, controller);\n};\n"],"names":[],"mappings":";;;;;;;;AAAA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,cAAc,GAAG,IAAI,OAAO,EAAE;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,IAAI,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK;AACpE,IAAI,MAAM,UAAU,GAAG,eAAe,IAAI,IAAI,eAAe,EAAE;AAC/D,IAAI,MAAM,eAAe,GAAG,0BAA0B,CAAC,YAAY;AACnE,QAAQ,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,EAAE;AAClD,YAAY,GAAG;AACf,YAAY,MAAM,EAAE,MAAM;AAC1B,YAAY,GAAG,OAAO;AACtB,YAAY,WAAW,EAAE,UAAU,CAAC,MAAM;AAC1C,YAAY,aAAa,EAAE;AAC3B,gBAAgB,QAAQ,EAAE,8BAA8B;AACxD,aAAa;AACb,SAAS,EAAE,6BAA6B,EAAE,OAAO,EAAE,kBAAkB,CAAC;AACtE,QAAQ,OAAO,QAAQ;AACvB,IAAI,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC;AAC9B,IAAI,MAAM,mBAAmB,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM;AAC9D,QAAQ,cAAc,CAAC,MAAM,CAAC,mBAAmB,CAAC;AAClD,IAAI,CAAC,CAAC;AACN,IAAI,OAAO,mBAAmB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,MAAM,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK;AAC5C,IAAI,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC;AAClD,IAAI,IAAI,UAAU,EAAE;AACpB,QAAQ,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC;AACjC,QAAQ,IAAI,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE;AAC7D;AACA;AACA,YAAY,UAAU,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO;AAC9C,QAAQ;AACR,QAAQ,OAAO,IAAI;AACnB,IAAI;AACJ,IAAI,OAAO,KAAK;AAChB;AACA;AACA;AACA;AACY,MAAC,4BAA4B,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK;AACrE,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC;AAC3C;;;;"}
|
|
@@ -10,31 +10,41 @@ import { transferHandler } from './transferHandler.mjs';
|
|
|
10
10
|
|
|
11
11
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
12
12
|
// SPDX-License-Identifier: Apache-2.0
|
|
13
|
-
const publicHandler = (amplify, options, method) =>
|
|
13
|
+
const publicHandler = (amplify, options, method) => {
|
|
14
14
|
const { apiName, options: apiOptions = {}, path: apiPath } = options;
|
|
15
|
-
const
|
|
16
|
-
const libraryConfigHeaders = await amplify.libraryOptions?.API?.REST?.headers?.({
|
|
15
|
+
const libraryConfigTimeout = amplify.libraryOptions?.API?.REST?.timeout?.({
|
|
17
16
|
apiName,
|
|
18
|
-
});
|
|
19
|
-
const { headers: invocationHeaders = {} } = apiOptions;
|
|
20
|
-
const headers = {
|
|
21
|
-
// custom headers from invocation options should precede library options
|
|
22
|
-
...libraryConfigHeaders,
|
|
23
|
-
...invocationHeaders,
|
|
24
|
-
};
|
|
25
|
-
const signingServiceInfo = parseSigningInfo(url, {
|
|
26
|
-
amplify,
|
|
27
|
-
apiName,
|
|
28
|
-
});
|
|
29
|
-
logger.debug(method, url, headers, `IAM signing options: ${JSON.stringify(signingServiceInfo)}`);
|
|
30
|
-
return transferHandler(amplify, {
|
|
31
|
-
...apiOptions,
|
|
32
|
-
url,
|
|
33
17
|
method,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
18
|
+
});
|
|
19
|
+
const timeout = apiOptions?.timeout || libraryConfigTimeout || undefined;
|
|
20
|
+
const publicApisAbortController = new AbortController();
|
|
21
|
+
const abortSignal = publicApisAbortController.signal;
|
|
22
|
+
return createCancellableOperation(async () => {
|
|
23
|
+
const url = resolveApiUrl(amplify, apiName, apiPath, apiOptions?.queryParams);
|
|
24
|
+
const libraryConfigHeaders = await amplify.libraryOptions?.API?.REST?.headers?.({
|
|
25
|
+
apiName,
|
|
26
|
+
});
|
|
27
|
+
const { headers: invocationHeaders = {} } = apiOptions;
|
|
28
|
+
const headers = {
|
|
29
|
+
// custom headers from invocation options should precede library options
|
|
30
|
+
...libraryConfigHeaders,
|
|
31
|
+
...invocationHeaders,
|
|
32
|
+
};
|
|
33
|
+
const signingServiceInfo = parseSigningInfo(url, {
|
|
34
|
+
amplify,
|
|
35
|
+
apiName,
|
|
36
|
+
});
|
|
37
|
+
logger.debug(method, url, headers, `IAM signing options: ${JSON.stringify(signingServiceInfo)}`);
|
|
38
|
+
return transferHandler(amplify, {
|
|
39
|
+
...apiOptions,
|
|
40
|
+
url,
|
|
41
|
+
method,
|
|
42
|
+
headers,
|
|
43
|
+
abortSignal,
|
|
44
|
+
}, isIamAuthApplicableForRest, signingServiceInfo);
|
|
45
|
+
}, publicApisAbortController, 'public', // operation Type
|
|
46
|
+
timeout);
|
|
47
|
+
};
|
|
38
48
|
const get = (amplify, input) => publicHandler(amplify, input, 'GET');
|
|
39
49
|
const post = (amplify, input) => publicHandler(amplify, input, 'POST');
|
|
40
50
|
const put = (amplify, input) => publicHandler(amplify, input, 'PUT');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publicApis.mjs","sources":["../../../../src/apis/common/publicApis.ts"],"sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nimport { createCancellableOperation, logger, parseSigningInfo, resolveApiUrl, } from '../../utils';\nimport { isIamAuthApplicableForRest } from '../../utils/isIamAuthApplicable';\nimport { transferHandler } from './transferHandler';\nconst publicHandler = (amplify, options, method) =>
|
|
1
|
+
{"version":3,"file":"publicApis.mjs","sources":["../../../../src/apis/common/publicApis.ts"],"sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nimport { createCancellableOperation, logger, parseSigningInfo, resolveApiUrl, } from '../../utils';\nimport { isIamAuthApplicableForRest } from '../../utils/isIamAuthApplicable';\nimport { transferHandler } from './transferHandler';\nconst publicHandler = (amplify, options, method) => {\n const { apiName, options: apiOptions = {}, path: apiPath } = options;\n const libraryConfigTimeout = amplify.libraryOptions?.API?.REST?.timeout?.({\n apiName,\n method,\n });\n const timeout = apiOptions?.timeout || libraryConfigTimeout || undefined;\n const publicApisAbortController = new AbortController();\n const abortSignal = publicApisAbortController.signal;\n return createCancellableOperation(async () => {\n const url = resolveApiUrl(amplify, apiName, apiPath, apiOptions?.queryParams);\n const libraryConfigHeaders = await amplify.libraryOptions?.API?.REST?.headers?.({\n apiName,\n });\n const { headers: invocationHeaders = {} } = apiOptions;\n const headers = {\n // custom headers from invocation options should precede library options\n ...libraryConfigHeaders,\n ...invocationHeaders,\n };\n const signingServiceInfo = parseSigningInfo(url, {\n amplify,\n apiName,\n });\n logger.debug(method, url, headers, `IAM signing options: ${JSON.stringify(signingServiceInfo)}`);\n return transferHandler(amplify, {\n ...apiOptions,\n url,\n method,\n headers,\n abortSignal,\n }, isIamAuthApplicableForRest, signingServiceInfo);\n }, publicApisAbortController, 'public', // operation Type\n timeout);\n};\nexport const get = (amplify, input) => publicHandler(amplify, input, 'GET');\nexport const post = (amplify, input) => publicHandler(amplify, input, 'POST');\nexport const put = (amplify, input) => publicHandler(amplify, input, 'PUT');\nexport const del = (amplify, input) => publicHandler(amplify, input, 'DELETE');\nexport const head = (amplify, input) => publicHandler(amplify, input, 'HEAD');\nexport const patch = (amplify, input) => publicHandler(amplify, input, 'PATCH');\n"],"names":[],"mappings":";;;;;;;;;;AAAA;AACA;AAIA,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK;AACpD,IAAI,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO;AACxE,IAAI,MAAM,oBAAoB,GAAG,OAAO,CAAC,cAAc,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG;AAC9E,QAAQ,OAAO;AACf,QAAQ,MAAM;AACd,KAAK,CAAC;AACN,IAAI,MAAM,OAAO,GAAG,UAAU,EAAE,OAAO,IAAI,oBAAoB,IAAI,SAAS;AAC5E,IAAI,MAAM,yBAAyB,GAAG,IAAI,eAAe,EAAE;AAC3D,IAAI,MAAM,WAAW,GAAG,yBAAyB,CAAC,MAAM;AACxD,IAAI,OAAO,0BAA0B,CAAC,YAAY;AAClD,QAAQ,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC;AACrF,QAAQ,MAAM,oBAAoB,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG;AACxF,YAAY,OAAO;AACnB,SAAS,CAAC;AACV,QAAQ,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,EAAE,EAAE,GAAG,UAAU;AAC9D,QAAQ,MAAM,OAAO,GAAG;AACxB;AACA,YAAY,GAAG,oBAAoB;AACnC,YAAY,GAAG,iBAAiB;AAChC,SAAS;AACT,QAAQ,MAAM,kBAAkB,GAAG,gBAAgB,CAAC,GAAG,EAAE;AACzD,YAAY,OAAO;AACnB,YAAY,OAAO;AACnB,SAAS,CAAC;AACV,QAAQ,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,qBAAqB,EAAE,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;AACxG,QAAQ,OAAO,eAAe,CAAC,OAAO,EAAE;AACxC,YAAY,GAAG,UAAU;AACzB,YAAY,GAAG;AACf,YAAY,MAAM;AAClB,YAAY,OAAO;AACnB,YAAY,WAAW;AACvB,SAAS,EAAE,0BAA0B,EAAE,kBAAkB,CAAC;AAC1D,IAAI,CAAC,EAAE,yBAAyB,EAAE,QAAQ;AAC1C,IAAI,OAAO,CAAC;AACZ,CAAC;AACW,MAAC,GAAG,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK;AAC9D,MAAC,IAAI,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM;AAChE,MAAC,GAAG,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK;AAC9D,MAAC,GAAG,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ;AACjE,MAAC,IAAI,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM;AAChE,MAAC,KAAK,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO;;;;"}
|
|
@@ -36,6 +36,10 @@ export interface RestApiOptionsBase {
|
|
|
36
36
|
* @default ` { strategy: 'jittered-exponential-backoff' } `
|
|
37
37
|
*/
|
|
38
38
|
retryStrategy?: RetryStrategy;
|
|
39
|
+
/**
|
|
40
|
+
* custom timeout in milliseconds.
|
|
41
|
+
*/
|
|
42
|
+
timeout?: number;
|
|
39
43
|
}
|
|
40
44
|
type Headers = Record<string, string>;
|
|
41
45
|
/**
|
|
@@ -4,9 +4,9 @@ import { Operation } from '../types';
|
|
|
4
4
|
* Create a cancellable operation conforming to the internal POST API interface.
|
|
5
5
|
* @internal
|
|
6
6
|
*/
|
|
7
|
-
export declare function createCancellableOperation(handler: () => Promise<HttpResponse>, abortController: AbortController): Promise<HttpResponse>;
|
|
7
|
+
export declare function createCancellableOperation(handler: () => Promise<HttpResponse>, abortController: AbortController, operationType: 'internal', timeout?: number): Promise<HttpResponse>;
|
|
8
8
|
/**
|
|
9
9
|
* Create a cancellable operation conforming to the external REST API interface.
|
|
10
10
|
* @internal
|
|
11
11
|
*/
|
|
12
|
-
export declare function createCancellableOperation(handler: (
|
|
12
|
+
export declare function createCancellableOperation(handler: () => Promise<HttpResponse>, abortController: AbortController, operationType: 'public', timeout?: number): Operation<HttpResponse>;
|
|
@@ -9,53 +9,63 @@ import { logger } from './logger.mjs';
|
|
|
9
9
|
/**
|
|
10
10
|
* @internal
|
|
11
11
|
*/
|
|
12
|
-
function createCancellableOperation(handler, abortController) {
|
|
13
|
-
const
|
|
14
|
-
// For creating a cancellable operation for public REST APIs, we need to create an AbortController
|
|
15
|
-
// internally. Whereas for internal POST APIs, we need to accept in the AbortController from the
|
|
16
|
-
// callers.
|
|
17
|
-
const publicApisAbortController = new AbortController();
|
|
18
|
-
const publicApisAbortSignal = publicApisAbortController.signal;
|
|
19
|
-
const internalPostAbortSignal = abortController?.signal;
|
|
12
|
+
function createCancellableOperation(handler, abortController, operationType, timeout) {
|
|
13
|
+
const abortSignal = abortController.signal;
|
|
20
14
|
let abortReason;
|
|
15
|
+
if (timeout != null) {
|
|
16
|
+
if (timeout < 0) {
|
|
17
|
+
throw new Error('Timeout must be a non-negative number');
|
|
18
|
+
}
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
abortReason = 'TimeoutError';
|
|
21
|
+
abortController.abort(abortReason);
|
|
22
|
+
}, timeout);
|
|
23
|
+
}
|
|
21
24
|
const job = async () => {
|
|
22
25
|
try {
|
|
23
|
-
const response = await (
|
|
24
|
-
? handler()
|
|
25
|
-
: handler(publicApisAbortSignal));
|
|
26
|
+
const response = await handler();
|
|
26
27
|
if (response.statusCode >= 300) {
|
|
27
28
|
throw await parseRestApiServiceError(response);
|
|
28
29
|
}
|
|
29
30
|
return response;
|
|
30
31
|
}
|
|
31
32
|
catch (error) {
|
|
32
|
-
const abortSignal = internalPostAbortSignal ?? publicApisAbortSignal;
|
|
33
|
-
const message = abortReason ?? abortSignal.reason;
|
|
34
33
|
if (error.name === 'AbortError' || abortSignal?.aborted === true) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
// Check if timeout caused the abort
|
|
35
|
+
const isTimeout = abortReason && abortReason === 'TimeoutError';
|
|
36
|
+
if (isTimeout) {
|
|
37
|
+
const timeoutError = new Error(`Request timeout after ${timeout}ms`);
|
|
38
|
+
timeoutError.name = 'TimeoutError';
|
|
39
|
+
logger.debug(timeoutError);
|
|
40
|
+
throw timeoutError;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const message = abortReason ?? abortSignal.reason;
|
|
44
|
+
const canceledError = new CanceledError({
|
|
45
|
+
...(message && { message }),
|
|
46
|
+
underlyingError: error,
|
|
47
|
+
recoverySuggestion: 'The API request was explicitly canceled. If this is not intended, validate if you called the `cancel()` function on the API request erroneously.',
|
|
48
|
+
});
|
|
49
|
+
logger.debug(canceledError);
|
|
50
|
+
throw canceledError;
|
|
51
|
+
}
|
|
42
52
|
}
|
|
43
53
|
logger.debug(error);
|
|
44
54
|
throw error;
|
|
45
55
|
}
|
|
46
56
|
};
|
|
47
|
-
if (
|
|
57
|
+
if (operationType === 'internal') {
|
|
48
58
|
return job();
|
|
49
59
|
}
|
|
50
60
|
else {
|
|
51
61
|
const cancel = (abortMessage) => {
|
|
52
|
-
if (
|
|
62
|
+
if (abortSignal.aborted === true) {
|
|
53
63
|
return;
|
|
54
64
|
}
|
|
55
|
-
|
|
65
|
+
abortController.abort(abortMessage);
|
|
56
66
|
// If abort reason is not supported, set a scoped reasons instead. The reason property inside an
|
|
57
67
|
// AbortSignal is a readonly property and trying to set it would throw an error.
|
|
58
|
-
if (abortMessage &&
|
|
68
|
+
if (abortMessage && abortSignal.reason !== abortMessage) {
|
|
59
69
|
abortReason = abortMessage;
|
|
60
70
|
}
|
|
61
71
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createCancellableOperation.mjs","sources":["../../../src/utils/createCancellableOperation.ts"],"sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nimport { CanceledError } from '../errors';\nimport { parseRestApiServiceError } from './serviceError';\nimport { logger } from './logger';\n/**\n * @internal\n */\nexport function createCancellableOperation(handler, abortController) {\n const
|
|
1
|
+
{"version":3,"file":"createCancellableOperation.mjs","sources":["../../../src/utils/createCancellableOperation.ts"],"sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nimport { CanceledError } from '../errors';\nimport { parseRestApiServiceError } from './serviceError';\nimport { logger } from './logger';\n/**\n * @internal\n */\nexport function createCancellableOperation(handler, abortController, operationType, timeout) {\n const abortSignal = abortController.signal;\n let abortReason;\n if (timeout != null) {\n if (timeout < 0) {\n throw new Error('Timeout must be a non-negative number');\n }\n setTimeout(() => {\n abortReason = 'TimeoutError';\n abortController.abort(abortReason);\n }, timeout);\n }\n const job = async () => {\n try {\n const response = await handler();\n if (response.statusCode >= 300) {\n throw await parseRestApiServiceError(response);\n }\n return response;\n }\n catch (error) {\n if (error.name === 'AbortError' || abortSignal?.aborted === true) {\n // Check if timeout caused the abort\n const isTimeout = abortReason && abortReason === 'TimeoutError';\n if (isTimeout) {\n const timeoutError = new Error(`Request timeout after ${timeout}ms`);\n timeoutError.name = 'TimeoutError';\n logger.debug(timeoutError);\n throw timeoutError;\n }\n else {\n const message = abortReason ?? abortSignal.reason;\n const canceledError = new CanceledError({\n ...(message && { message }),\n underlyingError: error,\n recoverySuggestion: 'The API request was explicitly canceled. If this is not intended, validate if you called the `cancel()` function on the API request erroneously.',\n });\n logger.debug(canceledError);\n throw canceledError;\n }\n }\n logger.debug(error);\n throw error;\n }\n };\n if (operationType === 'internal') {\n return job();\n }\n else {\n const cancel = (abortMessage) => {\n if (abortSignal.aborted === true) {\n return;\n }\n abortController.abort(abortMessage);\n // If abort reason is not supported, set a scoped reasons instead. The reason property inside an\n // AbortSignal is a readonly property and trying to set it would throw an error.\n if (abortMessage && abortSignal.reason !== abortMessage) {\n abortReason = abortMessage;\n }\n };\n return { response: job(), cancel };\n }\n}\n"],"names":[],"mappings":";;;;;;AAAA;AACA;AAIA;AACA;AACA;AACO,SAAS,0BAA0B,CAAC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,OAAO,EAAE;AAC7F,IAAI,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM;AAC9C,IAAI,IAAI,WAAW;AACnB,IAAI,IAAI,OAAO,IAAI,IAAI,EAAE;AACzB,QAAQ,IAAI,OAAO,GAAG,CAAC,EAAE;AACzB,YAAY,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC;AACpE,QAAQ;AACR,QAAQ,UAAU,CAAC,MAAM;AACzB,YAAY,WAAW,GAAG,cAAc;AACxC,YAAY,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC;AAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;AACnB,IAAI;AACJ,IAAI,MAAM,GAAG,GAAG,YAAY;AAC5B,QAAQ,IAAI;AACZ,YAAY,MAAM,QAAQ,GAAG,MAAM,OAAO,EAAE;AAC5C,YAAY,IAAI,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE;AAC5C,gBAAgB,MAAM,MAAM,wBAAwB,CAAC,QAAQ,CAAC;AAC9D,YAAY;AACZ,YAAY,OAAO,QAAQ;AAC3B,QAAQ;AACR,QAAQ,OAAO,KAAK,EAAE;AACtB,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,WAAW,EAAE,OAAO,KAAK,IAAI,EAAE;AAC9E;AACA,gBAAgB,MAAM,SAAS,GAAG,WAAW,IAAI,WAAW,KAAK,cAAc;AAC/E,gBAAgB,IAAI,SAAS,EAAE;AAC/B,oBAAoB,MAAM,YAAY,GAAG,IAAI,KAAK,CAAC,CAAC,sBAAsB,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AACxF,oBAAoB,YAAY,CAAC,IAAI,GAAG,cAAc;AACtD,oBAAoB,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC;AAC9C,oBAAoB,MAAM,YAAY;AACtC,gBAAgB;AAChB,qBAAqB;AACrB,oBAAoB,MAAM,OAAO,GAAG,WAAW,IAAI,WAAW,CAAC,MAAM;AACrE,oBAAoB,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC;AAC5D,wBAAwB,IAAI,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;AACnD,wBAAwB,eAAe,EAAE,KAAK;AAC9C,wBAAwB,kBAAkB,EAAE,kJAAkJ;AAC9L,qBAAqB,CAAC;AACtB,oBAAoB,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC;AAC/C,oBAAoB,MAAM,aAAa;AACvC,gBAAgB;AAChB,YAAY;AACZ,YAAY,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;AAC/B,YAAY,MAAM,KAAK;AACvB,QAAQ;AACR,IAAI,CAAC;AACL,IAAI,IAAI,aAAa,KAAK,UAAU,EAAE;AACtC,QAAQ,OAAO,GAAG,EAAE;AACpB,IAAI;AACJ,SAAS;AACT,QAAQ,MAAM,MAAM,GAAG,CAAC,YAAY,KAAK;AACzC,YAAY,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI,EAAE;AAC9C,gBAAgB;AAChB,YAAY;AACZ,YAAY,eAAe,CAAC,KAAK,CAAC,YAAY,CAAC;AAC/C;AACA;AACA,YAAY,IAAI,YAAY,IAAI,WAAW,CAAC,MAAM,KAAK,YAAY,EAAE;AACrE,gBAAgB,WAAW,GAAG,YAAY;AAC1C,YAAY;AACZ,QAAQ,CAAC;AACT,QAAQ,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE;AAC1C,IAAI;AACJ;;;;"}
|
package/package.json
CHANGED
|
@@ -1,95 +1,95 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
2
|
+
"name": "@aws-amplify/api-rest",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "4.5.0",
|
|
5
|
+
"description": "Api-rest category of aws-amplify",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.mjs",
|
|
8
|
+
"typings": "./dist/esm/index.d.ts",
|
|
9
|
+
"react-native": "./dist/cjs/index.js",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "npm run lint && jest -w 1 --coverage --logHeapUsage",
|
|
15
|
+
"test:watch": "jest -w 1 --watch",
|
|
16
|
+
"build-with-test": "npm test && npm build",
|
|
17
|
+
"build:umd": "webpack && webpack --config ./webpack.config.dev.js",
|
|
18
|
+
"build:esm-cjs": "rollup --forceExit -c rollup.config.mjs",
|
|
19
|
+
"build:watch": "npm run build:esm-cjs -- --watch",
|
|
20
|
+
"build": "npm run clean && npm run build:esm-cjs && npm run build:umd",
|
|
21
|
+
"clean": "npm run clean:size && rimraf dist lib lib-esm",
|
|
22
|
+
"clean:size": "rimraf dual-publish-tmp tmp*",
|
|
23
|
+
"format": "echo \"Not implemented\"",
|
|
24
|
+
"lint": "eslint '**/*.{ts,tsx}' && npm run ts-coverage",
|
|
25
|
+
"lint:fix": "eslint '**/*.{ts,tsx}' --fix",
|
|
26
|
+
"ts-coverage": "typescript-coverage-report -p ./tsconfig.build.json -t 70.0"
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"react-native": "./dist/cjs/index.js",
|
|
31
|
+
"types": "./dist/esm/index.d.ts",
|
|
32
|
+
"import": "./dist/esm/index.mjs",
|
|
33
|
+
"require": "./dist/cjs/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./server": {
|
|
36
|
+
"types": "./dist/esm/server.d.ts",
|
|
37
|
+
"import": "./dist/esm/server.mjs",
|
|
38
|
+
"require": "./dist/cjs/server.js"
|
|
39
|
+
},
|
|
40
|
+
"./internals": {
|
|
41
|
+
"react-native": "./dist/cjs/internals/index.js",
|
|
42
|
+
"types": "./dist/esm/internals/index.d.ts",
|
|
43
|
+
"import": "./dist/esm/internals/index.mjs",
|
|
44
|
+
"require": "./dist/cjs/internals/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./internals/server": {
|
|
47
|
+
"types": "./dist/esm/internals/server.d.ts",
|
|
48
|
+
"import": "./dist/esm/internals/server.mjs",
|
|
49
|
+
"require": "./dist/cjs/internals/server.js"
|
|
50
|
+
},
|
|
51
|
+
"./package.json": "./package.json"
|
|
52
|
+
},
|
|
53
|
+
"typesVersions": {
|
|
54
|
+
">=4.2": {
|
|
55
|
+
"server": [
|
|
56
|
+
"./dist/esm/server.d.ts"
|
|
57
|
+
],
|
|
58
|
+
"internals": [
|
|
59
|
+
"./dist/esm/internals/index.d.ts"
|
|
60
|
+
],
|
|
61
|
+
"internals/server": [
|
|
62
|
+
"./dist/esm/internals/server.d.ts"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "https://github.com/aws-amplify/amplify-js.git"
|
|
69
|
+
},
|
|
70
|
+
"author": "Amazon Web Services",
|
|
71
|
+
"license": "Apache-2.0",
|
|
72
|
+
"bugs": {
|
|
73
|
+
"url": "https://github.com/aws/aws-amplify/issues"
|
|
74
|
+
},
|
|
75
|
+
"homepage": "https://aws-amplify.github.io/",
|
|
76
|
+
"files": [
|
|
77
|
+
"dist/cjs",
|
|
78
|
+
"dist/esm",
|
|
79
|
+
"src",
|
|
80
|
+
"internals",
|
|
81
|
+
"server"
|
|
82
|
+
],
|
|
83
|
+
"dependencies": {
|
|
84
|
+
"tslib": "^2.5.0"
|
|
85
|
+
},
|
|
86
|
+
"peerDependencies": {
|
|
87
|
+
"@aws-amplify/core": "^6.1.0"
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"@aws-amplify/core": "6.14.0",
|
|
91
|
+
"@aws-amplify/react-native": "1.3.0",
|
|
92
|
+
"@aws-sdk/types": "3.387.0"
|
|
93
|
+
},
|
|
94
|
+
"gitHead": "bcb7c416052db16e7e329b2ac44fadc564274e2b"
|
|
95
95
|
}
|
|
@@ -58,24 +58,28 @@ export const post = (
|
|
|
58
58
|
{ url, options, abortController }: InternalPostInput,
|
|
59
59
|
): Promise<RestApiResponse> => {
|
|
60
60
|
const controller = abortController ?? new AbortController();
|
|
61
|
-
const responsePromise = createCancellableOperation(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
const responsePromise = createCancellableOperation(
|
|
62
|
+
async () => {
|
|
63
|
+
const response = transferHandler(
|
|
64
|
+
amplify,
|
|
65
|
+
{
|
|
66
|
+
url,
|
|
67
|
+
method: 'POST',
|
|
68
|
+
...options,
|
|
69
|
+
abortSignal: controller.signal,
|
|
70
|
+
retryStrategy: {
|
|
71
|
+
strategy: 'jittered-exponential-backoff',
|
|
72
|
+
},
|
|
71
73
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
);
|
|
74
|
+
isIamAuthApplicableForGraphQL,
|
|
75
|
+
options?.signingServiceInfo,
|
|
76
|
+
);
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
return response;
|
|
79
|
+
},
|
|
80
|
+
controller,
|
|
81
|
+
'internal', // operation Type
|
|
82
|
+
);
|
|
79
83
|
|
|
80
84
|
const responseWithCleanUp = responsePromise.finally(() => {
|
|
81
85
|
cancelTokenMap.delete(responseWithCleanUp);
|
|
@@ -33,49 +33,63 @@ const publicHandler = (
|
|
|
33
33
|
amplify: AmplifyClassV6,
|
|
34
34
|
options: ApiInput<RestApiOptionsBase>,
|
|
35
35
|
method: string,
|
|
36
|
-
) =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
) => {
|
|
37
|
+
const { apiName, options: apiOptions = {}, path: apiPath } = options;
|
|
38
|
+
const libraryConfigTimeout = amplify.libraryOptions?.API?.REST?.timeout?.({
|
|
39
|
+
apiName,
|
|
40
|
+
method,
|
|
41
|
+
});
|
|
42
|
+
const timeout = apiOptions?.timeout || libraryConfigTimeout || undefined;
|
|
43
|
+
const publicApisAbortController = new AbortController();
|
|
44
|
+
const abortSignal = publicApisAbortController.signal;
|
|
45
|
+
|
|
46
|
+
return createCancellableOperation(
|
|
47
|
+
async () => {
|
|
48
|
+
const url = resolveApiUrl(
|
|
49
|
+
amplify,
|
|
50
|
+
apiName,
|
|
51
|
+
apiPath,
|
|
52
|
+
apiOptions?.queryParams,
|
|
53
|
+
);
|
|
54
|
+
const libraryConfigHeaders =
|
|
55
|
+
await amplify.libraryOptions?.API?.REST?.headers?.({
|
|
56
|
+
apiName,
|
|
57
|
+
});
|
|
58
|
+
const { headers: invocationHeaders = {} } = apiOptions;
|
|
59
|
+
const headers = {
|
|
60
|
+
// custom headers from invocation options should precede library options
|
|
61
|
+
...libraryConfigHeaders,
|
|
62
|
+
...invocationHeaders,
|
|
63
|
+
};
|
|
64
|
+
const signingServiceInfo = parseSigningInfo(url, {
|
|
65
|
+
amplify,
|
|
47
66
|
apiName,
|
|
48
67
|
});
|
|
49
|
-
|
|
50
|
-
const headers = {
|
|
51
|
-
// custom headers from invocation options should precede library options
|
|
52
|
-
...libraryConfigHeaders,
|
|
53
|
-
...invocationHeaders,
|
|
54
|
-
};
|
|
55
|
-
const signingServiceInfo = parseSigningInfo(url, {
|
|
56
|
-
amplify,
|
|
57
|
-
apiName,
|
|
58
|
-
});
|
|
59
|
-
logger.debug(
|
|
60
|
-
method,
|
|
61
|
-
url,
|
|
62
|
-
headers,
|
|
63
|
-
`IAM signing options: ${JSON.stringify(signingServiceInfo)}`,
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
return transferHandler(
|
|
67
|
-
amplify,
|
|
68
|
-
{
|
|
69
|
-
...apiOptions,
|
|
70
|
-
url,
|
|
68
|
+
logger.debug(
|
|
71
69
|
method,
|
|
70
|
+
url,
|
|
72
71
|
headers,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
`IAM signing options: ${JSON.stringify(signingServiceInfo)}`,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return transferHandler(
|
|
76
|
+
amplify,
|
|
77
|
+
{
|
|
78
|
+
...apiOptions,
|
|
79
|
+
url,
|
|
80
|
+
method,
|
|
81
|
+
headers,
|
|
82
|
+
abortSignal,
|
|
83
|
+
},
|
|
84
|
+
isIamAuthApplicableForRest,
|
|
85
|
+
signingServiceInfo,
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
publicApisAbortController,
|
|
89
|
+
'public', // operation Type
|
|
90
|
+
timeout,
|
|
91
|
+
);
|
|
92
|
+
};
|
|
79
93
|
|
|
80
94
|
export const get = (amplify: AmplifyClassV6, input: GetInput): GetOperation =>
|
|
81
95
|
publicHandler(amplify, input, 'GET');
|
package/src/types/index.ts
CHANGED
|
@@ -41,6 +41,10 @@ export interface RestApiOptionsBase {
|
|
|
41
41
|
* @default ` { strategy: 'jittered-exponential-backoff' } `
|
|
42
42
|
*/
|
|
43
43
|
retryStrategy?: RetryStrategy;
|
|
44
|
+
/**
|
|
45
|
+
* custom timeout in milliseconds.
|
|
46
|
+
*/
|
|
47
|
+
timeout?: number;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
type Headers = Record<string, string>;
|
|
@@ -16,6 +16,8 @@ import { logger } from './logger';
|
|
|
16
16
|
export function createCancellableOperation(
|
|
17
17
|
handler: () => Promise<HttpResponse>,
|
|
18
18
|
abortController: AbortController,
|
|
19
|
+
operationType: 'internal',
|
|
20
|
+
timeout?: number,
|
|
19
21
|
): Promise<HttpResponse>;
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -23,37 +25,36 @@ export function createCancellableOperation(
|
|
|
23
25
|
* @internal
|
|
24
26
|
*/
|
|
25
27
|
export function createCancellableOperation(
|
|
26
|
-
handler: (
|
|
28
|
+
handler: () => Promise<HttpResponse>,
|
|
29
|
+
abortController: AbortController,
|
|
30
|
+
operationType: 'public',
|
|
31
|
+
timeout?: number,
|
|
27
32
|
): Operation<HttpResponse>;
|
|
28
33
|
|
|
29
34
|
/**
|
|
30
35
|
* @internal
|
|
31
36
|
*/
|
|
32
37
|
export function createCancellableOperation(
|
|
33
|
-
handler:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
handler: () => Promise<HttpResponse>,
|
|
39
|
+
abortController: AbortController,
|
|
40
|
+
operationType: 'public' | 'internal',
|
|
41
|
+
timeout?: number,
|
|
37
42
|
): Operation<HttpResponse> | Promise<HttpResponse> {
|
|
38
|
-
const
|
|
39
|
-
targetHandler:
|
|
40
|
-
| ((signal: AbortSignal) => Promise<HttpResponse>)
|
|
41
|
-
| (() => Promise<HttpResponse>),
|
|
42
|
-
): targetHandler is () => Promise<HttpResponse> => !!abortController;
|
|
43
|
-
|
|
44
|
-
// For creating a cancellable operation for public REST APIs, we need to create an AbortController
|
|
45
|
-
// internally. Whereas for internal POST APIs, we need to accept in the AbortController from the
|
|
46
|
-
// callers.
|
|
47
|
-
const publicApisAbortController = new AbortController();
|
|
48
|
-
const publicApisAbortSignal = publicApisAbortController.signal;
|
|
49
|
-
const internalPostAbortSignal = abortController?.signal;
|
|
43
|
+
const abortSignal = abortController.signal;
|
|
50
44
|
let abortReason: string;
|
|
45
|
+
if (timeout != null) {
|
|
46
|
+
if (timeout < 0) {
|
|
47
|
+
throw new Error('Timeout must be a non-negative number');
|
|
48
|
+
}
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
abortReason = 'TimeoutError';
|
|
51
|
+
abortController.abort(abortReason);
|
|
52
|
+
}, timeout);
|
|
53
|
+
}
|
|
51
54
|
|
|
52
55
|
const job = async () => {
|
|
53
56
|
try {
|
|
54
|
-
const response = await (
|
|
55
|
-
? handler()
|
|
56
|
-
: handler(publicApisAbortSignal));
|
|
57
|
+
const response = await handler();
|
|
57
58
|
|
|
58
59
|
if (response.statusCode >= 300) {
|
|
59
60
|
throw await parseRestApiServiceError(response)!;
|
|
@@ -61,34 +62,43 @@ export function createCancellableOperation(
|
|
|
61
62
|
|
|
62
63
|
return response;
|
|
63
64
|
} catch (error: any) {
|
|
64
|
-
const abortSignal = internalPostAbortSignal ?? publicApisAbortSignal;
|
|
65
|
-
const message = abortReason ?? abortSignal.reason;
|
|
66
65
|
if (error.name === 'AbortError' || abortSignal?.aborted === true) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
// Check if timeout caused the abort
|
|
67
|
+
const isTimeout = abortReason && abortReason === 'TimeoutError';
|
|
68
|
+
|
|
69
|
+
if (isTimeout) {
|
|
70
|
+
const timeoutError = new Error(`Request timeout after ${timeout}ms`);
|
|
71
|
+
timeoutError.name = 'TimeoutError';
|
|
72
|
+
logger.debug(timeoutError);
|
|
73
|
+
throw timeoutError;
|
|
74
|
+
} else {
|
|
75
|
+
const message = abortReason ?? abortSignal.reason;
|
|
76
|
+
const canceledError = new CanceledError({
|
|
77
|
+
...(message && { message }),
|
|
78
|
+
underlyingError: error,
|
|
79
|
+
recoverySuggestion:
|
|
80
|
+
'The API request was explicitly canceled. If this is not intended, validate if you called the `cancel()` function on the API request erroneously.',
|
|
81
|
+
});
|
|
82
|
+
logger.debug(canceledError);
|
|
83
|
+
throw canceledError;
|
|
84
|
+
}
|
|
75
85
|
}
|
|
76
86
|
logger.debug(error);
|
|
77
87
|
throw error;
|
|
78
88
|
}
|
|
79
89
|
};
|
|
80
90
|
|
|
81
|
-
if (
|
|
91
|
+
if (operationType === 'internal') {
|
|
82
92
|
return job();
|
|
83
93
|
} else {
|
|
84
94
|
const cancel = (abortMessage?: string) => {
|
|
85
|
-
if (
|
|
95
|
+
if (abortSignal.aborted === true) {
|
|
86
96
|
return;
|
|
87
97
|
}
|
|
88
|
-
|
|
98
|
+
abortController.abort(abortMessage);
|
|
89
99
|
// If abort reason is not supported, set a scoped reasons instead. The reason property inside an
|
|
90
100
|
// AbortSignal is a readonly property and trying to set it would throw an error.
|
|
91
|
-
if (abortMessage &&
|
|
101
|
+
if (abortMessage && abortSignal.reason !== abortMessage) {
|
|
92
102
|
abortReason = abortMessage;
|
|
93
103
|
}
|
|
94
104
|
};
|