From 35c84e3f0006e9fd8c862598a69825fa86c3a8af Mon Sep 17 00:00:00 2001 From: Dan Klco Date: Thu, 17 Aug 2023 14:13:46 -0400 Subject: [PATCH 1/4] feat: adding QueueClient --- contentlake-shared.code-workspace | 3 + package-lock.json | 1288 +++++++++++++++++++--- packages/queue-client/.nycrc.json | 11 + packages/queue-client/.prettierrc | 6 + packages/queue-client/LICENSE.txt | 264 +++++ packages/queue-client/README.md | 24 + packages/queue-client/it/index.test.js | 141 +++ packages/queue-client/package.json | 38 + packages/queue-client/src/index.js | 209 ++++ packages/queue-client/src/mock.js | 28 + packages/queue-client/test/index.test.js | 239 ++++ 11 files changed, 2081 insertions(+), 170 deletions(-) create mode 100644 packages/queue-client/.nycrc.json create mode 100644 packages/queue-client/.prettierrc create mode 100644 packages/queue-client/LICENSE.txt create mode 100644 packages/queue-client/README.md create mode 100644 packages/queue-client/it/index.test.js create mode 100644 packages/queue-client/package.json create mode 100644 packages/queue-client/src/index.js create mode 100644 packages/queue-client/src/mock.js create mode 100644 packages/queue-client/test/index.test.js diff --git a/contentlake-shared.code-workspace b/contentlake-shared.code-workspace index f810230..620a53f 100644 --- a/contentlake-shared.code-workspace +++ b/contentlake-shared.code-workspace @@ -15,6 +15,9 @@ { "path": "packages/functions" }, + { + "path": "packages/queue-client" + }, { "path": "packages/request-handler" }, diff --git a/package-lock.json b/package-lock.json index 145578a..cdfc68e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -92,6 +92,10 @@ "resolved": "packages/logger", "link": true }, + "node_modules/@adobe/contentlake-shared-queue-client": { + "resolved": "packages/queue-client", + "link": true + }, "node_modules/@adobe/contentlake-shared-request-handler": { "resolved": "packages/request-handler", "link": true @@ -2458,230 +2462,1097 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-signing": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-signing": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/protocol-http": "^1.1.0", + "@smithy/signature-v4": "^1.0.1", + "@smithy/types": "^1.1.0", + "@smithy/util-middleware": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.360.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.360.0", + "@aws-sdk/config-resolver": "3.357.0", + "@aws-sdk/credential-provider-node": "3.360.0", + "@aws-sdk/fetch-http-handler": "3.357.0", + "@aws-sdk/hash-node": "3.357.0", + "@aws-sdk/invalid-dependency": "3.357.0", + "@aws-sdk/middleware-content-length": "3.357.0", + "@aws-sdk/middleware-endpoint": "3.357.0", + "@aws-sdk/middleware-host-header": "3.357.0", + "@aws-sdk/middleware-logger": "3.357.0", + "@aws-sdk/middleware-recursion-detection": "3.357.0", + "@aws-sdk/middleware-retry": "3.357.0", + "@aws-sdk/middleware-serde": "3.357.0", + "@aws-sdk/middleware-signing": "3.357.0", + "@aws-sdk/middleware-stack": "3.357.0", + "@aws-sdk/middleware-user-agent": "3.357.0", + "@aws-sdk/node-config-provider": "3.357.0", + "@aws-sdk/node-http-handler": "3.360.0", + "@aws-sdk/smithy-client": "3.360.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/url-parser": "3.357.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.360.0", + "@aws-sdk/util-defaults-mode-node": "3.360.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-retry": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.357.0", + "@aws-sdk/util-user-agent-node": "3.357.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.391.0.tgz", + "integrity": "sha512-DEJEFplvmk2PW6/oNFUxMcpt0Ce2l6V5l4iI6QfWW8wDNOVqYqpHi/9zS31X4tYh9Ap58eg6eUZ/W8J9bkWzJA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.391.0", + "@aws-sdk/credential-provider-node": "3.391.0", + "@aws-sdk/middleware-host-header": "3.391.0", + "@aws-sdk/middleware-logger": "3.391.0", + "@aws-sdk/middleware-recursion-detection": "3.391.0", + "@aws-sdk/middleware-sdk-sqs": "3.391.0", + "@aws-sdk/middleware-signing": "3.391.0", + "@aws-sdk/middleware-user-agent": "3.391.0", + "@aws-sdk/types": "3.391.0", + "@aws-sdk/util-endpoints": "3.391.0", + "@aws-sdk/util-user-agent-browser": "3.391.0", + "@aws-sdk/util-user-agent-node": "3.391.0", + "@smithy/config-resolver": "^2.0.3", + "@smithy/fetch-http-handler": "^2.0.3", + "@smithy/hash-node": "^2.0.3", + "@smithy/invalid-dependency": "^2.0.3", + "@smithy/md5-js": "^2.0.3", + "@smithy/middleware-content-length": "^2.0.3", + "@smithy/middleware-endpoint": "^2.0.3", + "@smithy/middleware-retry": "^2.0.3", + "@smithy/middleware-serde": "^2.0.3", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.3", + "@smithy/node-http-handler": "^2.0.3", + "@smithy/protocol-http": "^2.0.3", + "@smithy/smithy-client": "^2.0.3", + "@smithy/types": "^2.2.0", + "@smithy/url-parser": "^2.0.3", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.0.0", + "@smithy/util-defaults-mode-browser": "^2.0.3", + "@smithy/util-defaults-mode-node": "^2.0.3", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/client-sso": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.391.0.tgz", + "integrity": "sha512-aT+O1CbWIWYlCtWK6g3ZaMvFNImOgFGurOEPscuedqzG5UQc1bRtRrGYShLyzcZgfXP+s0cKYJqgGeRNoWiwqA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.391.0", + "@aws-sdk/middleware-logger": "3.391.0", + "@aws-sdk/middleware-recursion-detection": "3.391.0", + "@aws-sdk/middleware-user-agent": "3.391.0", + "@aws-sdk/types": "3.391.0", + "@aws-sdk/util-endpoints": "3.391.0", + "@aws-sdk/util-user-agent-browser": "3.391.0", + "@aws-sdk/util-user-agent-node": "3.391.0", + "@smithy/config-resolver": "^2.0.3", + "@smithy/fetch-http-handler": "^2.0.3", + "@smithy/hash-node": "^2.0.3", + "@smithy/invalid-dependency": "^2.0.3", + "@smithy/middleware-content-length": "^2.0.3", + "@smithy/middleware-endpoint": "^2.0.3", + "@smithy/middleware-retry": "^2.0.3", + "@smithy/middleware-serde": "^2.0.3", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.3", + "@smithy/node-http-handler": "^2.0.3", + "@smithy/protocol-http": "^2.0.3", + "@smithy/smithy-client": "^2.0.3", + "@smithy/types": "^2.2.0", + "@smithy/url-parser": "^2.0.3", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.0.0", + "@smithy/util-defaults-mode-browser": "^2.0.3", + "@smithy/util-defaults-mode-node": "^2.0.3", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/client-sts": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.391.0.tgz", + "integrity": "sha512-y+KmorcUx9o5O99sXVPbhGUpsLpfhzYRaYCqxArLsyzZTCO6XDXMi8vg/xtS+b703j9lWEl5GxAv2oBaEwEnhQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.391.0", + "@aws-sdk/middleware-host-header": "3.391.0", + "@aws-sdk/middleware-logger": "3.391.0", + "@aws-sdk/middleware-recursion-detection": "3.391.0", + "@aws-sdk/middleware-sdk-sts": "3.391.0", + "@aws-sdk/middleware-signing": "3.391.0", + "@aws-sdk/middleware-user-agent": "3.391.0", + "@aws-sdk/types": "3.391.0", + "@aws-sdk/util-endpoints": "3.391.0", + "@aws-sdk/util-user-agent-browser": "3.391.0", + "@aws-sdk/util-user-agent-node": "3.391.0", + "@smithy/config-resolver": "^2.0.3", + "@smithy/fetch-http-handler": "^2.0.3", + "@smithy/hash-node": "^2.0.3", + "@smithy/invalid-dependency": "^2.0.3", + "@smithy/middleware-content-length": "^2.0.3", + "@smithy/middleware-endpoint": "^2.0.3", + "@smithy/middleware-retry": "^2.0.3", + "@smithy/middleware-serde": "^2.0.3", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.3", + "@smithy/node-http-handler": "^2.0.3", + "@smithy/protocol-http": "^2.0.3", + "@smithy/smithy-client": "^2.0.3", + "@smithy/types": "^2.2.0", + "@smithy/url-parser": "^2.0.3", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.0.0", + "@smithy/util-defaults-mode-browser": "^2.0.3", + "@smithy/util-defaults-mode-node": "^2.0.3", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.391.0.tgz", + "integrity": "sha512-mAzICedcg4bfL0mM5O6QTd9mQ331NLse1DMr6XL21ZZiLB48ej19L7AGV2xq5QwVbqKU3IVv1myRyhvpDM9jMg==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.391.0.tgz", + "integrity": "sha512-DJZmbmRMqNSfSV7UF8eBVhADz16KAMCTxnFuvgioHHfYUTZQEhCxRHI8jJqYWxhLTriS7AuTBIWr+1AIbwsCTA==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.391.0", + "@aws-sdk/credential-provider-process": "3.391.0", + "@aws-sdk/credential-provider-sso": "3.391.0", + "@aws-sdk/credential-provider-web-identity": "3.391.0", + "@aws-sdk/types": "3.391.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.0", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.391.0.tgz", + "integrity": "sha512-LXHQwsTw4WBwRzD9swu8254Hao5MoIaGXIzbhX4EQ84dtOkKYbwiY4pDpLfcHcw3B1lFKkVclMze8WAs4EdEww==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.391.0", + "@aws-sdk/credential-provider-ini": "3.391.0", + "@aws-sdk/credential-provider-process": "3.391.0", + "@aws-sdk/credential-provider-sso": "3.391.0", + "@aws-sdk/credential-provider-web-identity": "3.391.0", + "@aws-sdk/types": "3.391.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.0", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.391.0.tgz", + "integrity": "sha512-KMlzPlBI+hBmXDo+EoFZdLgCVRkRa9B9iEE6x0+hQQ6g9bW6HI7cDRVdceR1ZoPasSaNAZ9QOXMTIBxTpn0sPQ==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.0", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.391.0.tgz", + "integrity": "sha512-FT/WoiRHiKys+FcRwvjui0yKuzNtJdn2uGuI1hYE0gpW1wVmW02ouufLckJTmcw09THUZ4w53OoCVU5OY00p8A==", + "dependencies": { + "@aws-sdk/client-sso": "3.391.0", + "@aws-sdk/token-providers": "3.391.0", + "@aws-sdk/types": "3.391.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.0", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.391.0.tgz", + "integrity": "sha512-n0vYg82B8bc4rxKltVbVqclev7hx+elyS9pEnZs3YbnbWJq0qqsznXmDfLqd1TcWpa09PGXcah0nsRDolVThsA==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.391.0.tgz", + "integrity": "sha512-+nyNr0rb2ixY7mU48nibr7L7gsw37y4oELhqgnNKhcjZDJ34imBwKIMFa64n21FdftmhcjR8IdSpzXE9xrkJ8g==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/protocol-http": "^2.0.3", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-logger": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.391.0.tgz", + "integrity": "sha512-KOwl5zo16b17JDhqILHBStccBQ2w35em7+/6vdkJdUII6OU8aVIFTlIQT9wOUvd4do6biIRBMZG3IK0Rg7mRDQ==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.391.0.tgz", + "integrity": "sha512-hVR3z59G7pX4pjDQs9Ag1tMgbLeGXOzeAAaNP9fEtHSd3KBMAGQgN3K3b9WPjzE2W0EoloHRJMK4qxZErdde2g==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/protocol-http": "^2.0.3", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.391.0.tgz", + "integrity": "sha512-6ZXI3Z4QU+TnT5PwKWloGmRHG81tWeI18/zxf9wWzrO2NhYFvITzEJH0vWLLiXdWtn/BYfLULXtDvkTaepbI5A==", + "dependencies": { + "@aws-sdk/middleware-signing": "3.391.0", + "@aws-sdk/types": "3.391.0", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-signing": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.391.0.tgz", + "integrity": "sha512-2pAJJlZqaHc0d+cz2FTVrQmWi8ygKfqfczHUo/loCtOaMNtWXBHb/JsLEecs6cXdizy6gi3YsLz6VZYwY4Ssxw==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^2.0.3", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.2.0", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.391.0.tgz", + "integrity": "sha512-LdK9uMNA14zqRw3B79Mhy7GX36qld/GYo93xuu+lr+AQ98leZEdc6GUbrtNDI3fP1Z8TMQcyHUKBml4/B+wXpQ==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@aws-sdk/util-endpoints": "3.391.0", + "@smithy/protocol-http": "^2.0.3", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/token-providers": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.391.0.tgz", + "integrity": "sha512-kgfArsKLDJE71qQjfXiHiM5cZqgDHlMsqEx35+A65GmTWJaS1PGDqu3ZvVVU8E5mxnCCLw7vho21fsjvH6TBpg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.391.0", + "@aws-sdk/middleware-logger": "3.391.0", + "@aws-sdk/middleware-recursion-detection": "3.391.0", + "@aws-sdk/middleware-user-agent": "3.391.0", + "@aws-sdk/types": "3.391.0", + "@aws-sdk/util-endpoints": "3.391.0", + "@aws-sdk/util-user-agent-browser": "3.391.0", + "@aws-sdk/util-user-agent-node": "3.391.0", + "@smithy/config-resolver": "^2.0.3", + "@smithy/fetch-http-handler": "^2.0.3", + "@smithy/hash-node": "^2.0.3", + "@smithy/invalid-dependency": "^2.0.3", + "@smithy/middleware-content-length": "^2.0.3", + "@smithy/middleware-endpoint": "^2.0.3", + "@smithy/middleware-retry": "^2.0.3", + "@smithy/middleware-serde": "^2.0.3", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.3", + "@smithy/node-http-handler": "^2.0.3", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^2.0.3", + "@smithy/shared-ini-file-loader": "^2.0.0", + "@smithy/smithy-client": "^2.0.3", + "@smithy/types": "^2.2.0", + "@smithy/url-parser": "^2.0.3", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.0.0", + "@smithy/util-defaults-mode-browser": "^2.0.3", + "@smithy/util-defaults-mode-node": "^2.0.3", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/types": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.391.0.tgz", + "integrity": "sha512-QpYVFKMOnzHz/JMj/b8wb18qxiT92U/5r5MmtRz2R3LOH6ooTO96k4ozXCrYr0qNed1PAnOj73rPrrH2wnCJKQ==", + "dependencies": { + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-endpoints": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.391.0.tgz", + "integrity": "sha512-zv4sYDTQhNxyLoekcE02/nk3xvoo6yCHDy1kDJk0MFxOKaqUB+CvZdQBR4YBLSDlD4o4DUBmdYgKT58FfbM8sQ==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.391.0.tgz", + "integrity": "sha512-6ipHOB1WdCBNeAMJauN7l2qNE0WLVaTNhkD290/ElXm1FHGTL8yw6lIDIjhIFO1bmbZxDiKApwDiG7ROhaJoxQ==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/types": "^2.2.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.391.0.tgz", + "integrity": "sha512-PVvAK/Lf4BdB1eJIZtyFpGSslGQwKpYt9/hKs5NlR+qxBMXU9T0DnTqH4GiXZaazvXr7OUVWitIF2b7iKBMTow==", + "dependencies": { + "@aws-sdk/types": "3.391.0", + "@smithy/node-config-provider": "^2.0.3", + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/abort-controller": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.4.tgz", + "integrity": "sha512-3+3/xRQ0K/NFVtKSiTGsUa3muZnVaBmHrLNgxwoBLZO9rNhwZtjjjf7pFJ6aoucoul/c/w3xobRkgi8F9MWX8Q==", + "dependencies": { + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/config-resolver": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.4.tgz", + "integrity": "sha512-JtKWIKoCFeOY5JGQeEl81AKdIpzeLLSjSMmO5yoKqc58Yn3cxmteylT6Elba3FgAHjK1OthARRXz5JXaKKRB7g==", + "dependencies": { + "@smithy/types": "^2.2.1", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/credential-provider-imds": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.4.tgz", + "integrity": "sha512-vW7xoDKZwjjf/2GCwVf/uvZce/QJOAYan9r8UsqlzOrnnpeS2ffhxeZjLK0/emZu8n6qU3amGgZ/BTo3oVtEyQ==", + "dependencies": { + "@smithy/node-config-provider": "^2.0.4", + "@smithy/property-provider": "^2.0.4", + "@smithy/types": "^2.2.1", + "@smithy/url-parser": "^2.0.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/eventstream-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.4.tgz", + "integrity": "sha512-DkVLcQjhOxPj/4pf2hNj2kvOeoLczirHe57g7czMNJCUBvg9cpU9hNgqS37Y5sjdEtMSa2oTyCS5oeHZtKgoIw==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.2.1", + "@smithy/util-hex-encoding": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/fetch-http-handler": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.0.4.tgz", + "integrity": "sha512-1dwR8T+QMe5Gs60NpZgF7ReZp0SXz1O/aX5BdDhsOJh72fi3Bx2UZlDihCdb++9vPyBRMXFRF7I8/C4x8iIm8A==", + "dependencies": { + "@smithy/protocol-http": "^2.0.4", + "@smithy/querystring-builder": "^2.0.4", + "@smithy/types": "^2.2.1", + "@smithy/util-base64": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/hash-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.4.tgz", + "integrity": "sha512-vZ6a/fvEAFJKNtxJsn0I2WM8uBdypLLhLTpP4BA6fRsBAtwIl5S4wTt0Hspy6uGNn/74LmCxGmFSTMMbSd7ZDA==", + "dependencies": { + "@smithy/types": "^2.2.1", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/invalid-dependency": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.4.tgz", + "integrity": "sha512-zfbPPZFiZvhIXJYKlzQwDUnxmWK/SmyDcM6iQJRZHU2jQZAzhHUXFGIu2lKH9L02VUqysOgQi3S/HY4fhrVT8w==", + "dependencies": { + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/md5-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.0.4.tgz", + "integrity": "sha512-MN7tKNljSk0V8tLNFAfLFa5gYb/EPkNvPDMEsXSSZdgL+uD95idz7UyNV34f/0rVuBjKzM1AqULFI7tKpH+Z3w==", + "dependencies": { + "@smithy/types": "^2.2.1", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/middleware-content-length": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.4.tgz", + "integrity": "sha512-Pdd+fhRbvizqsgYJ0pLWE6hjhq42wDFWzMj/1T7mEY9tG9bP6/AcdsQK8SAOckrBLURDoeSqTAwPKalsgcZBxw==", + "dependencies": { + "@smithy/protocol-http": "^2.0.4", + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/middleware-endpoint": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.4.tgz", + "integrity": "sha512-aLPqkqKjZQ1V718P0Ostpp53nWfwK32uD0HFKSAOT25RvL285dqzGl0PAKDXpyLsPsPmHe0Yrg0AUFkRv4CRbQ==", + "dependencies": { + "@smithy/middleware-serde": "^2.0.4", + "@smithy/types": "^2.2.1", + "@smithy/url-parser": "^2.0.4", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/middleware-retry": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.4.tgz", + "integrity": "sha512-stozO6NgH9W/OSfFMOJEtlJCsnJFSoGyV4LHzIVQeXTzZ2RHjmytQ/Ez7GngHGZ1YsB4zxE1qDTXAU0AlaKf2w==", + "dependencies": { + "@smithy/protocol-http": "^2.0.4", + "@smithy/service-error-classification": "^2.0.0", + "@smithy/types": "^2.2.1", + "@smithy/util-middleware": "^2.0.0", + "@smithy/util-retry": "^2.0.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/middleware-serde": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.4.tgz", + "integrity": "sha512-oDttJMMES7yXmopjQHnqTkxu8vZOdjB9VpSj94Ff4/GXdKQH7ozKLNIPq4C568nbeQbBt/gsLb6Ttbx1+j+JPQ==", + "dependencies": { + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/middleware-stack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", + "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/node-config-provider": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.4.tgz", + "integrity": "sha512-s9O90cEhkpzZulvdHBBaroZ6AJ5uV6qtmycgYKP1yOCSfPHGIWYwaULdbfxraUsvzCcnMosDNkfckqXYoKI6jw==", + "dependencies": { + "@smithy/property-provider": "^2.0.4", + "@smithy/shared-ini-file-loader": "^2.0.4", + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/node-http-handler": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.0.4.tgz", + "integrity": "sha512-svqeqkGgQz1B2m3IurHtp1O8vfuUGbqw6vynFmOrvPirRdiIPukHTZW1GN/JuBCtDpq9mNPutSVipfz2n4sZbQ==", + "dependencies": { + "@smithy/abort-controller": "^2.0.4", + "@smithy/protocol-http": "^2.0.4", + "@smithy/querystring-builder": "^2.0.4", + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/property-provider": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.4.tgz", + "integrity": "sha512-OfaUIhnyvOkuCPHWMPkJqX++dUaDKsiZWuZqCdU04Z9dNAl2TtZAh7dw2rsZGb57vq6YH3PierNrDfQJTAKYtg==", + "dependencies": { + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/protocol-http": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-2.0.4.tgz", + "integrity": "sha512-I1vCZ/m1U424gA9TXkL/pJ3HlRfujY8+Oj3GfDWcrNiWVmAeyx3CTvXw+yMHp2X01BOOu5fnyAa6JwAn1O+txA==", + "dependencies": { + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/querystring-builder": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.4.tgz", + "integrity": "sha512-Jc7UPx1pNeisYcABkoo2Pn4kvomy1UI7uxv7R+1W3806KMAKgYHutWmZG01aPHu2XH0zY2RF2KfGiuialsxHvA==", + "dependencies": { + "@smithy/types": "^2.2.1", + "@smithy/util-uri-escape": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/querystring-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.4.tgz", + "integrity": "sha512-Uh6+PhGxSo17qe2g/JlyoekvTHKn7dYWfmHqUzPAvkW+dHlc3DNVG3++PV48z33lCo5YDVBBturWQ9N/TKn+EA==", + "dependencies": { + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/service-error-classification": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", + "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/shared-ini-file-loader": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.4.tgz", + "integrity": "sha512-091yneupXnSqvAU+vLG7h0g4QRRO6TjulpECXYVU6yW/LiNp7QE533DBpaphmbtI6tTC4EfGrhn35gTa0w+GQg==", + "dependencies": { + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/signature-v4": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.4.tgz", + "integrity": "sha512-y2xblkS0hb44QJDn9YjPp5aRFYSiI7w0bI3tATE3ybOrII2fppqD0SE3zgvew/B/3rTunuiCW+frTD0W4UYb9Q==", + "dependencies": { + "@smithy/eventstream-codec": "^2.0.4", + "@smithy/is-array-buffer": "^2.0.0", + "@smithy/types": "^2.2.1", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-middleware": "^2.0.0", + "@smithy/util-uri-escape": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/smithy-client": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.0.4.tgz", + "integrity": "sha512-Dg1dkqyj3jwa03RFs6E4ASmfQ7CjplbGISJIJNSt3F8NfIid2RalbeCMOIHK7VagKh9qngZNyoKxObZC9LB9Lg==", + "dependencies": { + "@smithy/middleware-stack": "^2.0.0", + "@smithy/types": "^2.2.1", + "@smithy/util-stream": "^2.0.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/types": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.2.1.tgz", + "integrity": "sha512-6nyDOf027ZeJiQVm6PXmLm7dR+hR2YJUkr4VwUniXA8xZUGAu5Mk0zfx2BPFrt+e5YauvlIqQoH0CsrM4tLkfg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/url-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.4.tgz", + "integrity": "sha512-puIQ6+TJpI2AAPw7IGdGG6d2DEcVP5nJqa1VjrxzUcy2Jx7LtGn+gDHY2o9Pc9vQkmoicovTEKgvv7CdqP+0gg==", + "dependencies": { + "@smithy/querystring-parser": "^2.0.4", + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-base64": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", + "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-body-length-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", + "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-body-length-node": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.0.0.tgz", + "integrity": "sha512-ZV7Z/WHTMxHJe/xL/56qZwSUcl63/5aaPAGjkfynJm4poILjdD4GmFI+V+YWabh2WJIjwTKZ5PNsuvPQKt93Mg==", "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", "dependencies": { - "@aws-sdk/middleware-signing": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", + "@smithy/is-array-buffer": "^2.0.0", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-signing": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-config-provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", + "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/protocol-http": "^1.1.0", - "@smithy/signature-v4": "^1.0.1", - "@smithy/types": "^1.1.0", - "@smithy/util-middleware": "^1.0.1", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.4.tgz", + "integrity": "sha512-wGdnPt4Ng72duUd97HrlqVkq6DKVB/yjaGkSg5n3uuQKzzHjoi3OdjXGumD/VYPHz0dYd7wpLNG2CnMm/nfDrg==", "dependencies": { - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", + "@smithy/property-provider": "^2.0.4", + "@smithy/types": "^2.2.1", + "bowser": "^2.11.0", "tslib": "^2.5.0" }, "engines": { - "node": ">=14.0.0" + "node": ">= 10.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-defaults-mode-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.4.tgz", + "integrity": "sha512-QMkNcV6x52BeeeIvhvow6UmOu7nP7DXQljY6DKOP/aAokrli53IWTP/kUTd9B0Mp9tbW3WC10O6zaM69xiMNYw==", "dependencies": { - "@aws-sdk/client-sso-oidc": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", + "@smithy/config-resolver": "^2.0.4", + "@smithy/credential-provider-imds": "^2.0.4", + "@smithy/node-config-provider": "^2.0.4", + "@smithy/property-provider": "^2.0.4", + "@smithy/types": "^2.2.1", "tslib": "^2.5.0" }, "engines": { - "node": ">=14.0.0" + "node": ">= 10.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", "dependencies": { - "@smithy/types": "^1.1.0", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-middleware": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", + "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", "dependencies": { - "@aws-sdk/types": "3.370.0", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-retry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", + "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", - "bowser": "^2.11.0", + "@smithy/service-error-classification": "^2.0.0", "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.370.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-stream": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.4.tgz", + "integrity": "sha512-ZVje79afuv3DB1Ma/g5m/5v9Zda8nA0xNgvE1pOD3EnoTp/Ekch1z20AN6gfVsf7JYWK2VSMVDiqI9N8Ua4wbg==", "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/types": "^1.1.0", + "@smithy/fetch-http-handler": "^2.0.4", + "@smithy/node-http-handler": "^2.0.4", + "@smithy/types": "^2.2.1", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } } }, - "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.360.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-uri-escape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", + "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.360.0", - "@aws-sdk/config-resolver": "3.357.0", - "@aws-sdk/credential-provider-node": "3.360.0", - "@aws-sdk/fetch-http-handler": "3.357.0", - "@aws-sdk/hash-node": "3.357.0", - "@aws-sdk/invalid-dependency": "3.357.0", - "@aws-sdk/middleware-content-length": "3.357.0", - "@aws-sdk/middleware-endpoint": "3.357.0", - "@aws-sdk/middleware-host-header": "3.357.0", - "@aws-sdk/middleware-logger": "3.357.0", - "@aws-sdk/middleware-recursion-detection": "3.357.0", - "@aws-sdk/middleware-retry": "3.357.0", - "@aws-sdk/middleware-serde": "3.357.0", - "@aws-sdk/middleware-signing": "3.357.0", - "@aws-sdk/middleware-stack": "3.357.0", - "@aws-sdk/middleware-user-agent": "3.357.0", - "@aws-sdk/node-config-provider": "3.357.0", - "@aws-sdk/node-http-handler": "3.360.0", - "@aws-sdk/smithy-client": "3.360.0", - "@aws-sdk/types": "3.357.0", - "@aws-sdk/url-parser": "3.357.0", - "@aws-sdk/util-base64": "3.310.0", - "@aws-sdk/util-body-length-browser": "3.310.0", - "@aws-sdk/util-body-length-node": "3.310.0", - "@aws-sdk/util-defaults-mode-browser": "3.360.0", - "@aws-sdk/util-defaults-mode-node": "3.360.0", - "@aws-sdk/util-endpoints": "3.357.0", - "@aws-sdk/util-retry": "3.357.0", - "@aws-sdk/util-user-agent-browser": "3.357.0", - "@aws-sdk/util-user-agent-node": "3.357.0", - "@aws-sdk/util-utf8": "3.310.0", - "@smithy/protocol-http": "^1.0.1", - "@smithy/types": "^1.0.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs": { - "version": "3.360.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.360.0", - "@aws-sdk/config-resolver": "3.357.0", - "@aws-sdk/credential-provider-node": "3.360.0", - "@aws-sdk/fetch-http-handler": "3.357.0", - "@aws-sdk/hash-node": "3.357.0", - "@aws-sdk/invalid-dependency": "3.357.0", - "@aws-sdk/md5-js": "3.357.0", - "@aws-sdk/middleware-content-length": "3.357.0", - "@aws-sdk/middleware-endpoint": "3.357.0", - "@aws-sdk/middleware-host-header": "3.357.0", - "@aws-sdk/middleware-logger": "3.357.0", - "@aws-sdk/middleware-recursion-detection": "3.357.0", - "@aws-sdk/middleware-retry": "3.357.0", - "@aws-sdk/middleware-sdk-sqs": "3.357.0", - "@aws-sdk/middleware-serde": "3.357.0", - "@aws-sdk/middleware-signing": "3.357.0", - "@aws-sdk/middleware-stack": "3.357.0", - "@aws-sdk/middleware-user-agent": "3.357.0", - "@aws-sdk/node-config-provider": "3.357.0", - "@aws-sdk/node-http-handler": "3.360.0", - "@aws-sdk/smithy-client": "3.360.0", - "@aws-sdk/types": "3.357.0", - "@aws-sdk/url-parser": "3.357.0", - "@aws-sdk/util-base64": "3.310.0", - "@aws-sdk/util-body-length-browser": "3.310.0", - "@aws-sdk/util-body-length-node": "3.310.0", - "@aws-sdk/util-defaults-mode-browser": "3.360.0", - "@aws-sdk/util-defaults-mode-node": "3.360.0", - "@aws-sdk/util-endpoints": "3.357.0", - "@aws-sdk/util-retry": "3.357.0", - "@aws-sdk/util-user-agent-browser": "3.357.0", - "@aws-sdk/util-user-agent-node": "3.357.0", - "@aws-sdk/util-utf8": "3.310.0", - "@smithy/protocol-http": "^1.0.1", - "@smithy/types": "^1.0.0", - "fast-xml-parser": "4.2.5", + "@smithy/util-buffer-from": "^2.0.0", "tslib": "^2.5.0" }, "engines": { @@ -3008,15 +3879,6 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/md5-js": { - "version": "3.357.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@aws-sdk/util-utf8": "3.310.0", - "tslib": "^2.5.0" - } - }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { "version": "3.370.0", "license": "Apache-2.0", @@ -3267,12 +4129,83 @@ } }, "node_modules/@aws-sdk/middleware-sdk-sqs": { - "version": "3.357.0", - "license": "Apache-2.0", + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.391.0.tgz", + "integrity": "sha512-Yw45IGgsJVvJmrBAYhUSH9livn61Hr6rgVKBJqirZIAqCt1Cs0BbvQW88q2bY4e1e5K7CDiHY58eZpblrN0Xuw==", "dependencies": { - "@aws-sdk/types": "3.357.0", - "@aws-sdk/util-hex-encoding": "3.310.0", - "@aws-sdk/util-utf8": "3.310.0", + "@aws-sdk/types": "3.391.0", + "@smithy/types": "^2.2.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@aws-sdk/types": { + "version": "3.391.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.391.0.tgz", + "integrity": "sha512-QpYVFKMOnzHz/JMj/b8wb18qxiT92U/5r5MmtRz2R3LOH6ooTO96k4ozXCrYr0qNed1PAnOj73rPrrH2wnCJKQ==", + "dependencies": { + "@smithy/types": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@smithy/types": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.2.1.tgz", + "integrity": "sha512-6nyDOf027ZeJiQVm6PXmLm7dR+hR2YJUkr4VwUniXA8xZUGAu5Mk0zfx2BPFrt+e5YauvlIqQoH0CsrM4tLkfg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", "tslib": "^2.5.0" }, "engines": { @@ -8267,7 +9200,8 @@ }, "node_modules/aws-sdk-client-mock": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/aws-sdk-client-mock/-/aws-sdk-client-mock-3.0.0.tgz", + "integrity": "sha512-4mBiWhuLYLZe1+K/iB8eYy5SAZyW2se+Keyh5u9QouMt6/qJ5SRZhss68xvUX5g3ApzROJ06QPRziYHP6buuvQ==", "dependencies": { "@types/sinon": "^10.0.10", "sinon": "^14.0.2", @@ -14727,6 +15661,19 @@ "@adobe/helix-shared-wrap": "^2.0.0" } }, + "packages/queue-client": { + "name": "@adobe/contentlake-shared-queue-client", + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "@adobe/contentlake-shared-blob-storage": "^1.2.0", + "@aws-sdk/client-sqs": "^3.391.0" + }, + "devDependencies": { + "aws-sdk-client-mock": "^3.0.0", + "dotenv": "^16.3.1" + } + }, "packages/request-handler": { "name": "@adobe/contentlake-shared-request-handler", "version": "1.0.0", @@ -14775,6 +15722,7 @@ } }, "packages/secrets-manager": { + "name": "@adobe/contentlake-shared-secrets-manager", "version": "1.0.0", "license": "Apache-2.0", "dependencies": { diff --git a/packages/queue-client/.nycrc.json b/packages/queue-client/.nycrc.json new file mode 100644 index 0000000..ecafcea --- /dev/null +++ b/packages/queue-client/.nycrc.json @@ -0,0 +1,11 @@ +{ + "reporter": [ + "lcov", + "text" + ], + "exclude": ["it/*", "test/*", "src/mock*", "scripts/*"], + "check-coverage": true, + "lines": 80, + "branches": 80, + "statements": 80 +} diff --git a/packages/queue-client/.prettierrc b/packages/queue-client/.prettierrc new file mode 100644 index 0000000..56d4876 --- /dev/null +++ b/packages/queue-client/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/packages/queue-client/LICENSE.txt b/packages/queue-client/LICENSE.txt new file mode 100644 index 0000000..883ab09 --- /dev/null +++ b/packages/queue-client/LICENSE.txt @@ -0,0 +1,264 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +APACHE JACKRABBIT SUBCOMPONENTS + +Apache Jackrabbit includes parts with separate copyright notices and license +terms. Your use of these subcomponents is subject to the terms and conditions +of the following licenses: + + XPath 2.0/XQuery 1.0 Parser: + http://www.w3.org/2002/11/xquery-xpath-applets/xgrammar.zip + + Copyright (C) 2002 World Wide Web Consortium, (Massachusetts Institute of + Technology, European Research Consortium for Informatics and Mathematics, + Keio University). All Rights Reserved. + + This work is distributed under the W3C(R) Software License in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + W3C(R) SOFTWARE NOTICE AND LICENSE + http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + + This work (and included software, documentation such as READMEs, or + other related items) is being provided by the copyright holders under + the following license. By obtaining, using and/or copying this work, + you (the licensee) agree that you have read, understood, and will comply + with the following terms and conditions. + + Permission to copy, modify, and distribute this software and its + documentation, with or without modification, for any purpose and + without fee or royalty is hereby granted, provided that you include + the following on ALL copies of the software and documentation or + portions thereof, including modifications: + + 1. The full text of this NOTICE in a location viewable to users + of the redistributed or derivative work. + + 2. Any pre-existing intellectual property disclaimers, notices, + or terms and conditions. If none exist, the W3C Software Short + Notice should be included (hypertext is preferred, text is + permitted) within the body of any redistributed or derivative code. + + 3. Notice of any changes or modifications to the files, including + the date changes were made. (We recommend you provide URIs to the + location from which the code is derived.) + + THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT + HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS + FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR + DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, + TRADEMARKS OR OTHER RIGHTS. + + COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL + OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR + DOCUMENTATION. + + The name and trademarks of copyright holders may NOT be used in + advertising or publicity pertaining to the software without specific, + written prior permission. Title to copyright in this software and + any associated documentation will at all times remain with + copyright holders. diff --git a/packages/queue-client/README.md b/packages/queue-client/README.md new file mode 100644 index 0000000..98c1667 --- /dev/null +++ b/packages/queue-client/README.md @@ -0,0 +1,24 @@ +# Content Lake Shared - blob-storage + +> Blob storage library for Asset Catalog + +This is one of the [Content Lake Shared](https://github.com/adobe/contentlake-shared) libraries. + +## Status + +[![GitHub license](https://img.shields.io/github/license/adobe/contentlake-shared.svg)](https://github.com/adobe/contentlake-shared/blob/main/LICENSE.txt) + +## Usage + +Install using: + +``` +npm install @adobe/contentlake-shared-blob-storage +``` + +``` +import { BlobStorage } from '@adobe/contentlake-shared-blob-storage' + +const blobStorage = new BlobStorage(config); +const binary = await blobStorage.get('key') +``` diff --git a/packages/queue-client/it/index.test.js b/packages/queue-client/it/index.test.js new file mode 100644 index 0000000..d4ccd42 --- /dev/null +++ b/packages/queue-client/it/index.test.js @@ -0,0 +1,141 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +/* eslint-env mocha */ +import assert from 'assert'; +import dotenv from 'dotenv'; +import { + PurgeQueueCommand, + ReceiveMessageCommand, + SendMessageCommand, +} from '@aws-sdk/client-sqs'; +import util from 'util'; +import { BlobStorage } from '@adobe/contentlake-shared-blob-storage'; +import { QueueClient } from '../src/index.js'; + +dotenv.config(); + +const sleep = util.promisify(setTimeout); + +const DEFAULT_CONFIG = { + log: console, + queueUrl: process.env.QUEUE_URL, +}; + +const SLOW_TEST_TIMEOUT = 5000; + +function generateLargeMessage() { + const message = []; + for (let i = 0; i < 4086; i += 1) { + message.push( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse nec nisl massa. Morbi at erat elementum, dignissim quam at.', + ); + } + return message; +} + +describe('Queue Integration Tests', () => { + it('will use default logger', async () => { + const queueClient = new QueueClient({ + ...DEFAULT_CONFIG, + log: undefined, + }); + const id = await queueClient.sendMessage({ message: 'Hello World' }); + assert.ok(id); + }); + + describe('send message', () => { + it('can send message', async () => { + const queueClient = new QueueClient(DEFAULT_CONFIG); + const id = await queueClient.sendMessage({ message: 'Hello World' }); + assert.ok(id); + }); + + it('will fail with invalid queue', async () => { + const queueClient = new QueueClient({ + ...DEFAULT_CONFIG, + queueUrl: 'http://notaqueue.com', + }); + await assert.rejects(() => queueClient.sendMessage({ message: 'Hello World' })); + }); + + it('will fail on oversized message with no blob storage', async () => { + const queueClient = new QueueClient(DEFAULT_CONFIG); + await assert.rejects(() => queueClient.sendMessage(generateLargeMessage())); + }).timeout(SLOW_TEST_TIMEOUT); + + it('can send oversized message with blob storage', async () => { + const queueClient = new QueueClient({ + ...DEFAULT_CONFIG, + blobStorage: new BlobStorage({ + ...DEFAULT_CONFIG, + bucket: 'cl-commons-it-files', + }), + }); + const messageId = await queueClient.sendMessage(generateLargeMessage()); + assert.ok(messageId); + }).timeout(SLOW_TEST_TIMEOUT); + + after(async () => { + const queueClient = new QueueClient(DEFAULT_CONFIG); + await queueClient.client + .send( + new PurgeQueueCommand({ + QueueUrl: process.env.QUEUE_URL, + }), + ) + .catch(() => {}); + }); + }); + + describe('remove message', () => { + it('will fail on invalid recieptHandle', async () => { + const queueClient = new QueueClient(DEFAULT_CONFIG); + await assert.rejects(() => queueClient.removeMessage('notvalid')); + }); + + it('can remove message by recieptHandle', async () => { + const queueClient = new QueueClient(DEFAULT_CONFIG); + await queueClient.client.send( + new SendMessageCommand({ + QueueUrl: process.env.QUEUE_URL, + MessageBody: JSON.stringify({ message: 'Hello world' }), + }), + ); + await sleep(2000); + const messages = await queueClient.client.send( + new ReceiveMessageCommand({ + QueueUrl: process.env.QUEUE_URL, + }), + ); + const { ReceiptHandle } = messages.Messages[0]; + assert.ok(ReceiptHandle); + await queueClient.removeMessage(ReceiptHandle); + }).timeout(SLOW_TEST_TIMEOUT); + }); + + after(async () => { + const queueClient = new QueueClient(DEFAULT_CONFIG); + const messages = await queueClient.client.send( + new ReceiveMessageCommand({ + QueueUrl: process.env.QUEUE_URL, + }), + ); + Promise.all( + messages.Messages?.map(async (msg) => { + const { ReceiptHandle } = msg; + assert.ok(ReceiptHandle); + await queueClient.removeMessage(ReceiptHandle); + }), + ); + }); +}); diff --git a/packages/queue-client/package.json b/packages/queue-client/package.json new file mode 100644 index 0000000..c637de7 --- /dev/null +++ b/packages/queue-client/package.json @@ -0,0 +1,38 @@ +{ + "name": "@adobe/contentlake-shared-queue-client", + "version": "1.2.0", + "description": "Queue library for Asset Catalog", + "main": "src/index.js", + "type": "module", + "scripts": { + "test": "c8 mocha test", + "test:integration": "mocha it", + "lint": "eslint .", + "clean": "rm -rf package-lock.json node_modules" + }, + "mocha": { + "reporter": "mocha-multi-reporters", + "reporter-options": "configFile=../../.mocha-multi.json" + }, + "repository": { + "type": "git", + "url": "https://github.com/adobe/contentlake-shared" + }, + "author": "", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/adobe/contentlake-shared/issues" + }, + "homepage": "https://github.com/adobe/contentlake-shared/tree/main/packages/queue-client#readme", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@adobe/contentlake-shared-blob-storage": "^1.2.0", + "@aws-sdk/client-sqs": "^3.391.0" + }, + "devDependencies": { + "aws-sdk-client-mock": "^3.0.0", + "dotenv": "^16.3.1" + } +} diff --git a/packages/queue-client/src/index.js b/packages/queue-client/src/index.js new file mode 100644 index 0000000..c4beb0e --- /dev/null +++ b/packages/queue-client/src/index.js @@ -0,0 +1,209 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +import { + DeleteMessageCommand, + SQSClient, + SendMessageCommand, +} from '@aws-sdk/client-sqs'; +import { randomUUID } from 'crypto'; + +export * from './mock.js'; + +/** + * @typedef QueueConfig + * @property {import('@adobe/contentlake-shared-blob-storage').BlobStorage} [blobStorage] + * @property {SQSClient} [client] + * @property {*} [log] + * @property {string} queueUrl + */ + +/** + * @typedef {Object} QueueRecord + * @property {string} messageId + * @property {string} receiptHandle + * @property {string} body + * @property {Record} attributes + * @property {Record} messageAttributes + * @property {string} eventSource + */ + +const MAX_MESSAGE_LEN = 127000; // 127 KB + +export class QueueClient { + /** + * @type {import('@adobe/contentlake-shared-blob-storage').BlobStorage} + */ + #blobStorage; + + /** + * @type {SQSClient} + */ + client; + + #log; + + /** + * @type {string} + */ + #queueUrl; + + /** + * Check whether or not the context contains Records from a queue + * @param {import('@adobe/helix-universal').UniversalContext} context + * @returns {boolean} + */ + static isQueueRequest(context) { + return typeof context.invocation?.event?.Records !== 'undefined'; + } + + /** + * Gets the queue records from the context + * @param {import('@adobe/helix-universal').UniversalContext} context + * @returns {Array} the queue records + */ + static extractQueueRecords(context) { + return context.invocation?.event?.Records || []; + } + + /** + * @param {QueueConfig} config + */ + constructor(config) { + this.#log = config.log || console; + this.client = config.client || new SQSClient(); + this.#blobStorage = config.blobStorage; + this.#queueUrl = config.queueUrl; + } + + /** + * Serializes the specified message to a string, saving to blob storage + * if the message exceeds the max size + * @param {Object} message the message to serialize + * @returns {Promise} the message serialized as a string + */ + async #serializeMessage(message) { + const json = JSON.stringify(message); + const size = Buffer.byteLength(json); + if (size < MAX_MESSAGE_LEN) { + this.#log.debug('Message size is below max size, returning JSON', { + size, + }); + return json; + } else if (this.#blobStorage) { + const key = randomUUID(); + this.#log.debug( + 'Message size is above max size, saving to blob storage', + { + size, + key, + }, + ); + await this.#blobStorage.save(key, Buffer.from(json)); + return JSON.stringify({ blobStorage: true, key }); + } else { + this.#log.error( + 'Message size exceeds maximum and no blob storage provided', + { size }, + ); + throw new Error( + `Message size ${size} exceeds maximum and no blob storage provided`, + ); + } + } + + /** + * Reads the message, reading from blob storage if required + * @param {string} messageBody + * @returns {Promise} + */ + async readMessageBody(messageBody) { + const json = JSON.parse(messageBody); + if (!json.blobStorage) { + this.#log.debug('Message does not use blob storage'); + return json; + } else if (this.#blobStorage) { + const { key } = json; + this.#log.debug('Retrieving message from blob storage', { key }); + return JSON.parse(await this.#blobStorage.getString(key)); + } else { + throw new Error( + 'Message stored in blob storage, but no blob storage client provided', + ); + } + } + + /** + * Sends the specified message to the queue + * @param {Object} message the message to send + * @returns {Promise} the message id + */ + async sendMessage(message) { + try { + this.#log.debug('Enqueuing message', { + message, + queueUrl: this.#queueUrl, + }); + const messageBody = await this.#serializeMessage(message); + const res = await this.client.send( + new SendMessageCommand({ + QueueUrl: this.#queueUrl, + MessageBody: messageBody, + }), + ); + const { MessageId } = res; + this.#log.debug('Message enqueued successfully', { + message, + queueUrl: this.#queueUrl, + MessageId, + }); + return MessageId; + } catch (err) { + this.#log.error('Failed to queue message', { + err, + message, + queueUrl: this.#queueUrl, + }); + throw err; + } + } + + /** + * Removes a message by it's receipt handle + * @param {string} receiptHandle + */ + async removeMessage(receiptHandle) { + const queueUrl = this.#queueUrl; + try { + this.#log.debug('Removing message', { + receiptHandle, + queueUrl, + }); + await this.client.send( + new DeleteMessageCommand({ + QueueUrl: this.#queueUrl, + ReceiptHandle: receiptHandle, + }), + ); + this.#log.debug('Message removed successfully', { + receiptHandle, + queueUrl, + }); + } catch (err) { + this.#log.error('Failed to remove message', { + err, + receiptHandle, + queueUrl, + }); + throw err; + } + } +} diff --git a/packages/queue-client/src/mock.js b/packages/queue-client/src/mock.js new file mode 100644 index 0000000..1514c23 --- /dev/null +++ b/packages/queue-client/src/mock.js @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +export class MockQueueClient { + messages = []; + + async sendMessage(message) { + const id = this.messages.push(message); + return id.toString(); + } + + // eslint-disable-next-line class-methods-use-this + async removeMessage() { + // do nothing + } + + reset() { + this.messages = []; + } +} diff --git a/packages/queue-client/test/index.test.js b/packages/queue-client/test/index.test.js new file mode 100644 index 0000000..93c6073 --- /dev/null +++ b/packages/queue-client/test/index.test.js @@ -0,0 +1,239 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +/* eslint-env mocha */ + +/* eslint-env mocha */ +import assert from 'assert'; +import { mockClient } from 'aws-sdk-client-mock'; +import { + DeleteMessageCommand, + SQSClient, + SendMessageCommand, +} from '@aws-sdk/client-sqs'; +import { QueueClient } from '../src/index.js'; + +const mockSqsClient = mockClient(SQSClient); + +const LARGE_MESSAGE = { + // generates a 128KB string by joining the alphabet 5120 times + msg: [...Array(5120)].map(() => 'abcdefghijklmnopqrstuvwxyz').join(''), +}; + +describe('QueueClient Unit Tests', () => { + afterEach(() => mockSqsClient.reset()); + + it('can get client', () => { + const client = new QueueClient({}); + assert.ok(client); + }); + + it('can send message', async () => { + mockSqsClient.on(SendMessageCommand).callsFake((input) => { + assert.deepStrictEqual(input, { + MessageBody: '{"message":"Hello World!"}', + QueueUrl: 'http://www.queue.com', + }); + return { + MessageId: 'test-id', + }; + }); + const client = new QueueClient({ + client: mockSqsClient, + queueUrl: 'http://www.queue.com', + }); + const resp = await client.sendMessage({ message: 'Hello World!' }); + assert.strictEqual(resp, 'test-id'); + }); + + it('throws on create message failure', async () => { + mockSqsClient.on(SendMessageCommand).rejects(); + const client = new QueueClient({ + client: mockSqsClient, + queueUrl: 'http://www.queue.com', + }); + let caught; + try { + await client.sendMessage({ message: 'Hello World!' }); + } catch (err) { + caught = err; + } + assert.ok(caught); + }); + + it('can remove message', async () => { + mockSqsClient.on(DeleteMessageCommand).callsFake((input) => { + assert.strictEqual(input.ReceiptHandle, 'test-handle'); + }); + const client = new QueueClient({ + client: mockSqsClient, + queueUrl: 'http://www.queue.com', + }); + await client.removeMessage('test-handle'); + }); + + it('remove handles throws', async () => { + mockSqsClient.on(DeleteMessageCommand).rejects(); + const client = new QueueClient({ + client: mockSqsClient, + queueUrl: 'http://www.queue.com', + }); + await assert.rejects(() => client.removeMessage('test-handle')); + }); + + it('fails on large message if no blob storage provided', async () => { + const client = new QueueClient({ + client: mockSqsClient, + queueUrl: 'http://www.queue.com', + }); + + await assert.rejects(() => client.sendMessage(LARGE_MESSAGE)); + }); + + it('can send large messages', async () => { + const blobs = {}; + const mockBlobStorage = { + save: (key, buf) => { + blobs[key] = buf.toString('utf-8'); + }, + }; + mockSqsClient.on(SendMessageCommand).callsFake((input) => { + const { blobStorage, key } = JSON.parse(input.MessageBody); + assert.strictEqual(blobStorage, true); + assert.strictEqual(blobs[key].message, LARGE_MESSAGE.message); + assert.ok(blobs[key]); + return { + MessageId: 'test-message-id', + }; + }); + + const client = new QueueClient({ + client: mockSqsClient, + queueUrl: 'http://www.queue.com', + blobStorage: mockBlobStorage, + }); + + const id = await client.sendMessage(LARGE_MESSAGE); + assert.strictEqual(id, 'test-message-id'); + }); + + it('can read large messages', async () => { + const blobs = { test: JSON.stringify(LARGE_MESSAGE) }; + const mockBlobStorage = { + getString: (key) => blobs[key], + }; + const client = new QueueClient({ + queueUrl: 'http://www.queue.com', + blobStorage: mockBlobStorage, + }); + + const messageBody = await client.readMessageBody( + JSON.stringify({ + blobStorage: true, + key: 'test', + }), + ); + assert.strictEqual(messageBody.message, LARGE_MESSAGE.message); + }); + + it('can read normal sized messages', async () => { + const client = new QueueClient({ + queueUrl: 'http://www.queue.com', + }); + + const messageBody = await client.readMessageBody( + JSON.stringify({ + message: 'Hello World!', + }), + ); + assert.strictEqual(messageBody.message, 'Hello World!'); + }); + + it('will fail to read large message if no blob storage provided', async () => { + const client = new QueueClient({ + client: mockSqsClient, + queueUrl: 'http://www.queue.com', + }); + + await assert.rejects(() => client.readMessageBody( + JSON.stringify({ + blobStorage: true, + key: 'test', + }), + )); + }); + + describe('isQueueRequest', () => { + it('will not fail if undefined', () => { + assert.ok(!QueueClient.isQueueRequest({})); + }); + + it('will not fail if not present', () => { + assert.ok( + !QueueClient.isQueueRequest({ + invocation: { event: {} }, + }), + ); + }); + + it('will return true on empty records array', () => { + assert.ok( + QueueClient.isQueueRequest({ + invocation: { + event: { + Records: [], + }, + }, + }), + ); + }); + }); + + describe('get sqs records', () => { + it('will not fail if not present', () => { + assert.ok(QueueClient.extractQueueRecords({})); + }); + + it('will extract records', () => { + const res = QueueClient.extractQueueRecords({ + invocation: { + event: { + Records: [ + { + messageId: 'af88e691-c3a6-4b46-b4d2-1c897b41b600', + receiptHandle: + 'AQEBJCLTpWgDm+oaeBAlSKWumzIoFRHeJglHCwWEfJANgc7GSWQBcYTiLPfbO1IuxAIkJagUIEkqgmszqnj2a7hLZjoIcv0AWCQfL0tmje/hhnDWYKdQmrUmfITdPDIg49XI+n+Ub/gKjXEy3VvunLsp0bxuF33OCsR8+N0Skff+U+zan+42GcHtn8lacm6ZQIF9msoFxszourA+zpJ/DJ1DTMlEpr9cSPxa6nsbg7JHOOwBzWknn7d3Zkimuo/J3shMyb+4fBYFRNpzXt9o9l8rfQpi9JZDwGIFRqDYFvpI0Emqv9ke1V2uBAJPiiGS0h1MIKO6dZZ/ejfWAR0Rug3zMEH9SEa6N+hT4gF5Pu2IN6WmcRhE4sh0jW/ImAAunuIo/OZ1FhNjqp+keK3AvBiPiQ==', + body: '{"message":"Hello World"}', + attributes: { + ApproximateReceiveCount: '1', + SentTimestamp: '1678764328689', + SenderId: 'AIDAXXYBVS2FJDJXJ56HK', + ApproximateFirstReceiveTimestamp: '1678764328690', + }, + messageAttributes: {}, + md5OfBody: 'd7e5fb40d1b43e304158449c3ecd6e5c', + eventSource: 'aws:sqs', + eventSourceARN: + 'arn:aws:sqs:us-east-1:532042585738:content-lake-it', + awsRegion: 'us-east-1', + }, + ], + }, + }, + }); + assert.strictEqual(res.length, 1); + assert.strictEqual( + res[0].messageId, + 'af88e691-c3a6-4b46-b4d2-1c897b41b600', + ); + }); + }); +}); From e76ebd646e0513e6872314cc203737a76d918fd6 Mon Sep 17 00:00:00 2001 From: Dan Klco Date: Thu, 17 Aug 2023 15:11:59 -0400 Subject: [PATCH 2/4] Actually updating the README --- packages/queue-client/README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/queue-client/README.md b/packages/queue-client/README.md index 98c1667..3783633 100644 --- a/packages/queue-client/README.md +++ b/packages/queue-client/README.md @@ -1,6 +1,6 @@ -# Content Lake Shared - blob-storage +# Content Lake Shared - Queue Client -> Blob storage library for Asset Catalog +> Queue library for Asset Catalog This is one of the [Content Lake Shared](https://github.com/adobe/contentlake-shared) libraries. @@ -13,12 +13,14 @@ This is one of the [Content Lake Shared](https://github.com/adobe/contentlake-sh Install using: ``` -npm install @adobe/contentlake-shared-blob-storage +npm install @adobe/contentlake-shared-queue-client ``` ``` -import { BlobStorage } from '@adobe/contentlake-shared-blob-storage' +import { QueueClient } from '@adobe/contentlake-shared-queue-client' -const blobStorage = new BlobStorage(config); -const binary = await blobStorage.get('key') +const queueClient = new QueueClient({ + queueUrl: 'https://somequeue.com' +}); +await queueClient.sendMessage({ message: 'Hello World' }); ``` From aa7bbe27f526e695ea078dad3e32d895feff4ca9 Mon Sep 17 00:00:00 2001 From: Dan Klco Date: Fri, 18 Aug 2023 16:24:06 -0400 Subject: [PATCH 3/4] Tracking the removed messages in the mock and fixing a JSDoc tag --- packages/queue-client/src/index.js | 2 +- packages/queue-client/src/mock.js | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/queue-client/src/index.js b/packages/queue-client/src/index.js index c4beb0e..c9b0dbb 100644 --- a/packages/queue-client/src/index.js +++ b/packages/queue-client/src/index.js @@ -68,7 +68,7 @@ export class QueueClient { /** * Gets the queue records from the context * @param {import('@adobe/helix-universal').UniversalContext} context - * @returns {Array} the queue records + * @returns {Array} the queue records */ static extractQueueRecords(context) { return context.invocation?.event?.Records || []; diff --git a/packages/queue-client/src/mock.js b/packages/queue-client/src/mock.js index 1514c23..e8097d2 100644 --- a/packages/queue-client/src/mock.js +++ b/packages/queue-client/src/mock.js @@ -12,17 +12,29 @@ export class MockQueueClient { messages = []; + removed = []; + async sendMessage(message) { const id = this.messages.push(message); return id.toString(); } + /** + * Reads the message, reading from blob storage if required + * @param {string} messageBody + * @returns {Promise} + */ // eslint-disable-next-line class-methods-use-this - async removeMessage() { - // do nothing + async readMessageBody(messageBody) { + return JSON.parse(messageBody); + } + + async removeMessage(receiptHandle) { + this.removed.push(receiptHandle); } reset() { this.messages = []; + this.removed = []; } } From 2f74bc51aae685c16addd76d6f520af3cc1ad941 Mon Sep 17 00:00:00 2001 From: Dan Klco Date: Mon, 21 Aug 2023 10:31:06 -0400 Subject: [PATCH 4/4] feat: refector request handler to expose new main method, cleanup double logs and update dependencies --- package-lock.json | 9 +- packages/request-handler/package.json | 4 +- packages/request-handler/src/index.js | 179 ++++---------------- packages/request-handler/src/internal.js | 169 ++++++++++++++++++ packages/request-handler/test/index.test.js | 134 +++++++-------- 5 files changed, 271 insertions(+), 224 deletions(-) create mode 100644 packages/request-handler/src/internal.js diff --git a/package-lock.json b/package-lock.json index b387d45..16007bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,11 @@ "workspaces": [ "./packages/*" ], + "dependencies": { + "@adobe/contentlake-shared-blob-storage": "^1.2.1", + "@adobe/contentlake-shared-queue-client": "^1.2.0", + "@adobe/contentlake-shared-rest-error": "^1.0.0" + }, "devDependencies": { "@adobe/eslint-config-helix": "2.0.2", "@semantic-release/changelog": "6.0.3", @@ -15605,7 +15610,9 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@adobe/content-lake-commons": "^1.8.1", + "@adobe/contentlake-shared-blob-storage": "^1.2.1", + "@adobe/contentlake-shared-queue-client": "^1.2.0", + "@adobe/contentlake-shared-rest-error": "^1.0.0", "@adobe/helix-shared-wrap": "^2.0.0", "@adobe/helix-status": "^10.0.9", "@adobe/helix-universal-logger": "^3.0.10", diff --git a/packages/request-handler/package.json b/packages/request-handler/package.json index 3e0e28c..6e9fb1e 100644 --- a/packages/request-handler/package.json +++ b/packages/request-handler/package.json @@ -26,7 +26,9 @@ "access": "public" }, "dependencies": { - "@adobe/content-lake-commons": "^1.8.1", + "@adobe/contentlake-shared-blob-storage": "^1.2.1", + "@adobe/contentlake-shared-queue-client": "^1.2.0", + "@adobe/contentlake-shared-rest-error": "^1.0.0", "@adobe/helix-shared-wrap": "^2.0.0", "@adobe/helix-status": "^10.0.9", "@adobe/helix-universal-logger": "^3.0.10", diff --git a/packages/request-handler/src/index.js b/packages/request-handler/src/index.js index e076333..a32c5de 100644 --- a/packages/request-handler/src/index.js +++ b/packages/request-handler/src/index.js @@ -10,21 +10,16 @@ * governing permissions and limitations under the License. */ -import { - BlobStorage, - ContextHelper, - QueueClient, - RestError, -} from '@adobe/content-lake-commons'; +import { RestError } from '@adobe/contentlake-shared-rest-error'; import wrap from '@adobe/helix-shared-wrap'; import { helixStatus } from '@adobe/helix-status'; import { logger } from '@adobe/helix-universal-logger'; -import { Response } from 'node-fetch'; +import { RequestHandlerInternal } from './internal.js'; /** * @callback HandlerFn * @param {Record} event the event to handle - * @param {import('@adobe/content-lake-commons').contextHelper.UniversalishContext} context + * @param {import('@adobe/helix-universal').UniversalContext} context * the current context * @returns {Promise} the response from handling the request */ @@ -41,7 +36,7 @@ export class RequestHandler { #wrapFn; /** - * @type {HandlerFn} + * @type {Record} */ #handlers = {}; @@ -61,30 +56,7 @@ export class RequestHandler { * @returns {function(Request,UniversalContext):Promise} the main function */ getMain() { - let main = wrap(async (request, context) => { - const helper = new ContextHelper(context); - const log = helper.getLog(); - let res; - const { method, url } = request; - const loggableRequest = { - method, - url, - headers: Object.fromEntries(request.headers), - invocation: context?.invocation, - event: helper.extractOriginalEvent(), - }; - const start = Date.now(); - try { - log.debug(`> ${method} ${url}`); - log.debug('Handling request', loggableRequest); - res = await this.handleRequest(context); - } catch (err) { - log.warn('Exception handling request', { ...loggableRequest, err }); - res = RestError.toProblemResponse(err); - } - log.info(`< ${method} ${res.status} ${url} ${Date.now() - start}ms`); - return res; - }); + let main = wrap(async (request, context) => this.main(request, context)); if (this.#wrapFn) { main = main.with(this.#wrapFn); } @@ -92,126 +64,35 @@ export class RequestHandler { } /** - * Get the queue client for the specified request - * @param {any} context Context for the current execution is running. - * @returns {QueueClient} the queue client + * @param {Request} request + * @param {import('@adobe/helix-universal').UniversalContext} context + * @returns {Promise} */ - // eslint-disable-next-line class-methods-use-this - getQueueClient(context) { - const helper = new ContextHelper(context); - const queueUrl = context.env.QUEUE_URL; - if (!queueUrl) { - throw new Error('Missing ENV variable QUEUE_URL'); - } - let blobStorage; - const queueStorageBucket = context.env.QUEUE_STORAGE_BUCKET; - if (queueStorageBucket) { - blobStorage = new BlobStorage({ - ...helper.extractAwsConfig(context), - bucket: queueStorageBucket, - }); - } - return new QueueClient({ - ...helper.extractAwsConfig(context), - queueUrl, - blobStorage, - }); - } - - /** - * Handles a request that comes into an extractor's HTTP service. - * @param {any} context Context for the current execution is running. - * @returns {Promise} Resolves with the response that the service - * will provide - */ - async handleRequest(context) { - const helper = new ContextHelper(context); - const log = helper.getLog(); - - if (helper.isQueueRequest()) { - const queueClient = this.getQueueClient(context); - const records = helper.extractQueueRecords(); - log.debug('Handing queue records', { count: records.length }); - - const results = await Promise.allSettled( - records.map((qr) => this.handleQueueRecord(context, qr, queueClient, log)), - ); - - const batchItemFailures = results - .filter((r) => r.status === 'rejected') - .map((fail) => fail.reason) - .map((itemIdentifier) => ({ itemIdentifier })); - if (batchItemFailures.length === 0) { - log.debug('All batch items processed successfully'); - return new Response(); - } else { - log.info('Failed to process batch items', { - items: batchItemFailures, - count: batchItemFailures.length, - }); - return new Response( - JSON.stringify({ - batchItemFailures, - }), - { status: 206 }, - ); - } - } else { - log.debug('Handing POST payload'); - const event = helper.extractOriginalEvent(); - return this.handleEvent(event, context); - } - } - - /** - * Handles a single event - * @param {Record} event the event to handle - * @param {import('@adobe/content-lake-commons').contextHelper.UniversalishContext} context - * the current context - * @returns {Promise} the response from handling the event - */ - async handleEvent(event, context) { - const { action } = event || {}; - if (!action) { - throw new RestError(400, 'Missing parameter [action]'); - } - if (!this.#handlers[action]) { - throw new RestError(400, `Invalid action [${action}]`); - } - return this.#handlers[action](event, context); - } - - /** - * Handles a queue event record - * @param {import('@adobe/content-lake-commons').contextHelper.UniversalishContext} context - * @param {import('@adobe/content-lake-commons').QueueRecord} record - * @param {QueueClient} queueClient - * @param {import('@adobe/content-lake-commons').contextHelper.Logger} log - * @returns {Promise} - */ - async handleQueueRecord(context, record, queueClient, log) { + async main(request, context) { + const log = context.log || console; + let res; + const { method, url } = request; + const loggableRequest = { + method, + url, + headers: Object.fromEntries(request.headers), + invocation: context?.invocation, + }; + const start = Date.now(); try { - log.debug('Handling queue record', { - record, - }); - const event = await queueClient.readMessageBody(record.body); - const res = await this.handleEvent(event, context); - if (res.ok) { - log.debug('Record handled successfully, removing from queue', { - record, - }); - await queueClient.removeMessage(record.receiptHandle); - } else { - log.warn('Record handling unsuccessful', { - record, - res, - }); - throw record.messageId; - } + log.debug(`> ${method} ${url}`); + log.debug('Handling request', loggableRequest); + const internalHandler = new RequestHandlerInternal( + context, + this.#handlers, + ); + res = await internalHandler.handle(); } catch (err) { - log.warn('Failed to handle queue record', { record, err }); - throw record.messageId; + log.warn('Exception handling request', { ...loggableRequest, err }); + res = RestError.toProblemResponse(err, context); } + log.info(`< ${method} ${res.status} ${url} ${Date.now() - start}ms`); + return res; } /** diff --git a/packages/request-handler/src/internal.js b/packages/request-handler/src/internal.js new file mode 100644 index 0000000..a370cd3 --- /dev/null +++ b/packages/request-handler/src/internal.js @@ -0,0 +1,169 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { RestError } from '@adobe/contentlake-shared-rest-error'; +import { BlobStorage } from '@adobe/contentlake-shared-blob-storage'; +import { QueueClient } from '@adobe/contentlake-shared-queue-client'; +import { Response } from 'node-fetch'; + +/** + * Internal class for handling requests + */ +export class RequestHandlerInternal { + #context; + + #log; + + /** + * @type {Record} + */ + #handlers = {}; + + /** + * @type {QueueClient} + */ + #queueClient; + + /** + * @param {import('@adobe/helix-universal').UniversalContext} context + * @param {Record} handlers + */ + constructor(context, handlers) { + this.#context = context; + this.#handlers = handlers; + this.#log = context.log || console; + + if (context.queueClient) { + this.#queueClient = context.queueClient; + } else { + const queueUrl = context.env.QUEUE_URL; + let blobStorage; + const queueStorageBucket = context.env.QUEUE_STORAGE_BUCKET; + if (queueStorageBucket) { + blobStorage = new BlobStorage({ + bucket: queueStorageBucket, + }); + } + this.queueClient = new QueueClient({ + queueUrl, + blobStorage, + }); + } + } + + /** + * @param {Response} res + */ + async #serializeResponse(res) { + let body; + try { + body = await res.text(); + } catch (err) { + this.#log.warn('Failed to retrieve body', err); + } + return { + body, + status: res.status, + headers: Object.fromEntries(res.headers), + }; + } + + /** + * Handles a single event + * @param {Record} event the event to handle + * @returns {Promise} the response from handling the event + */ + async #handleEvent(event) { + const { action } = event || {}; + if (!action) { + throw new RestError(400, 'Missing parameter [action]'); + } + if (!this.#handlers[action]) { + throw new RestError(400, `Invalid action [${action}]`); + } + return this.#handlers[action](event, this.#context); + } + + /** + * Handles queue records + * @returns {Promise} + */ + async #handleQueueRecords() { + const records = QueueClient.extractQueueRecords(this.#context); + this.#log.debug('Handing queue records', { count: records.length }); + + const results = await Promise.allSettled( + records.map(async (record) => { + let res; + try { + this.#log.debug('Handling queue record', { + record, + }); + const event = await this.#queueClient.readMessageBody(record.body); + res = await this.#handleEvent(event, this.#context); + } catch (err) { + this.#log.warn('Failed to handle queue record', { record, err }); + throw record.messageId; + } + if (res.ok) { + this.#log.debug('Record handled successfully, removing from queue', { + record, + }); + await this.#queueClient.removeMessage(record.receiptHandle); + } else { + const serialized = this.#serializeResponse(res); + this.#log.warn('Record handling unsuccessful', { + record, + response: serialized, + }); + throw record.messageId; + } + }), + ); + + const batchItemFailures = results + .filter((r) => r.status === 'rejected') + .map((fail) => fail.reason) + .map((itemIdentifier) => ({ itemIdentifier })); + if (batchItemFailures.length === 0) { + this.#log.debug('All batch items processed successfully'); + return new Response(); + } else { + this.#log.info('Failed to process batch items', { + total: results.length, + failures: batchItemFailures, + failure_count: batchItemFailures.length, + }); + return new Response( + JSON.stringify({ + batchItemFailures, + }), + { status: 206 }, + ); + } + } + + /** + * Handle the request + * @returns {Promise} + */ + async handle() { + const { event } = this.#context.invocation; + this.#log.debug('Handling event', event); + if (QueueClient.isQueueRequest(this.#context)) { + return this.#handleQueueRecords(); + } else { + this.#log.debug('Handing POST payload'); + return this.#handleEvent(event); + } + } +} diff --git a/packages/request-handler/test/index.test.js b/packages/request-handler/test/index.test.js index dda1b6d..c4b87a7 100644 --- a/packages/request-handler/test/index.test.js +++ b/packages/request-handler/test/index.test.js @@ -14,9 +14,10 @@ /* eslint-disable class-methods-use-this */ import { createHash, randomUUID } from 'crypto'; import assert from 'assert'; -import { stub } from 'sinon'; +import { MockQueueClient } from '@adobe/contentlake-shared-queue-client'; import { RequestHandler } from '../src/index.js'; +const mockQueueClient = new MockQueueClient(); const MOCK_REQUEST = new Request('http://localhost', { method: 'POST' }); function mockContext(event) { return { @@ -27,6 +28,7 @@ function mockContext(event) { QUEUE_URL: 'http://testqueue.com', QUEUE_STORAGE_BUCKET: 'queue-test', }, + queueClient: mockQueueClient, }; } @@ -48,6 +50,7 @@ function mockSqsRecord(action) { } describe('Request Handler Tests', () => { + afterEach(() => mockQueueClient.reset()); describe('main', () => { it('can get main', () => { const requestHandler = new RequestHandler(); @@ -57,9 +60,9 @@ describe('Request Handler Tests', () => { it('main requires context / request', async () => { const requestHandler = new RequestHandler(); - const main = requestHandler.getMain(); let caught; try { + const main = requestHandler.getMain(); await main(undefined, undefined); } catch (err) { caught = err; @@ -72,7 +75,10 @@ describe('Request Handler Tests', () => { throw new Error('Surprise!'); }); const main = requestHandler.getMain(); - const res = await main(MOCK_REQUEST, mockContext({ action: 'throw' })); + const res = await main(MOCK_REQUEST, { + ...mockContext({ action: 'throw' }), + queueClient: undefined, + }); assert.strictEqual(res.status, 500); }); @@ -94,19 +100,13 @@ describe('Request Handler Tests', () => { 'test', () => new Response(), ); - let caught; - try { - await requestHandler.handleEvent( - { - payload: 'Hello World', - }, - mockContext(), - ); - } catch (err) { - caught = err; - } - assert.ok(caught); - assert.strictEqual(caught.status, 400); + const res = await requestHandler.main( + MOCK_REQUEST, + mockContext({ + payload: 'Hello World', + }), + ); + assert.strictEqual(res.status, 400); }); it('will throw on unknown action', async () => { @@ -114,14 +114,13 @@ describe('Request Handler Tests', () => { 'test', () => new Response(), ); - let caught; - try { - await requestHandler.handleEvent({ action: 'test2' }, mockContext()); - } catch (err) { - caught = err; - } - assert.ok(caught); - assert.strictEqual(caught.status, 400); + const res = await requestHandler.main( + MOCK_REQUEST, + mockContext({ + action: 'test2', + }), + ); + assert.strictEqual(res.status, 400); }); it('will not fail to destructure on undefined event', async () => { @@ -129,14 +128,13 @@ describe('Request Handler Tests', () => { 'test', () => new Response(), ); - let caught; - try { - await requestHandler.handleEvent(undefined, mockContext()); - } catch (err) { - caught = err; - } - assert.ok(caught); - assert.strictEqual(caught.status, 400); + const res = await requestHandler.main(MOCK_REQUEST, { + ...mockContext(), + invocation: { + event: undefined, + }, + }); + assert.strictEqual(res.status, 400); }); it('can add/invoke actions', async () => { @@ -162,11 +160,6 @@ describe('Request Handler Tests', () => { return new Response(); }); - const removed = []; - stub(requestHandler, 'getQueueClient').returns({ - removeMessage: (handle) => removed.push(handle), - readMessageBody: (msgBody) => JSON.parse(msgBody), - }); const main = requestHandler.getMain(); const res = await main( MOCK_REQUEST, @@ -177,7 +170,7 @@ describe('Request Handler Tests', () => { assert.ok(res.ok); assert.strictEqual(res.status, 200); assert.strictEqual(events.length, 2); - assert.strictEqual(removed.length, 2); + assert.strictEqual(mockQueueClient.removed.length, 2); }); it('can handle SQS record(s) with failures', async () => { @@ -196,57 +189,52 @@ describe('Request Handler Tests', () => { throw new Error('bad news'); }); - const removed = []; - stub(requestHandler, 'getQueueClient').returns({ - removeMessage: (handle) => removed.push(handle), - readMessageBody: (msgBody) => JSON.parse(msgBody), - }); const main = requestHandler.getMain(); - const res = await main( - MOCK_REQUEST, - mockContext({ + const res = await main(MOCK_REQUEST, { + ...mockContext({ Records: [ mockSqsRecord('test'), mockSqsRecord('fail'), mockSqsRecord('throw'), ], }), - ); + log: console, + }); assert.ok(res.ok); assert.strictEqual(res.status, 206); const body = await res.json(); assert.strictEqual(body.batchItemFailures.length, 2); assert.strictEqual(events.length, 3); - assert.strictEqual(removed.length, 1); - }); - }); - - describe('getQueueClient', () => { - it('can get queue client', async () => { - const requestHandler = new RequestHandler(); - const queueClient = requestHandler.getQueueClient(mockContext()); - assert.ok(queueClient); + assert.strictEqual(mockQueueClient.removed.length, 1); }); - it('can get queue client without bucket', async () => { - const requestHandler = new RequestHandler(); - const queueClient = requestHandler.getQueueClient({ - env: { - QUEUE_URL: 'http://test.queue.com', + it('handles failures reading response body', async () => { + const events = []; + const requestHandler = new RequestHandler().withHandler( + 'bad-body', + (evt) => { + events.push(evt); + return { + text() { + throw Error(); + }, + }; }, - }); - assert.ok(queueClient); - }); + ); - it('getQueueClient requires queue url', async () => { - const requestHandler = new RequestHandler(); - let caught; - try { - requestHandler.getQueueClient({ env: {} }); - } catch (err) { - caught = err; - } - assert.ok(caught); + const main = requestHandler.getMain(); + const res = await main( + MOCK_REQUEST, + mockContext({ + Records: [mockSqsRecord('bad-body')], + }), + ); + assert.ok(res.ok); + assert.strictEqual(res.status, 206); + const body = await res.json(); + assert.strictEqual(body.batchItemFailures.length, 1); + assert.strictEqual(events.length, 1); + assert.strictEqual(mockQueueClient.removed.length, 0); }); }); });