From d27266e844dfaf97521f92bc512554ccf95d387b Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Sun, 6 Oct 2024 20:01:56 +0100 Subject: [PATCH 01/17] Dependencies imported --- package-lock.json | 1808 +++++++++++++++++++++++++++++++++++++++++---- package.json | 5 + 2 files changed, 1689 insertions(+), 124 deletions(-) diff --git a/package-lock.json b/package-lock.json index 26dcd5140..33e83595f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,11 @@ "version": "0.0.1", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.665.0", + "@aws-sdk/lib-dynamodb": "^3.665.0", + "aws-sdk-client-mock": "^4.0.2" + }, "devDependencies": { "@babel/core": "^7.17.2", "@babel/eslint-parser": "^7.17.0", @@ -68,6 +73,746 @@ "node": "^12.14.1 || >=14.0.0" } }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.665.0.tgz", + "integrity": "sha512-W1idOmV4bDzUxZhkYbDGzmNwJ5Lf61UIOGUUAva5A2CpfVm2qTGRhEnowj6PEv5I06O/gMTsJpJVJxypzKdLQQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.665.0", + "@aws-sdk/client-sts": "3.665.0", + "@aws-sdk/core": "3.665.0", + "@aws-sdk/credential-provider-node": "3.665.0", + "@aws-sdk/middleware-endpoint-discovery": "3.664.0", + "@aws-sdk/middleware-host-header": "3.664.0", + "@aws-sdk/middleware-logger": "3.664.0", + "@aws-sdk/middleware-recursion-detection": "3.664.0", + "@aws-sdk/middleware-user-agent": "3.664.0", + "@aws-sdk/region-config-resolver": "3.664.0", + "@aws-sdk/types": "3.664.0", + "@aws-sdk/util-endpoints": "3.664.0", + "@aws-sdk/util-user-agent-browser": "3.664.0", + "@aws-sdk/util-user-agent-node": "3.664.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.7", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.22", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.22", + "@smithy/util-defaults-mode-node": "^3.0.22", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.6", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.665.0.tgz", + "integrity": "sha512-zje+oaIiyviDv5dmBWhGHifPTb0Idq/HatNPy+VEiwo2dxcQBexibD5CQE5e8CWZK123Br/9DHft+iNKdiY5bA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.665.0", + "@aws-sdk/middleware-host-header": "3.664.0", + "@aws-sdk/middleware-logger": "3.664.0", + "@aws-sdk/middleware-recursion-detection": "3.664.0", + "@aws-sdk/middleware-user-agent": "3.664.0", + "@aws-sdk/region-config-resolver": "3.664.0", + "@aws-sdk/types": "3.664.0", + "@aws-sdk/util-endpoints": "3.664.0", + "@aws-sdk/util-user-agent-browser": "3.664.0", + "@aws-sdk/util-user-agent-node": "3.664.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.7", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.22", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.22", + "@smithy/util-defaults-mode-node": "^3.0.22", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.665.0.tgz", + "integrity": "sha512-FQ2YyM9/6y3clWkf3d60/W4c/HZy61hbfIsR4KIh8aGOifwfIx/UpZQ61pCr/TXTNqbaAVU2/sK+J1zFkGEoLw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.665.0", + "@aws-sdk/credential-provider-node": "3.665.0", + "@aws-sdk/middleware-host-header": "3.664.0", + "@aws-sdk/middleware-logger": "3.664.0", + "@aws-sdk/middleware-recursion-detection": "3.664.0", + "@aws-sdk/middleware-user-agent": "3.664.0", + "@aws-sdk/region-config-resolver": "3.664.0", + "@aws-sdk/types": "3.664.0", + "@aws-sdk/util-endpoints": "3.664.0", + "@aws-sdk/util-user-agent-browser": "3.664.0", + "@aws-sdk/util-user-agent-node": "3.664.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.7", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.22", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.22", + "@smithy/util-defaults-mode-node": "^3.0.22", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.665.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.665.0.tgz", + "integrity": "sha512-/OQEaWB1euXhZ/hV+wetDw1tynlrkNKzirzoiFuJ1EQsiIb9Ih/qjUF9KLdF1+/bXbnGu5YvIaAx80YReUchjg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.665.0", + "@aws-sdk/core": "3.665.0", + "@aws-sdk/credential-provider-node": "3.665.0", + "@aws-sdk/middleware-host-header": "3.664.0", + "@aws-sdk/middleware-logger": "3.664.0", + "@aws-sdk/middleware-recursion-detection": "3.664.0", + "@aws-sdk/middleware-user-agent": "3.664.0", + "@aws-sdk/region-config-resolver": "3.664.0", + "@aws-sdk/types": "3.664.0", + "@aws-sdk/util-endpoints": "3.664.0", + "@aws-sdk/util-user-agent-browser": "3.664.0", + "@aws-sdk/util-user-agent-node": "3.664.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.7", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.22", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.22", + "@smithy/util-defaults-mode-node": "^3.0.22", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.665.0.tgz", + "integrity": "sha512-nqmNNf7Ml7qDXTIisDv+OYe/rl3nAW4cmR+HxrOCWdhTHe8xRdR5c45VPoh8nv1KIry5xtd+iqPrzzjydes+Og==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/core": "^2.4.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.664.0.tgz", + "integrity": "sha512-95rE+9Voaco0nmKJrXqfJAxSSkSWqlBy76zomiZrUrv7YuijQtHCW8jte6v6UHAFAaBzgFsY7QqBxs15u9SM7g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.664.0.tgz", + "integrity": "sha512-svaPwVfWV3g/qjd4cYHTUyBtkdOwcVjC+tSj6EjoMrpZwGUXcCbYe04iU0ARZ6tuH/u3vySbTLOGjSa7g8o9Qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.665.0.tgz", + "integrity": "sha512-CSWBV5GqCkK78TTXq6qx40MWCt90t8rS/O7FIR4nbmoUhG/DysaC1G0om1fSx6k+GWcvIIIsSvD4hdbh8FRWKA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.664.0", + "@aws-sdk/credential-provider-http": "3.664.0", + "@aws-sdk/credential-provider-process": "3.664.0", + "@aws-sdk/credential-provider-sso": "3.665.0", + "@aws-sdk/credential-provider-web-identity": "3.664.0", + "@aws-sdk/types": "3.664.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.665.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.665.0.tgz", + "integrity": "sha512-cmJfVi4IM0WaKMQvPXhiS5mdIZyCoa04I3D+IEKpD2GAuVZa6tgwqfPyaApFDLjyedGGNFkC4MRgAjCcCl4WFg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.664.0", + "@aws-sdk/credential-provider-http": "3.664.0", + "@aws-sdk/credential-provider-ini": "3.665.0", + "@aws-sdk/credential-provider-process": "3.664.0", + "@aws-sdk/credential-provider-sso": "3.665.0", + "@aws-sdk/credential-provider-web-identity": "3.664.0", + "@aws-sdk/types": "3.664.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.664.0.tgz", + "integrity": "sha512-sQicIw/qWTsmMw8EUQNJXdrWV5SXaZc2zGdCQsQxhR6wwNO2/rZ5JmzdcwUADmleBVyPYk3KGLhcofF/qXT2Ng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.665.0.tgz", + "integrity": "sha512-Xe8WW4r70bsetGQG3azFeK/gd+Q4OmNiidtRrG64y/V9TIvIqc7Y/yUZNhEgFkpG19o188VmXg/ulnG3E+MvLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.665.0", + "@aws-sdk/token-providers": "3.664.0", + "@aws-sdk/types": "3.664.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.664.0.tgz", + "integrity": "sha512-10ltP1BfSKRJVXd8Yr5oLbo+VSDskWbps0X3szSsxTk0Dju1xvkz7hoIjylWLvtGbvQ+yb2pmsJYKCudW/4DJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.664.0" + } + }, + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.572.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.572.0.tgz", + "integrity": "sha512-CzuRWMj/xtN9p9eP915nlPmlyniTzke732Ow/M60++gGgB3W+RtZyFftw3TEx+NzNhd1tH54dEcGiWdiNaBz3Q==", + "license": "Apache-2.0", + "dependencies": { + "mnemonist": "0.38.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/lib-dynamodb": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.665.0.tgz", + "integrity": "sha512-L1Za773RvqxFN8j2E9gVjELSgf3pWUWi1VUOMSmAwJQYb9z+yS7skxyYk22IVWWP3gkYaYY+N73ypaTn3kmYOg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/util-dynamodb": "3.665.0", + "@smithy/core": "^2.4.7", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.665.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.664.0.tgz", + "integrity": "sha512-K7I1eM9zaRNtOUo+jf8M2ZkTsncWhY/gMMuJubNbj7ceym3LzShougrbxTZl/OOug5Vcmn8A3aOn7gIpq9WT1A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/endpoint-cache": "3.572.0", + "@aws-sdk/types": "3.664.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.664.0.tgz", + "integrity": "sha512-4tCXJ+DZWTq38eLmFgnEmO8X4jfWpgPbWoCyVYpRHCPHq6xbrU65gfwS9jGx25L4YdEce641ChI9TKLryuUgRA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.664.0.tgz", + "integrity": "sha512-eNykMqQuv7eg9pAcaLro44fscIe1VkFfhm+gYnlxd+PH6xqapRki1E68VHehnIptnVBdqnWfEqLUSLGm9suqhg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.664.0.tgz", + "integrity": "sha512-jq27WMZhm+dY8BWZ9Ipy3eXtZj0lJzpaKQE3A3tH5AOIlUV/gqrmnJ9CdqVVef4EJsq9Yil4ZzQjKKmPsxveQg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.664.0.tgz", + "integrity": "sha512-Kp5UwXwayO6d472nntiwgrxqay2KS9ozXNmKjQfDrUWbEzvgKI+jgKNMia8MMnjSxYoBGpQ1B8NGh8a6KMEJJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@aws-sdk/util-endpoints": "3.664.0", + "@smithy/core": "^2.4.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.664.0.tgz", + "integrity": "sha512-o/B8dg8K+9714RGYPgMxZgAChPe/MTSMkf/eHXTUFHNik5i1HgVKfac22njV2iictGy/6GhpFsKa1OWNYAkcUg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.664.0.tgz", + "integrity": "sha512-dBAvXW2/6bAxidvKARFxyCY2uCynYBKRFN00NhS1T5ggxm3sUnuTpWw1DTjl02CVPkacBOocZf10h8pQbHSK8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.664.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.664.0.tgz", + "integrity": "sha512-+GtXktvVgpreM2b+NJL9OqZGsOzHwlCUrO8jgQUvH/yA6Kd8QO2YFhQCp0C9sSzTteZJVqGBu8E0CQurxJHPbw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-dynamodb": { + "version": "3.665.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.665.0.tgz", + "integrity": "sha512-u93VA9AtkpME/G+I6bGeiGGyqRt6fH5khPI2kBDpq3ABr6WYsRtwIEZvARP4bB/ZNwV7GlGi8bgaeQHt19Sfbw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.665.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.664.0.tgz", + "integrity": "sha512-KrXoHz6zmAahVHkyWMRT+P6xJaxItgmklxEDrT+npsUB4d5C/lhw16Crcp9TDi828fiZK3GYKRAmmNhvmzvBNg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.664.0.tgz", + "integrity": "sha512-c/PV3+f1ss4PpskHbcOxTZ6fntV2oXy/xcDR9nW+kVaz5cM1G702gF0rvGLKPqoBwkj2rWGe6KZvEBeLzynTUQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.664.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.664.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.664.0.tgz", + "integrity": "sha512-l/m6KkgrTw1p/VTJTk0IoP9I2OnpWp3WbBgzxoNeh9cUcxTufIn++sBxKj5hhDql57LKWsckScG/MhFuH0vZZA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.664.0", + "@aws-sdk/types": "3.664.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", @@ -6281,176 +7026,800 @@ "@octokit/types": "^9.2.3" }, "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=4" + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=4" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", + "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", + "dev": true, + "dependencies": { + "@octokit/types": "^10.0.0" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", + "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", + "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/request-error": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", + "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^9.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/request/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@octokit/rest": { + "version": "19.0.13", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", + "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", + "dev": true, + "dependencies": { + "@octokit/core": "^4.2.1", + "@octokit/plugin-paginate-rest": "^6.1.2", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^7.1.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/tsconfig": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", + "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==", + "dev": true + }, + "node_modules/@octokit/types": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", + "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", + "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, + "node_modules/@simple-dom/interface": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@simple-dom/interface/-/interface-1.4.0.tgz", + "integrity": "sha512-l5qumKFWU0S+4ZzMaLXFU8tQZsicHEMEyAxI5kDFGhJsRqDwe0a7/iPA/GdxlGyDKseQQAgIz5kzU7eXTrlSpA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons/node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.5.tgz", + "integrity": "sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.9.tgz", + "integrity": "sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.8.tgz", + "integrity": "sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.4.tgz", + "integrity": "sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.7.tgz", + "integrity": "sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.7.tgz", + "integrity": "sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.9.tgz", + "integrity": "sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.4.tgz", + "integrity": "sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.23.tgz", + "integrity": "sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/service-error-classification": "^3.0.7", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.7.tgz", + "integrity": "sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.7.tgz", + "integrity": "sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.4.tgz", + "integrity": "sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.5", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.7.tgz", + "integrity": "sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.4.tgz", + "integrity": "sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.7.tgz", + "integrity": "sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.7.tgz", + "integrity": "sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.7.tgz", + "integrity": "sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.0.tgz", + "integrity": "sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.0.tgz", + "integrity": "sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.5.0.tgz", + "integrity": "sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "dev": true, - "peerDependencies": { - "@octokit/core": ">=3" + "node_modules/@smithy/url-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.7.tgz", + "integrity": "sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", - "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", - "dev": true, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", "dependencies": { - "@octokit/types": "^10.0.0" + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=3" + "node": ">=16.0.0" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", - "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", - "dev": true, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "tslib": "^2.6.2" } }, - "node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", - "dev": true, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">=16.0.0" } }, - "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dev": true, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">=16.0.0" } }, - "node_modules/@octokit/request/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/@octokit/rest": { - "version": "19.0.13", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", - "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", - "dev": true, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.23.tgz", + "integrity": "sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw==", + "license": "Apache-2.0", "dependencies": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">= 10.0.0" } }, - "node_modules/@octokit/tsconfig": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", - "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==", - "dev": true - }, - "node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dev": true, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.23.tgz", + "integrity": "sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg==", + "license": "Apache-2.0", "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@smithy/config-resolver": "^3.0.9", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@parcel/watcher": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", - "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", - "dev": true, - "hasInstallScript": true, + "node_modules/@smithy/util-endpoints": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.3.tgz", + "integrity": "sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==", + "license": "Apache-2.0", "dependencies": { - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "dev": true, + "node_modules/@smithy/util-middleware": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.7.tgz", + "integrity": "sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==", + "license": "Apache-2.0", "dependencies": { - "@hapi/hoek": "^9.0.0" + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "dev": true + "node_modules/@smithy/util-retry": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.7.tgz", + "integrity": "sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "dev": true + "node_modules/@smithy/util-stream": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.9.tgz", + "integrity": "sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@simple-dom/interface": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@simple-dom/interface/-/interface-1.4.0.tgz", - "integrity": "sha512-l5qumKFWU0S+4ZzMaLXFU8tQZsicHEMEyAxI5kDFGhJsRqDwe0a7/iPA/GdxlGyDKseQQAgIz5kzU7eXTrlSpA==", - "dev": true + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, + "node_modules/@smithy/util-waiter": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.6.tgz", + "integrity": "sha512-xs/KAwWOeCklq8aMlnpk25LgxEYHKOEodfjfKclDMLcBJEVEKzDLxZxBQyztcuPJ7F54213NJS8PxoiHNMdItQ==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@smithy/abort-controller": "^3.1.5", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, "node_modules/@tootallnate/once": { @@ -6637,6 +8006,21 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, + "node_modules/@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "license": "MIT" + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -7240,6 +8624,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sdk-client-mock": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/aws-sdk-client-mock/-/aws-sdk-client-mock-4.0.2.tgz", + "integrity": "sha512-saFLXQPqHuMH0A1peNIGoAFEq9B0bpS5y5qrr+Y5F86MasVkCctggHKhHPRVjGr852Nz7cLg/PBxKs6lQoK3mg==", + "license": "MIT", + "dependencies": { + "@types/sinon": "^17.0.3", + "sinon": "^18.0.1", + "tslib": "^2.1.0" + } + }, "node_modules/axios": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", @@ -7558,6 +8953,12 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -9955,6 +11356,28 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -14207,6 +15630,12 @@ "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", "dev": true }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -15250,6 +16679,12 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "license": "MIT" + }, "node_modules/lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -16068,6 +17503,15 @@ "node": ">=10" } }, + "node_modules/mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "license": "MIT", + "dependencies": { + "obliterator": "^1.6.1" + } + }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -16175,6 +17619,37 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/nise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", + "just-extend": "^6.2.0", + "path-to-regexp": "^8.1.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.2.tgz", + "integrity": "sha512-4Bb+oqXZTSTZ1q27Izly9lv8B9dlV61CROxPiVtywwzv5SnytJqhvYe6FclHYuXml4cd1VHPo1zd5PmTeJozvA==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -17619,6 +19094,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -18281,6 +19762,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -20264,6 +21754,72 @@ "joi": "^17.3.0" } }, + "node_modules/sinon": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-18.0.1.tgz", + "integrity": "sha512-a2N2TDY1uGviajJ6r4D1CyRAkzE9NNVlYOV1wX5xQDuAk0ONgzgRl0EjCQuRCPxOwp13ghsMwt9Gdldujs39qw==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.2.0", + "nise": "^6.0.0", + "supports-color": "^7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -20996,6 +22552,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, "node_modules/strong-log-transformer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", @@ -21465,8 +23027,7 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -21505,7 +23066,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, "engines": { "node": ">=4" } diff --git a/package.json b/package.json index 3a03085ab..120d2050d 100644 --- a/package.json +++ b/package.json @@ -105,5 +105,10 @@ "testEnvironment": "node", "testRunner": "jest-circus/runner", "silent": true + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.665.0", + "@aws-sdk/lib-dynamodb": "^3.665.0", + "aws-sdk-client-mock": "^4.0.2" } } From 9b95c697635821c81d013f10887825defe2b2666 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Mon, 7 Oct 2024 20:37:58 +0100 Subject: [PATCH 02/17] upgrade in document-client-decorator --- .../document-client-decorator.spec.js | 126 ++++++------------ .../src/documentclient-decorator.js | 29 ++-- 2 files changed, 57 insertions(+), 98 deletions(-) diff --git a/packages/connectors-lib/src/__tests__/document-client-decorator.spec.js b/packages/connectors-lib/src/__tests__/document-client-decorator.spec.js index affc3fc8e..34f62c6af 100644 --- a/packages/connectors-lib/src/__tests__/document-client-decorator.spec.js +++ b/packages/connectors-lib/src/__tests__/document-client-decorator.spec.js @@ -1,85 +1,55 @@ -import AWSSdk from 'aws-sdk' +import { DynamoDBDocumentClient, QueryCommand, ScanCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb' +import { mockClient } from 'aws-sdk-client-mock' import AWS from '../aws.js' const { docClient } = AWS() describe('document client decorations', () => { + const ddbMock = mockClient(DynamoDBDocumentClient) + beforeEach(() => { + ddbMock.reset() + }) + it('deals with pagination where DynamoDB returns a LastEvaluatedKey in a query response', async () => { const testLastEvaluatedKey = { id: '16324258-85-92746491' } - AWSSdk.DynamoDB.DocumentClient.__setNextResponses( - 'query', - { - Items: [], - LastEvaluatedKey: testLastEvaluatedKey - }, - { - Items: [] - } - ) - await docClient.queryAllPromise({ - TableName: 'TEST' - }) - expect(AWSSdk.DynamoDB.DocumentClient.mockedMethods.query).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - TableName: 'TEST' - }) - ) - expect(AWSSdk.DynamoDB.DocumentClient.mockedMethods.query).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - TableName: 'TEST', - ExclusiveStartKey: testLastEvaluatedKey - }) - ) + // mock QueryCommand to return items with a LastEvaluatedKey + ddbMock.on(QueryCommand).resolvesOnce({ Items: [], LastEvaluatedKey: testLastEvaluatedKey }).resolvesOnce({ Items: [] }) + + await docClient.queryAllPromise({ TableName: 'TEST' }) + + // check QueryCommand was called twice & with the correct parameters + expect(ddbMock.send.callCount).toBe(2) + expect(ddbMock.send.firstCall.args[0].input.TableName).toEqual('TEST') + expect(ddbMock.send.secondCall.args[0].input.ExclusiveStartKey).toEqual(testLastEvaluatedKey) }) it('deals with pagination where DynamoDB returns a LastEvaluatedKey in a scan response', async () => { const testLastEvaluatedKey = { id: '16324258-85-92746491' } - AWSSdk.DynamoDB.DocumentClient.__setNextResponses( - 'scan', - { - Items: [], - LastEvaluatedKey: testLastEvaluatedKey - }, - { - Items: [] - } - ) - await docClient.scanAllPromise({ - TableName: 'TEST' - }) - expect(AWSSdk.DynamoDB.DocumentClient.mockedMethods.scan).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - TableName: 'TEST' - }) - ) - expect(AWSSdk.DynamoDB.DocumentClient.mockedMethods.scan).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - TableName: 'TEST', - ExclusiveStartKey: testLastEvaluatedKey - }) - ) + // mock ScanCommand to return items with a LastEvaluatedKey + ddbMock.on(ScanCommand).resolvesOnce({ Items: [], LastEvaluatedKey: testLastEvaluatedKey }).resolvesOnce({ Items: [] }) + + await docClient.scanAllPromise({ TableName: 'TEST' }) + + // check ScanCommand was called twicce & with the correct parameters + expect(ddbMock.send.callCount).toBe(2) + expect(ddbMock.send.firstCall.args[0].input.TableName).toEqual('TEST') + expect(ddbMock.send.secondCall.args[0].input.ExclusiveStartKey).toEqual(testLastEvaluatedKey) }) it('deals with UnprocessedItems when making batchWrite requests to DynamoDB', async () => { - AWSSdk.DynamoDB.DocumentClient.__setNextResponses( - 'batchWrite', - { + // mock BatchWriteCommand to return UnprocessedItems + ddbMock + .on(BatchWriteCommand) + .resolvesOnce({ UnprocessedItems: { NameOfTableToUpdate: [ { PutRequest: { Item: { key: '1', field: 'data1' } } }, { PutRequest: { Item: { key: '2', field: 'data2' } } } ] } - }, - { - UnprocessedItems: null - } - ) + }) + .resolvesOnce({ UnprocessedItems: null }) await docClient.batchWriteAllPromise({ RequestItems: { NameOfTableToUpdate: [ @@ -89,30 +59,11 @@ describe('document client decorations', () => { ] } }) - expect(AWSSdk.DynamoDB.DocumentClient.mockedMethods.batchWrite).toHaveBeenCalledTimes(2) - expect(AWSSdk.DynamoDB.DocumentClient.mockedMethods.batchWrite).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - RequestItems: { - NameOfTableToUpdate: [ - { PutRequest: { Item: { key: '1', field: 'data1' } } }, - { PutRequest: { Item: { key: '2', field: 'data2' } } }, - { PutRequest: { Item: { key: '3', field: 'data3' } } } - ] - } - }) - ) - expect(AWSSdk.DynamoDB.DocumentClient.mockedMethods.batchWrite).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - RequestItems: { - NameOfTableToUpdate: [ - { PutRequest: { Item: { key: '1', field: 'data1' } } }, - { PutRequest: { Item: { key: '2', field: 'data2' } } } - ] - } - }) - ) + + // check BatchWriteCommand was called twice & with the correct parameters + expect(ddbMock.send.callCount).toBe(2) + expect(ddbMock.send.firstCall.args[0].input.RequestItems.NameOfTableToUpdate).toHaveLength(3) + expect(ddbMock.send.secondCall.args[0].input.RequestItems.NameOfTableToUpdate).toHaveLength(2) }) it('deals with UnprocessedItems when making batchWrite requests to DynamoDB up to the given retry limit', async () => { @@ -124,7 +75,8 @@ describe('document client decorations', () => { ] } }) - AWSSdk.DynamoDB.DocumentClient.__setNextResponses('batchWrite', ...batchWriteResponses) + ddbMock.on(BatchWriteCommand).resolves(...batchWriteResponses) + const request = { RequestItems: { NameOfTableToUpdate: [ @@ -137,7 +89,7 @@ describe('document client decorations', () => { // Don't delay on setTimeouts! jest.spyOn(global, 'setTimeout').mockImplementation(cb => cb()) await expect(docClient.batchWriteAllPromise(request)).rejects.toThrow( - 'Failed to write items to DynamoDB using batch write. UnprocessedItems were returned and maxRetries has been reached.' + 'Failed to write items to DynamoDB using batch write. UnprocessedItems were returned and maxRetries has been reached.' ) }) diff --git a/packages/connectors-lib/src/documentclient-decorator.js b/packages/connectors-lib/src/documentclient-decorator.js index b648cf028..3e0a84520 100644 --- a/packages/connectors-lib/src/documentclient-decorator.js +++ b/packages/connectors-lib/src/documentclient-decorator.js @@ -1,10 +1,14 @@ import db from 'debug' -import AWS from 'aws-sdk' -const { DynamoDB } = AWS +import { DynamoDB } from '@aws-sdk/client-dynamodb' +import { DynamoDBDocument, BatchWriteCommand, QueryCommand, ScanCommand } from '@aws-sdk/lib-dynamodb' const debug = db('connectors:aws') -export const createDocumentClient = options => { - const docClient = new DynamoDB.DocumentClient(options) +export const createDocumentClient = (options = {}) => { + const client = new DynamoDB({ + ...options, + region: options.region || process.env.AWS_REGION || 'eu-west-2' + }) + const docClient = DynamoDBDocument.from(client) // Support for large query/scan operations which return results in pages const wrapPagedDocumentClientOperation = operationName => { @@ -12,10 +16,13 @@ export const createDocumentClient = options => { const items = [] let lastEvaluatedKey = null do { - const response = await docClient[operationName]({ - ...params, - ...(lastEvaluatedKey && { ExclusiveStartKey: lastEvaluatedKey }) - }).promise() + const Command = operationName === 'query' ? QueryCommand : ScanCommand + const response = await docClient.send( + new Command({ + ...params, + ...(lastEvaluatedKey && { ExclusiveStartKey: lastEvaluatedKey }) + }) + ) lastEvaluatedKey = response.LastEvaluatedKey response.Items && items.push(...response.Items) } while (lastEvaluatedKey) @@ -37,18 +44,18 @@ export const createDocumentClient = options => { let unprocessedItemsDelay = 500 let maxRetries = 10 while (hasUnprocessedItems) { - const result = await docClient.batchWrite(request).promise() + const result = await docClient.send(new BatchWriteCommand(request)) hasUnprocessedItems = !!Object.keys(result.UnprocessedItems ?? {}).length if (hasUnprocessedItems) { request = { ...params, RequestItems: result.UnprocessedItems } if (maxRetries-- === 0) { throw new Error( - 'Failed to write items to DynamoDB using batch write. UnprocessedItems were returned and maxRetries has been reached.' + 'Failed to write items to DynamoDB using batch write. UnprocessedItems were returned and maxRetries has been reached.' ) } await new Promise(resolve => setTimeout(resolve, unprocessedItemsDelay)) unprocessedItemsDelay = Math.min(2500, unprocessedItemsDelay * 1.5) - debug('Replaying DynamoDB batchWrite operation due to UnprocessedItems: %o', params.RequestItems) + debug('Replaying DynamoDB batchWrite operation due to UnprocessedItems: %o', request.RequestItems) } } } From 3a7334d715230dc2b214b1d096ed445a556bd95b Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:14:14 +0100 Subject: [PATCH 03/17] Install aws sdk mock --- package-lock.json | 211 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 212 insertions(+) diff --git a/package-lock.json b/package-lock.json index 33e83595f..5db8aa7a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@ksmithut/prettier-standard": "^0.0.10", "@types/hapi__hapi": "^20.0.10", "@types/jest": "^27.4.0", + "aws-sdk-mock": "^6.2.0", "babel-jest": "^27.5.1", "clone-deep": "^4.0.1", "dynamics-web-api": "^1.7.3", @@ -8624,6 +8625,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sdk": { + "version": "2.1691.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1691.0.tgz", + "integrity": "sha512-/F2YC+DlsY3UBM2Bdnh5RLHOPNibS/+IcjUuhP8XuctyrN+MlL+fWDAiela32LTDk7hMy4rx8MTgvbJ+0blO5g==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/aws-sdk-client-mock": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/aws-sdk-client-mock/-/aws-sdk-client-mock-4.0.2.tgz", @@ -8635,6 +8659,57 @@ "tslib": "^2.1.0" } }, + "node_modules/aws-sdk-mock": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/aws-sdk-mock/-/aws-sdk-mock-6.2.0.tgz", + "integrity": "sha512-3l1KWjriaIKy9CPxnPB5Bx/DW57Ex4p+S8AIGN6JKAR14+lXSyC/rbKz26FaYDHrquNLogAF2ap83f8iwjPCmw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sdk": "^2.1231.0", + "neotraverse": "^0.6.15", + "sinon": "^18.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/aws-sdk/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/aws-sdk/node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/aws-sdk/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/axios": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", @@ -11240,6 +11315,16 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/execa": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", @@ -12824,6 +12909,23 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -13004,6 +13106,22 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -15378,6 +15496,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/joi": { "version": "17.11.0", "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", @@ -17619,6 +17747,16 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/nise": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", @@ -20804,6 +20942,16 @@ "teleport": ">=0.2.0" } }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -21615,6 +21763,13 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "dev": true, + "license": "ISC" + }, "node_modules/saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", @@ -23455,6 +23610,17 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -23465,6 +23631,27 @@ "requires-port": "^1.0.0" } }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -24006,6 +24193,30 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", diff --git a/package.json b/package.json index 120d2050d..43b916275 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@ksmithut/prettier-standard": "^0.0.10", "@types/hapi__hapi": "^20.0.10", "@types/jest": "^27.4.0", + "aws-sdk-mock": "^6.2.0", "babel-jest": "^27.5.1", "clone-deep": "^4.0.1", "dynamics-web-api": "^1.7.3", From 81ba18a9f5a5043c2c15c79938f04b880ae21f0a Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:56:52 +0100 Subject: [PATCH 04/17] Mocked 'readFile' of 'fs_1.promises' --- package-lock.json | 1 + package.json | 1 + .../src/transport/__tests__/s3.spec.js | 15 ++++++++++++++- packages/pocl-job/src/io/__tests__/s3.spec.js | 17 ++++++++++++++++- .../__tests__/pocl-data-staging.spec.js | 16 +++++++++++++++- .../src/transport/__tests__/ftp-to-s3.spec.js | 18 +++++++++++++++++- .../transport/__tests__/s3-to-local.spec.js | 16 +++++++++++++++- 7 files changed, 79 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5db8aa7a5..b4c5f994c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "dependencies": { "@aws-sdk/client-dynamodb": "^3.665.0", "@aws-sdk/lib-dynamodb": "^3.665.0", + "@smithy/shared-ini-file-loader": "^3.1.8", "aws-sdk-client-mock": "^4.0.2" }, "devDependencies": { diff --git a/package.json b/package.json index 43b916275..4fbe80762 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "dependencies": { "@aws-sdk/client-dynamodb": "^3.665.0", "@aws-sdk/lib-dynamodb": "^3.665.0", + "@smithy/shared-ini-file-loader": "^3.1.8", "aws-sdk-client-mock": "^4.0.2" } } diff --git a/packages/fulfilment-job/src/transport/__tests__/s3.spec.js b/packages/fulfilment-job/src/transport/__tests__/s3.spec.js index dcf2e599a..a6c3502f6 100644 --- a/packages/fulfilment-job/src/transport/__tests__/s3.spec.js +++ b/packages/fulfilment-job/src/transport/__tests__/s3.spec.js @@ -4,7 +4,20 @@ import AwsMock from 'aws-sdk' import { FulfilmentRequestFile } from '@defra-fish/dynamics-lib' import { fulfilmentDataTransformer } from '../../transform/fulfilment-transform.js' -jest.mock('fs') +jest.mock('fs', () => { + const originalFs = jest.requireActual('fs') + return { + ...originalFs, + promises: { + readFile: jest.fn().mockResolvedValue('mocked file content') + }, + createWriteStream: jest.fn(() => ({ + on: jest.fn(), + end: jest.fn() + })) + } +}) + jest.mock('stream') jest.mock('../../config.js', () => ({ s3: { diff --git a/packages/pocl-job/src/io/__tests__/s3.spec.js b/packages/pocl-job/src/io/__tests__/s3.spec.js index 4a1a2da22..5a9515848 100644 --- a/packages/pocl-job/src/io/__tests__/s3.spec.js +++ b/packages/pocl-job/src/io/__tests__/s3.spec.js @@ -7,7 +7,22 @@ import fs from 'fs' import AwsMock from 'aws-sdk' import { mockedFtpMethods } from 'ssh2-sftp-client' -jest.mock('fs') +jest.mock('fs', () => { + const originalFs = jest.requireActual('fs') + return { + ...originalFs, + promises: { + readFile: jest.fn().mockResolvedValue('mocked file content') + }, + createWriteStream: jest.fn(() => ({ + on: jest.fn(), + end: jest.fn() + })), + createReadStream: jest.fn(() => 'mocked stream'), + statSync: jest.fn(() => ({ size: 1024 })) + } +}) + jest.mock('md5-file') jest.mock('../../io/db.js') jest.mock('../../io/file.js') diff --git a/packages/pocl-job/src/staging/__tests__/pocl-data-staging.spec.js b/packages/pocl-job/src/staging/__tests__/pocl-data-staging.spec.js index 20f5ff325..6526f27fd 100644 --- a/packages/pocl-job/src/staging/__tests__/pocl-data-staging.spec.js +++ b/packages/pocl-job/src/staging/__tests__/pocl-data-staging.spec.js @@ -7,10 +7,24 @@ import { finaliseTransactions } from '../finalise-transactions.js' import { getFileRecord, updateFileStagingTable } from '../../io/db.js' import fs from 'fs' +jest.mock('fs', () => { + const originalFs = jest.requireActual('fs') + return { + ...originalFs, + promises: { + readFile: jest.fn().mockResolvedValue('mocked file content') + }, + createWriteStream: jest.fn(() => ({ + on: jest.fn(), + end: jest.fn() + })), + statSync: jest.fn(() => ({ size: 1024 })) + } +}) + jest.mock('../create-transactions.js') jest.mock('../finalise-transactions.js') jest.mock('../../io/db.js') -jest.mock('fs') jest.mock('md5-file', () => () => 'test-md5') jest.mock('@defra-fish/connectors-lib', () => { diff --git a/packages/pocl-job/src/transport/__tests__/ftp-to-s3.spec.js b/packages/pocl-job/src/transport/__tests__/ftp-to-s3.spec.js index 7b5259da2..f79b7e73f 100644 --- a/packages/pocl-job/src/transport/__tests__/ftp-to-s3.spec.js +++ b/packages/pocl-job/src/transport/__tests__/ftp-to-s3.spec.js @@ -9,7 +9,23 @@ import md5File from 'md5-file' import AwsMock from 'aws-sdk' import { mockedFtpMethods } from 'ssh2-sftp-client' -jest.mock('fs') +jest.mock('fs', () => { + const originalFs = jest.requireActual('fs') + return { + ...originalFs, + promises: { + readFile: jest.fn().mockResolvedValue('mocked file content') + }, + createWriteStream: jest.fn(() => ({ + on: jest.fn(), + end: jest.fn() + })), + createReadStream: jest.fn(() => 'mocked stream'), + statSync: jest.fn(() => ({ size: 1024 })), + unlinkSync: jest.fn() + } +}) + jest.mock('md5-file') jest.mock('../../io/db.js') jest.mock('../../io/file.js') diff --git a/packages/pocl-job/src/transport/__tests__/s3-to-local.spec.js b/packages/pocl-job/src/transport/__tests__/s3-to-local.spec.js index 5b2dfe963..d288fea74 100644 --- a/packages/pocl-job/src/transport/__tests__/s3-to-local.spec.js +++ b/packages/pocl-job/src/transport/__tests__/s3-to-local.spec.js @@ -3,7 +3,21 @@ import stream from 'stream' import AwsMock from 'aws-sdk' const MOCK_TMP = '/tmp/local/mock' -jest.mock('fs') + +jest.mock('fs', () => { + const originalFs = jest.requireActual('fs') + return { + ...originalFs, + promises: { + readFile: jest.fn().mockResolvedValue('mocked file content') + }, + createWriteStream: jest.fn(() => ({ + on: jest.fn(), + end: jest.fn() + })) + } +}) + jest.mock('stream') jest.mock('../../io/file.js', () => ({ getTempDir: jest.fn((...subfolders) => `${MOCK_TMP}/${subfolders.join('/')}`) From 07e0adb5c8efb60f6c66adaf71219956da038735 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:22:50 +0100 Subject: [PATCH 05/17] update aws --- packages/connectors-lib/src/aws.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/connectors-lib/src/aws.js b/packages/connectors-lib/src/aws.js index 8a157fda8..a3c7dd601 100644 --- a/packages/connectors-lib/src/aws.js +++ b/packages/connectors-lib/src/aws.js @@ -1,16 +1,20 @@ import Config from './config.js' +import { DynamoDB } from '@aws-sdk/client-dynamodb' import { createDocumentClient } from './documentclient-decorator.js' import AWS from 'aws-sdk' -const { DynamoDB, SQS, S3, SecretsManager } = AWS + +const { SQS, S3, SecretsManager } = AWS export default function () { + const dynamoDBInstance = new DynamoDB({ + apiVersion: '2012-08-10', + ...(Config.aws.dynamodb.endpoint && { + endpoint: Config.aws.dynamodb.endpoint + }) + }) + return { - ddb: new DynamoDB({ - apiVersion: '2012-08-10', - ...(Config.aws.dynamodb.endpoint && { - endpoint: Config.aws.dynamodb.endpoint - }) - }), + ddb: dynamoDBInstance, docClient: createDocumentClient({ convertEmptyValues: true, apiVersion: '2012-08-10', From edd306a41a662e1b3c60153f09bd80bc1566b819 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Thu, 17 Oct 2024 07:47:29 +0100 Subject: [PATCH 06/17] reverse head --- packages/connectors-lib/src/__tests__/aws.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/connectors-lib/src/__tests__/aws.spec.js b/packages/connectors-lib/src/__tests__/aws.spec.js index a3a9eccd9..1f3bda1f4 100644 --- a/packages/connectors-lib/src/__tests__/aws.spec.js +++ b/packages/connectors-lib/src/__tests__/aws.spec.js @@ -1,5 +1,6 @@ import Config from '../config.js' const TEST_ENDPOINT = 'http://localhost:8080' + jest.dontMock('aws-sdk') describe('aws connectors', () => { it('configures dynamodb with a custom endpoint if one is defined in configuration', async () => { From c8bf5352e403a0b5e11a099f7512c77fb3ce5d93 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Thu, 17 Oct 2024 10:24:22 +0100 Subject: [PATCH 07/17] mock dynanodb endpoints --- .../connectors-lib/src/__tests__/aws.spec.js | 123 ++++++++++++++---- 1 file changed, 96 insertions(+), 27 deletions(-) diff --git a/packages/connectors-lib/src/__tests__/aws.spec.js b/packages/connectors-lib/src/__tests__/aws.spec.js index 1f3bda1f4..b4d9c0626 100644 --- a/packages/connectors-lib/src/__tests__/aws.spec.js +++ b/packages/connectors-lib/src/__tests__/aws.spec.js @@ -1,59 +1,128 @@ -import Config from '../config.js' -const TEST_ENDPOINT = 'http://localhost:8080' +import { DynamoDB } from '@aws-sdk/client-dynamodb' +import AWS from 'aws-sdk' +import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb' +import Config from '../config' -jest.dontMock('aws-sdk') -describe('aws connectors', () => { - it('configures dynamodb with a custom endpoint if one is defined in configuration', async () => { - Config.aws.dynamodb.endpoint = TEST_ENDPOINT - const { ddb } = require('../aws.js').default() - expect(ddb.config.endpoint).toEqual(TEST_ENDPOINT) +jest.mock('aws-sdk', () => { + const SQS = jest.fn().mockImplementation((config) => ({ + config: { ...config, apiVersion: '2012-11-05', region: config.region || 'eu-west-2' } + })) + const S3 = jest.fn().mockImplementation((config) => ({ + config: { ...config, apiVersion: '2006-03-01', region: config.region || 'eu-west-2' } + })) + const SecretsManager = jest.fn().mockImplementation((config) => ({ + config: { ...config, apiVersion: '2017-10-17', region: config.region || 'eu-west-2' } + })) + + return { SQS, S3, SecretsManager } +}) + +jest.mock('@aws-sdk/client-dynamodb') +jest.mock('@aws-sdk/lib-dynamodb', () => ({ + DynamoDBDocument: { + from: jest.fn() + } +})) + +describe('AWS Connectors', () => { + let SQS, S3, SecretsManager + + beforeEach(() => { + DynamoDB.mockClear() + DynamoDBDocument.from.mockClear() + + DynamoDBDocument.from.mockReturnValue({ + send: jest.fn(), + queryAllPromise: jest.fn(), + scanAllPromise: jest.fn(), + batchWriteAllPromise: jest.fn(), + createUpdateExpression: jest.fn(), + }) + + SQS = AWS.SQS + S3 = AWS.S3 + SecretsManager = AWS.SecretsManager + + SQS.mockClear() + S3.mockClear() + SecretsManager.mockClear() }) - it('configures sqs with a custom endpoint if one is defined in configuration', async () => { - Config.aws.sqs.endpoint = TEST_ENDPOINT - const { sqs } = require('../aws.js').default() - expect(sqs.config.endpoint).toEqual(TEST_ENDPOINT) + it('configures dynamodb with a custom endpoint if one is defined in configuration', () => { + const TEST_ENDPOINT = 'http://localhost:8080' + Config.aws.dynamodb.endpoint = TEST_ENDPOINT + const { ddb, docClient } = require('../aws.js').default() + expect(DynamoDB).toHaveBeenCalledWith(expect.objectContaining({ + endpoint: TEST_ENDPOINT + })) + expect(DynamoDBDocument.from).toHaveBeenCalledWith(expect.any(DynamoDB)) }) - it('uses the default dynamodb endpoint if it is not overridden in configuration', async () => { + it('uses the default dynamodb endpoint if it is not overridden in configuration', () => { process.env.AWS_REGION = 'eu-west-2' delete Config.aws.dynamodb.endpoint - const { ddb } = require('../aws.js').default() - expect(ddb.config.endpoint).toEqual('dynamodb.eu-west-2.amazonaws.com') + const { ddb, docClient } = require('../aws.js').default() + expect(DynamoDB).toHaveBeenCalledWith(expect.objectContaining({ + region: 'eu-west-2' + })) + expect(DynamoDBDocument.from).toHaveBeenCalledWith(expect.any(DynamoDB)) + }) + + it('configures sqs with a custom endpoint if one is defined in configuration', () => { + const TEST_ENDPOINT = 'http://localhost:8080' + Config.aws.sqs.endpoint = TEST_ENDPOINT + const { sqs } = require('../aws.js').default() + expect(SQS).toHaveBeenCalledWith(expect.objectContaining({ + endpoint: TEST_ENDPOINT, + region: 'eu-west-2' + })) }) - it('uses the default sqs endpoint if it is not overridden in configuration', async () => { + it('uses the default sqs endpoint if it is not overridden in configuration', () => { process.env.AWS_REGION = 'eu-west-2' delete Config.aws.sqs.endpoint const { sqs } = require('../aws.js').default() - expect(sqs.config.endpoint).toEqual('sqs.eu-west-2.amazonaws.com') + expect(SQS).toHaveBeenCalledWith(expect.objectContaining({ + region: 'eu-west-2' + })) }) - it('configures s3 with a custom endpoint if one is defined in configuration', async () => { + it('configures s3 with a custom endpoint if one is defined in configuration', () => { + const TEST_ENDPOINT = 'http://localhost:8080' Config.aws.s3.endpoint = TEST_ENDPOINT const { s3 } = require('../aws.js').default() - expect(s3.config.endpoint).toEqual(TEST_ENDPOINT) - expect(s3.config.s3ForcePathStyle).toBeTruthy() + expect(S3).toHaveBeenCalledWith(expect.objectContaining({ + endpoint: TEST_ENDPOINT, + s3ForcePathStyle: true, + region: 'eu-west-2' + })) }) - it('uses default s3 settings if a custom endpoint is not defined', async () => { + it('uses default s3 settings if a custom endpoint is not defined', () => { process.env.AWS_REGION = 'eu-west-2' delete Config.aws.s3.endpoint const { s3 } = require('../aws.js').default() - expect(s3.config.endpoint).toEqual('s3.eu-west-2.amazonaws.com') - expect(s3.config.s3ForcePathStyle).toBeFalsy() + expect(S3).toHaveBeenCalledWith(expect.objectContaining({ + region: 'eu-west-2' + })) }) - it('configures secretsmanager with a custom endpoint if one is defined in configuration', async () => { + it('configures secretsmanager with a custom endpoint if one is defined in configuration', () => { + const TEST_ENDPOINT = 'http://localhost:8080' Config.aws.secretsManager.endpoint = TEST_ENDPOINT const { secretsManager } = require('../aws.js').default() - expect(secretsManager.config.endpoint).toEqual(TEST_ENDPOINT) + expect(SecretsManager).toHaveBeenCalledWith(expect.objectContaining({ + endpoint: TEST_ENDPOINT, + region: 'eu-west-2' + })) }) - it('uses default secretsmanager settings if a custom endpoint is not defined', async () => { + it('uses default secretsmanager settings if a custom endpoint is not defined', () => { process.env.AWS_REGION = 'eu-west-2' delete Config.aws.secretsManager.endpoint const { secretsManager } = require('../aws.js').default() - expect(secretsManager.config.endpoint).toEqual('secretsmanager.eu-west-2.amazonaws.com') + expect(SecretsManager).toHaveBeenCalledWith(expect.objectContaining({ + region: 'eu-west-2' + })) }) }) From 2c0e5a317a23f9dabe83286739666917a4b7ea96 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:14:34 +0100 Subject: [PATCH 08/17] update aws.spec.js --- .../connectors-lib/src/__tests__/aws.spec.js | 98 +++++++++++-------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/packages/connectors-lib/src/__tests__/aws.spec.js b/packages/connectors-lib/src/__tests__/aws.spec.js index b4d9c0626..422cfe1c0 100644 --- a/packages/connectors-lib/src/__tests__/aws.spec.js +++ b/packages/connectors-lib/src/__tests__/aws.spec.js @@ -4,13 +4,13 @@ import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb' import Config from '../config' jest.mock('aws-sdk', () => { - const SQS = jest.fn().mockImplementation((config) => ({ + const SQS = jest.fn().mockImplementation(config => ({ config: { ...config, apiVersion: '2012-11-05', region: config.region || 'eu-west-2' } })) - const S3 = jest.fn().mockImplementation((config) => ({ - config: { ...config, apiVersion: '2006-03-01', region: config.region || 'eu-west-2' } + const S3 = jest.fn().mockImplementation(config => ({ + config: { ...config, apiVersion: '2006-03-01', region: config.region || 'eu-west-2', s3ForcePathStyle: config.s3ForcePathStyle } })) - const SecretsManager = jest.fn().mockImplementation((config) => ({ + const SecretsManager = jest.fn().mockImplementation(config => ({ config: { ...config, apiVersion: '2017-10-17', region: config.region || 'eu-west-2' } })) @@ -36,7 +36,7 @@ describe('AWS Connectors', () => { queryAllPromise: jest.fn(), scanAllPromise: jest.fn(), batchWriteAllPromise: jest.fn(), - createUpdateExpression: jest.fn(), + createUpdateExpression: jest.fn() }) SQS = AWS.SQS @@ -51,78 +51,94 @@ describe('AWS Connectors', () => { it('configures dynamodb with a custom endpoint if one is defined in configuration', () => { const TEST_ENDPOINT = 'http://localhost:8080' Config.aws.dynamodb.endpoint = TEST_ENDPOINT - const { ddb, docClient } = require('../aws.js').default() - expect(DynamoDB).toHaveBeenCalledWith(expect.objectContaining({ - endpoint: TEST_ENDPOINT - })) + require('../aws.js').default() + expect(DynamoDB).toHaveBeenCalledWith( + expect.objectContaining({ + endpoint: TEST_ENDPOINT + }) + ) expect(DynamoDBDocument.from).toHaveBeenCalledWith(expect.any(DynamoDB)) }) it('uses the default dynamodb endpoint if it is not overridden in configuration', () => { process.env.AWS_REGION = 'eu-west-2' delete Config.aws.dynamodb.endpoint - const { ddb, docClient } = require('../aws.js').default() - expect(DynamoDB).toHaveBeenCalledWith(expect.objectContaining({ - region: 'eu-west-2' - })) + require('../aws.js').default() + expect(DynamoDB).toHaveBeenCalledWith( + expect.objectContaining({ + region: 'eu-west-2' + }) + ) expect(DynamoDBDocument.from).toHaveBeenCalledWith(expect.any(DynamoDB)) }) it('configures sqs with a custom endpoint if one is defined in configuration', () => { const TEST_ENDPOINT = 'http://localhost:8080' Config.aws.sqs.endpoint = TEST_ENDPOINT - const { sqs } = require('../aws.js').default() - expect(SQS).toHaveBeenCalledWith(expect.objectContaining({ - endpoint: TEST_ENDPOINT, - region: 'eu-west-2' - })) + require('../aws.js').default() + expect(SQS).toHaveBeenCalledWith( + expect.objectContaining({ + apiVersion: '2012-11-05', + endpoint: TEST_ENDPOINT + }) + ) }) it('uses the default sqs endpoint if it is not overridden in configuration', () => { process.env.AWS_REGION = 'eu-west-2' delete Config.aws.sqs.endpoint - const { sqs } = require('../aws.js').default() - expect(SQS).toHaveBeenCalledWith(expect.objectContaining({ - region: 'eu-west-2' - })) + require('../aws.js').default() + expect(SQS).toHaveBeenCalledWith( + expect.objectContaining({ + apiVersion: '2012-11-05' + }) + ) }) it('configures s3 with a custom endpoint if one is defined in configuration', () => { const TEST_ENDPOINT = 'http://localhost:8080' Config.aws.s3.endpoint = TEST_ENDPOINT - const { s3 } = require('../aws.js').default() - expect(S3).toHaveBeenCalledWith(expect.objectContaining({ - endpoint: TEST_ENDPOINT, - s3ForcePathStyle: true, - region: 'eu-west-2' - })) + require('../aws.js').default() + expect(S3).toHaveBeenCalledWith( + expect.objectContaining({ + apiVersion: '2006-03-01', + endpoint: TEST_ENDPOINT, + s3ForcePathStyle: true + }) + ) }) it('uses default s3 settings if a custom endpoint is not defined', () => { process.env.AWS_REGION = 'eu-west-2' delete Config.aws.s3.endpoint - const { s3 } = require('../aws.js').default() - expect(S3).toHaveBeenCalledWith(expect.objectContaining({ - region: 'eu-west-2' - })) + require('../aws.js').default() + expect(S3).toHaveBeenCalledWith( + expect.objectContaining({ + apiVersion: '2006-03-01' + }) + ) }) it('configures secretsmanager with a custom endpoint if one is defined in configuration', () => { const TEST_ENDPOINT = 'http://localhost:8080' Config.aws.secretsManager.endpoint = TEST_ENDPOINT - const { secretsManager } = require('../aws.js').default() - expect(SecretsManager).toHaveBeenCalledWith(expect.objectContaining({ - endpoint: TEST_ENDPOINT, - region: 'eu-west-2' - })) + require('../aws.js').default() + expect(SecretsManager).toHaveBeenCalledWith( + expect.objectContaining({ + apiVersion: '2017-10-17', + endpoint: TEST_ENDPOINT + }) + ) }) it('uses default secretsmanager settings if a custom endpoint is not defined', () => { process.env.AWS_REGION = 'eu-west-2' delete Config.aws.secretsManager.endpoint - const { secretsManager } = require('../aws.js').default() - expect(SecretsManager).toHaveBeenCalledWith(expect.objectContaining({ - region: 'eu-west-2' - })) + require('../aws.js').default() + expect(SecretsManager).toHaveBeenCalledWith( + expect.objectContaining({ + apiVersion: '2017-10-17' + }) + ) }) }) From b78db0f6e7465a07c13ae149315ad5af419516cc Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:34:19 +0100 Subject: [PATCH 09/17] implemented dynamo v3 to process-transaction-dlq --- .../__tests__/process-transaction-dlq.spec.js | 29 ++++++++++--------- .../transactions/process-transaction-dlq.js | 20 ++++++------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/sales-api-service/src/services/transactions/__tests__/process-transaction-dlq.spec.js b/packages/sales-api-service/src/services/transactions/__tests__/process-transaction-dlq.spec.js index 5a9613589..a4cc17046 100644 --- a/packages/sales-api-service/src/services/transactions/__tests__/process-transaction-dlq.spec.js +++ b/packages/sales-api-service/src/services/transactions/__tests__/process-transaction-dlq.spec.js @@ -2,9 +2,16 @@ import { processDlq } from '../process-transaction-dlq.js' import { retrieveStagedTransaction } from '../retrieve-transaction.js' import { createStagingExceptionFromError } from '../../exceptions/exceptions.service.js' import { TRANSACTION_STAGING_TABLE } from '../../../config.js' -import AwsMock from 'aws-sdk' +import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb' + +jest.mock('@aws-sdk/lib-dynamodb', () => ({ + DynamoDBDocument: { + from: jest.fn().mockReturnValue({ + update: jest.fn().mockResolvedValue({}) + }) + } +})) -let mockProcessingException jest.mock('../process-transaction-queue.js', () => ({ processQueue: async () => { if (mockProcessingException) { @@ -17,15 +24,12 @@ jest.mock('../retrieve-transaction.js', () => ({ retrieveStagedTransaction: jest.fn(async () => ({ testTransaction: true })) })) -jest.mock('@defra-fish/dynamics-lib', () => ({ - ...jest.requireActual('@defra-fish/dynamics-lib'), - persist: jest.fn() -})) - jest.mock('../../exceptions/exceptions.service.js') +let mockProcessingException + const expectDynamoDbTtlUpdate = () => { - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.update).toBeCalledWith( + expect(DynamoDBDocument.from().update).toBeCalledWith( expect.objectContaining({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id: 'test' }, @@ -45,7 +49,6 @@ describe('transaction service', () => { beforeEach(() => { mockProcessingException = new Error('Test error') jest.clearAllMocks() - AwsMock.__resetAll() }) describe('processDlq', () => { @@ -53,7 +56,7 @@ describe('transaction service', () => { mockProcessingException = null await processDlq({ id: 'test' }) expect(createStagingExceptionFromError).not.toBeCalled() - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.update).not.toBeCalled() + expect(DynamoDBDocument.from().update).not.toBeCalled() }) it('creates a staging exception if the retry attempt is not successful', async () => { @@ -62,17 +65,17 @@ describe('transaction service', () => { expectDynamoDbTtlUpdate() }) - it('handles exceptions originating DynamoDB', async () => { + it('handles exceptions originating from DynamoDB', async () => { const testDynamoDbException = new Error('DynamoDB error') retrieveStagedTransaction.mockRejectedValueOnce(testDynamoDbException) await processDlq({ id: 'test' }) expect(createStagingExceptionFromError).toBeCalledWith('test', testDynamoDbException, null) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.update).not.toHaveBeenCalled() + expect(DynamoDBDocument.from().update).not.toHaveBeenCalled() }) it('logs an exception if unable to update the TTL on the transaction in DynamoDB', async () => { const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) - AwsMock.DynamoDB.DocumentClient.__throwWithErrorOn('update') + DynamoDBDocument.from().update.mockRejectedValueOnce(new Error('DynamoDB error')) await processDlq({ id: 'test' }) expect(createStagingExceptionFromError).toBeCalledWith('test', mockProcessingException, { testTransaction: true }) expectDynamoDbTtlUpdate() diff --git a/packages/sales-api-service/src/services/transactions/process-transaction-dlq.js b/packages/sales-api-service/src/services/transactions/process-transaction-dlq.js index 8e40adde7..b0ada5aee 100644 --- a/packages/sales-api-service/src/services/transactions/process-transaction-dlq.js +++ b/packages/sales-api-service/src/services/transactions/process-transaction-dlq.js @@ -14,17 +14,15 @@ export async function processDlq ({ id }) { await createStagingExceptionFromError(id, exception, transaction) if (transaction) { try { - await docClient - .update({ - TableName: TRANSACTION_STAGING_TABLE.TableName, - Key: { id }, - ConditionExpression: 'attribute_exists(id)', - UpdateExpression: 'SET expires = :expires', - ExpressionAttributeValues: { - ':expires': Math.floor(Date.now() / 1000) + TRANSACTION_STAGING_TABLE.StagingErrorsTtl - } - }) - .promise() + await docClient.update({ + TableName: TRANSACTION_STAGING_TABLE.TableName, + Key: { id }, + ConditionExpression: 'attribute_exists(id)', + UpdateExpression: 'SET expires = :expires', + ExpressionAttributeValues: { + ':expires': Math.floor(Date.now() / 1000) + TRANSACTION_STAGING_TABLE.StagingErrorsTtl + } + }) } catch (e) { console.error('Unable to update expiry on unprocessable transaction: ', transaction, e) } From d077a0dc9ca23a73684409c051941933941934f2 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:04:10 +0100 Subject: [PATCH 10/17] implemented dynamo v3 to payment-journals.service --- .../payment-journals.service.spec.js | 23 ++++++++--- .../payment-journals.service.js | 38 ++++++++++++------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/sales-api-service/src/services/paymentjournals/__tests__/payment-journals.service.spec.js b/packages/sales-api-service/src/services/paymentjournals/__tests__/payment-journals.service.spec.js index 9b11e0445..cbba6b1bc 100644 --- a/packages/sales-api-service/src/services/paymentjournals/__tests__/payment-journals.service.spec.js +++ b/packages/sales-api-service/src/services/paymentjournals/__tests__/payment-journals.service.spec.js @@ -1,6 +1,17 @@ -import AwsSdk from 'aws-sdk' import { PAYMENTS_TABLE } from '../../../config.js' import { createPaymentJournal, updatePaymentJournal, getPaymentJournal, queryJournalsByTimestamp } from '../payment-journals.service.js' +import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb' + +jest.mock('@aws-sdk/lib-dynamodb', () => ({ + DynamoDBDocument: { + from: jest.fn().mockReturnValue({ + put: jest.fn().mockResolvedValue({}), + update: jest.fn().mockResolvedValue({ Attributes: { some: 'data' } }), + get: jest.fn().mockResolvedValue({ Item: { id: 'test-id', some: 'data' } }), + query: jest.fn().mockResolvedValue({ Items: [] }) + }) + } +})) describe('payment-journals service', () => { beforeAll(async () => { @@ -10,7 +21,7 @@ describe('payment-journals service', () => { describe('createPaymentJournal', () => { it('calls put on dynamodb', async () => { await createPaymentJournal('test-id', { some: 'data' }) - expect(AwsSdk.DynamoDB.DocumentClient.mockedMethods.put).toHaveBeenCalledWith({ + expect(DynamoDBDocument.from().put).toHaveBeenCalledWith({ TableName: PAYMENTS_TABLE.TableName, Item: { id: 'test-id', some: 'data', expires: expect.any(Number) }, ConditionExpression: 'attribute_not_exists(id)' @@ -21,10 +32,10 @@ describe('payment-journals service', () => { describe('updatePaymentJournal', () => { it('calls update on dynamodb', async () => { await updatePaymentJournal('test-id', { some: 'data' }) - expect(AwsSdk.DynamoDB.DocumentClient.mockedMethods.update).toHaveBeenCalledWith({ + expect(DynamoDBDocument.from().update).toHaveBeenCalledWith({ TableName: PAYMENTS_TABLE.TableName, Key: { id: 'test-id' }, - UpdateExpression: 'SET #expires = :expires,#some = :some', + UpdateExpression: 'SET #expires = :expires, #some = :some', ExpressionAttributeNames: { '#expires': 'expires', '#some': 'some' @@ -42,7 +53,7 @@ describe('payment-journals service', () => { describe('getPaymentJournal', () => { it('calls get on dynamodb', async () => { await getPaymentJournal('test-id') - expect(AwsSdk.DynamoDB.DocumentClient.mockedMethods.get).toHaveBeenCalledWith({ + expect(DynamoDBDocument.from().get).toHaveBeenCalledWith({ TableName: PAYMENTS_TABLE.TableName, Key: { id: 'test-id' }, ConsistentRead: true @@ -53,7 +64,7 @@ describe('payment-journals service', () => { describe('queryJournalsByTimestamp', () => { it('calls query on dynamodb', async () => { await queryJournalsByTimestamp({ paymentStatus: 'In Progress', from: '2020-05-29T11:44:45.875Z', to: '2020-05-29T11:44:45.875Z' }) - expect(AwsSdk.DynamoDB.DocumentClient.mockedMethods.query).toHaveBeenCalledWith({ + expect(DynamoDBDocument.from().query).toHaveBeenCalledWith({ TableName: PAYMENTS_TABLE.TableName, IndexName: 'PaymentJournalsByStatusAndTimestamp', KeyConditionExpression: 'paymentStatus = :paymentStatus AND paymentTimestamp BETWEEN :from AND :to', diff --git a/packages/sales-api-service/src/services/paymentjournals/payment-journals.service.js b/packages/sales-api-service/src/services/paymentjournals/payment-journals.service.js index d1b040433..fa4f22027 100644 --- a/packages/sales-api-service/src/services/paymentjournals/payment-journals.service.js +++ b/packages/sales-api-service/src/services/paymentjournals/payment-journals.service.js @@ -11,7 +11,11 @@ const debug = db('sales:paymentjournals') */ export async function createPaymentJournal (id, payload) { const record = { id, expires: Math.floor(Date.now() / 1000) + PAYMENTS_TABLE.Ttl, ...payload } - await docClient.put({ TableName: PAYMENTS_TABLE.TableName, Item: record, ConditionExpression: 'attribute_not_exists(id)' }).promise() + await docClient.put({ + TableName: PAYMENTS_TABLE.TableName, + Item: record, + ConditionExpression: 'attribute_not_exists(id)' + }) debug('Payment journal stored with payload %o', record) return record } @@ -23,25 +27,33 @@ export async function createPaymentJournal (id, payload) { */ export async function updatePaymentJournal (id, payload) { const updates = { expires: Math.floor(Date.now() / 1000) + PAYMENTS_TABLE.Ttl, ...payload } - const result = await docClient - .update({ - TableName: PAYMENTS_TABLE.TableName, - Key: { id }, - ...docClient.createUpdateExpression(updates), - ConditionExpression: 'attribute_exists(id)', - ReturnValues: 'ALL_NEW' - }) - .promise() + const result = await docClient.update({ + TableName: PAYMENTS_TABLE.TableName, + Key: { id }, + UpdateExpression: + 'SET ' + + Object.keys(updates) + .map(key => `#${key} = :${key}`) + .join(', '), + ExpressionAttributeNames: Object.keys(updates).reduce((acc, key) => ({ ...acc, [`#${key}`]: key }), {}), + ExpressionAttributeValues: Object.keys(updates).reduce((acc, key) => ({ ...acc, [`:${key}`]: updates[key] }), {}), + ConditionExpression: 'attribute_exists(id)', + ReturnValues: 'ALL_NEW' + }) return result.Attributes } /** * Get an existing payment journal - * @param {*} payload + * @param {*} id * @returns {Promise<*>} */ export async function getPaymentJournal (id) { - const result = await docClient.get({ TableName: PAYMENTS_TABLE.TableName, Key: { id }, ConsistentRead: true }).promise() + const result = await docClient.get({ + TableName: PAYMENTS_TABLE.TableName, + Key: { id }, + ConsistentRead: true + }) return result.Item } @@ -51,7 +63,7 @@ export async function getPaymentJournal (id) { * @returns {Promise<*>} */ export async function queryJournalsByTimestamp ({ paymentStatus, from, to }) { - return docClient.queryAllPromise({ + return docClient.query({ TableName: PAYMENTS_TABLE.TableName, IndexName: 'PaymentJournalsByStatusAndTimestamp', KeyConditionExpression: 'paymentStatus = :paymentStatus AND paymentTimestamp BETWEEN :from AND :to', From f8bac575400af506b8fbec8b581b55d0c9ae2dda Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Fri, 18 Oct 2024 07:14:28 +0100 Subject: [PATCH 11/17] implemented dynamo v3 to db.js --- packages/pocl-job/src/io/__tests__/db.spec.js | 171 ++++++++++++------ packages/pocl-job/src/io/db.js | 15 +- 2 files changed, 126 insertions(+), 60 deletions(-) diff --git a/packages/pocl-job/src/io/__tests__/db.spec.js b/packages/pocl-job/src/io/__tests__/db.spec.js index 524ce5f78..bad2e378d 100644 --- a/packages/pocl-job/src/io/__tests__/db.spec.js +++ b/packages/pocl-job/src/io/__tests__/db.spec.js @@ -1,5 +1,39 @@ import * as db from '../db.js' -import AwsMock from 'aws-sdk' +import { DynamoDBDocument, GetCommand, UpdateCommand, BatchWriteCommand, QueryCommand, ScanCommand } from '@aws-sdk/lib-dynamodb' + +jest.mock('@aws-sdk/lib-dynamodb', () => { + const actualLib = jest.requireActual('@aws-sdk/lib-dynamodb') + + return { + DynamoDBDocument: { + from: jest.fn().mockReturnValue({ + send: jest.fn(command => { + if (command instanceof actualLib.GetCommand) { + return Promise.resolve({ Item: { id: 'testfile.xml' } }) + } + + if (command instanceof actualLib.UpdateCommand) { + return Promise.resolve({ Attributes: { id: 'testfile.xml', param1: 'test1', param2: 'test2' } }) + } + + if (command instanceof actualLib.BatchWriteCommand) { + return Promise.resolve({ UnprocessedItems: {} }) + } + + if (command instanceof actualLib.QueryCommand || command instanceof actualLib.ScanCommand) { + return Promise.resolve({ Items: [] }) + } + return Promise.resolve({}) + }) + }) + }, + GetCommand: actualLib.GetCommand, + UpdateCommand: actualLib.UpdateCommand, + BatchWriteCommand: actualLib.BatchWriteCommand, + QueryCommand: actualLib.QueryCommand, + ScanCommand: actualLib.ScanCommand + } +}) jest.mock('../../config.js', () => ({ db: { @@ -11,40 +45,53 @@ jest.mock('../../config.js', () => ({ describe('database operations', () => { const TEST_FILENAME = 'testfile.xml' + beforeEach(() => { jest.clearAllMocks() - AwsMock.__resetAll() }) + describe('getFileRecord', () => { it('calls a get operation on dynamodb', async () => { await db.getFileRecord(TEST_FILENAME) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.get).toHaveBeenCalledWith({ - TableName: 'TestFileTable', - Key: { filename: TEST_FILENAME }, - ConsistentRead: true - }) + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith(expect.any(GetCommand)) + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith( + expect.objectContaining({ + input: { + TableName: 'TestFileTable', + Key: { filename: TEST_FILENAME }, + ConsistentRead: true + } + }) + ) }) }) describe('getFileRecords', () => { it('retrieves all records for the given file if no stages are provided', async () => { await db.getFileRecords() - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.scan).toHaveBeenCalledWith( + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith(expect.any(ScanCommand)) + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith( expect.objectContaining({ - TableName: 'TestFileTable', - ConsistentRead: true + input: { + TableName: 'TestFileTable', + ConsistentRead: true, + ExpressionAttributeValues: {} + } }) ) }) - it('retrieves all records a given set of stages', async () => { + it('retrieves all records for a given set of stages', async () => { await db.getFileRecords('STAGE 1', 'STAGE 2') - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.scan).toHaveBeenCalledWith( + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith(expect.any(ScanCommand)) + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith( expect.objectContaining({ - TableName: 'TestFileTable', - FilterExpression: 'stage IN (:stage0,:stage1)', - ExpressionAttributeValues: { ':stage0': 'STAGE 1', ':stage1': 'STAGE 2' }, - ConsistentRead: true + input: { + TableName: 'TestFileTable', + FilterExpression: 'stage IN (:stage0,:stage1)', + ExpressionAttributeValues: { ':stage0': 'STAGE 1', ':stage1': 'STAGE 2' }, + ConsistentRead: true + } }) ) }) @@ -53,20 +100,23 @@ describe('database operations', () => { describe('updateFileStagingTable', () => { it('calls update on dynamodb including all necessary parameters', async () => { await db.updateFileStagingTable({ filename: TEST_FILENAME, param1: 'test1', param2: 'test2' }) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.update).toHaveBeenCalledWith( + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith(expect.any(UpdateCommand)) + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith( expect.objectContaining({ - TableName: 'TestFileTable', - Key: { filename: TEST_FILENAME }, - UpdateExpression: 'SET #expires = :expires,#param1 = :param1,#param2 = :param2', - ExpressionAttributeNames: { - '#expires': 'expires', - '#param1': 'param1', - '#param2': 'param2' - }, - ExpressionAttributeValues: { - ':expires': expect.any(Number), - ':param1': 'test1', - ':param2': 'test2' + input: { + TableName: 'TestFileTable', + Key: { filename: TEST_FILENAME }, + UpdateExpression: 'SET #expires = :expires,#param1 = :param1,#param2 = :param2', + ExpressionAttributeNames: { + '#expires': 'expires', + '#param1': 'param1', + '#param2': 'param2' + }, + ExpressionAttributeValues: { + ':expires': expect.any(Number), + ':param1': 'test1', + ':param2': 'test2' + } } }) ) @@ -77,54 +127,63 @@ describe('database operations', () => { it('calls batchWrite on dynamodb including all necessary parameters', async () => { const records = [{ id: 'test1' }, { id: 'test2' }] await db.updateRecordStagingTable(TEST_FILENAME, records) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.batchWrite).toHaveBeenCalledWith( + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith(expect.any(BatchWriteCommand)) + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith( expect.objectContaining({ - RequestItems: { - TestRecordTable: [ - { - PutRequest: { - Item: { filename: TEST_FILENAME, id: 'test1', expires: expect.any(Number) } + input: { + RequestItems: { + TestRecordTable: [ + { + PutRequest: { + Item: { filename: TEST_FILENAME, id: 'test1', expires: expect.any(Number) } + } + }, + { + PutRequest: { + Item: { filename: TEST_FILENAME, id: 'test2', expires: expect.any(Number) } + } } - }, - { - PutRequest: { - Item: { filename: TEST_FILENAME, id: 'test2', expires: expect.any(Number) } - } - } - ] + ] + } } }) ) }) - it('is a no-op if records is empty', async () => { + it('is a no-op if records are empty', async () => { await db.updateRecordStagingTable(TEST_FILENAME, []) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.batchWrite).not.toHaveBeenCalled() + expect(DynamoDBDocument.from().send).not.toHaveBeenCalled() }) }) describe('getProcessedRecords', () => { it('retrieves all records for the given file if no stages are provided', async () => { await db.getProcessedRecords(TEST_FILENAME) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.query).toHaveBeenCalledWith( + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith(expect.any(QueryCommand)) + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith( expect.objectContaining({ - TableName: 'TestRecordTable', - KeyConditionExpression: 'filename = :filename', - ExpressionAttributeValues: { ':filename': TEST_FILENAME }, - ConsistentRead: true + input: { + TableName: 'TestRecordTable', + KeyConditionExpression: 'filename = :filename', + ExpressionAttributeValues: { ':filename': TEST_FILENAME }, + ConsistentRead: true + } }) ) }) - it('retrieves all records a given set of stages', async () => { + it('retrieves all records for a given set of stages', async () => { await db.getProcessedRecords(TEST_FILENAME, 'STAGE 1', 'STAGE 2') - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.query).toHaveBeenCalledWith( + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith(expect.any(QueryCommand)) + expect(DynamoDBDocument.from().send).toHaveBeenCalledWith( expect.objectContaining({ - TableName: 'TestRecordTable', - KeyConditionExpression: 'filename = :filename', - FilterExpression: 'stage IN (:stage0,:stage1)', - ExpressionAttributeValues: { ':filename': TEST_FILENAME, ':stage0': 'STAGE 1', ':stage1': 'STAGE 2' }, - ConsistentRead: true + input: { + TableName: 'TestRecordTable', + KeyConditionExpression: 'filename = :filename', + FilterExpression: 'stage IN (:stage0,:stage1)', + ExpressionAttributeValues: { ':filename': TEST_FILENAME, ':stage0': 'STAGE 1', ':stage1': 'STAGE 2' }, + ConsistentRead: true + } }) ) }) diff --git a/packages/pocl-job/src/io/db.js b/packages/pocl-job/src/io/db.js index a1fa9ecf3..41ec14338 100644 --- a/packages/pocl-job/src/io/db.js +++ b/packages/pocl-job/src/io/db.js @@ -1,3 +1,4 @@ +import { GetCommand, UpdateCommand } from '@aws-sdk/lib-dynamodb' import config from '../config.js' import { AWS } from '@defra-fish/connectors-lib' const { docClient } = AWS() @@ -10,13 +11,13 @@ const { docClient } = AWS() * @returns {Promise} */ export const updateFileStagingTable = async ({ filename, ...entries }) => { - await docClient - .update({ + await docClient.send( + new UpdateCommand({ TableName: config.db.fileStagingTable, Key: { filename }, ...docClient.createUpdateExpression({ expires: Math.floor(Date.now() / 1000) + config.db.stagingTtlDelta, ...entries }) }) - .promise() + ) } /** @@ -42,7 +43,13 @@ export const getFileRecords = async (...stages) => { * @returns {DocumentClient.AttributeMap} */ export const getFileRecord = async filename => { - const result = await docClient.get({ TableName: config.db.fileStagingTable, Key: { filename }, ConsistentRead: true }).promise() + const result = await docClient.send( + new GetCommand({ + TableName: config.db.fileStagingTable, + Key: { filename }, + ConsistentRead: true + }) + ) return result.Item } From d3c445ccb0f3662536de4e0b08baf6496a37b384 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:34:48 +0100 Subject: [PATCH 12/17] implemented dynamo v3 to health.js --- .../server/plugins/__tests__/health.spec.js | 29 ++++++++++++------- .../src/server/plugins/health.js | 2 +- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/sales-api-service/src/server/plugins/__tests__/health.spec.js b/packages/sales-api-service/src/server/plugins/__tests__/health.spec.js index 410d9ae81..f1f9193de 100644 --- a/packages/sales-api-service/src/server/plugins/__tests__/health.spec.js +++ b/packages/sales-api-service/src/server/plugins/__tests__/health.spec.js @@ -1,11 +1,30 @@ import initialiseServer from '../../server.js' import { dynamicsClient } from '@defra-fish/dynamics-lib' import AwsMock from 'aws-sdk' +import { DynamoDBClient, ListTablesCommand } from '@aws-sdk/client-dynamodb' +import { mockClient } from 'aws-sdk-client-mock' + let server = null +const dynamoDbMock = mockClient(DynamoDBClient) + describe('hapi healthcheck', () => { beforeAll(async () => { server = await initialiseServer({ port: null }) + + AwsMock.SQS.__init({ + expectedResponses: { + listQueues: { QueueUrls: ['TestQueue'] } + } + }) + + dynamoDbMock.on(ListTablesCommand).resolves({ + TableNames: ['TestTable'] + }) + }) + + beforeEach(() => { + jest.clearAllMocks() }) afterAll(async () => { @@ -21,16 +40,6 @@ describe('hapi healthcheck', () => { }) it('exposes a service status endpoint providing additional detailed information', async () => { - AwsMock.SQS.__init({ - expectedResponses: { - listQueues: { QueueUrls: ['TestQueue'] } - } - }) - AwsMock.DynamoDB.__init({ - expectedResponses: { - listTables: { TableNames: ['TestTable'] } - } - }) const result = await server.inject({ method: 'GET', url: '/service-status?v&h' diff --git a/packages/sales-api-service/src/server/plugins/health.js b/packages/sales-api-service/src/server/plugins/health.js index 456cd338a..6feee8aa1 100644 --- a/packages/sales-api-service/src/server/plugins/health.js +++ b/packages/sales-api-service/src/server/plugins/health.js @@ -22,7 +22,7 @@ export default { return { connection: 'dynamics', status: 'ok', ...(await dynamicsClient.executeUnboundFunction('RetrieveVersion')) } }, async () => { - return { connection: 'dynamodb', status: 'ok', ...(await ddb.listTables().promise()) } + return { connection: 'dynamodb', status: 'ok', ...(await ddb.listTables()) } }, async () => { return { connection: 'sqs', status: 'ok', ...(await sqs.listQueues().promise()) } From 514da7739badac7317867d0d4f6ffe137719edb6 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:35:53 +0100 Subject: [PATCH 13/17] prettier fix --- .../src/server/plugins/__tests__/health.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sales-api-service/src/server/plugins/__tests__/health.spec.js b/packages/sales-api-service/src/server/plugins/__tests__/health.spec.js index f1f9193de..d0ccbf90f 100644 --- a/packages/sales-api-service/src/server/plugins/__tests__/health.spec.js +++ b/packages/sales-api-service/src/server/plugins/__tests__/health.spec.js @@ -17,7 +17,7 @@ describe('hapi healthcheck', () => { listQueues: { QueueUrls: ['TestQueue'] } } }) - + dynamoDbMock.on(ListTablesCommand).resolves({ TableNames: ['TestTable'] }) From d683723fa258d4bbcd0c073ca5f481527356ba24 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Sun, 20 Oct 2024 00:46:12 +0100 Subject: [PATCH 14/17] implemented dynamo v3 to process-transaction-queue --- .../process-transaction-queue.spec.js | 48 ++++++++++++------- .../transactions/process-transaction-queue.js | 14 +++--- .../transactions/retrieve-transaction.js | 25 +++++----- 3 files changed, 52 insertions(+), 35 deletions(-) diff --git a/packages/sales-api-service/src/services/transactions/__tests__/process-transaction-queue.spec.js b/packages/sales-api-service/src/services/transactions/__tests__/process-transaction-queue.spec.js index f86a4c786..740833f2f 100644 --- a/packages/sales-api-service/src/services/transactions/__tests__/process-transaction-queue.spec.js +++ b/packages/sales-api-service/src/services/transactions/__tests__/process-transaction-queue.spec.js @@ -23,9 +23,21 @@ import { MOCK_EXISTING_CONTACT_ENTITY } from '../../../__mocks__/test-data.js' import { TRANSACTION_STAGING_TABLE, TRANSACTION_STAGING_HISTORY_TABLE } from '../../../config.js' -import AwsMock from 'aws-sdk' import { POCL_DATA_SOURCE, DDE_DATA_SOURCE } from '@defra-fish/business-rules-lib' import moment from 'moment' +import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb' + +jest.mock('@aws-sdk/lib-dynamodb', () => ({ + DynamoDBDocument: { + from: jest.fn().mockReturnValue({ + get: jest.fn(), + put: jest.fn(), + delete: jest.fn(), + update: jest.fn(), + query: jest.fn() + }) + } +})) jest.mock('../../reference-data.service.js', () => ({ ...jest.requireActual('../../reference-data.service.js'), @@ -65,8 +77,10 @@ jest.mock('@defra-fish/business-rules-lib', () => ({ })) describe('transaction service', () => { + let mockDynamoDb beforeAll(() => { TRANSACTION_STAGING_TABLE.TableName = 'TestTable' + mockDynamoDb = DynamoDBDocument.from() }) beforeEach(jest.clearAllMocks) @@ -152,18 +166,20 @@ describe('transaction service', () => { ] ])('handles %s', async (description, initialiseMockTransactionRecord, entityExpectations) => { const mockRecord = initialiseMockTransactionRecord() - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) + mockDynamoDb.delete.mockResolvedValueOnce({}) + mockDynamoDb.put.mockResolvedValueOnce({}) const result = await processQueue({ id: mockRecord.id }) expect(result).toBeUndefined() expect(persist).toBeCalledWith(entityExpectations, undefined) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.get).toBeCalledWith( + expect(mockDynamoDb.get).toBeCalledWith( expect.objectContaining({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id: mockRecord.id }, ConsistentRead: true }) ) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.delete).toBeCalledWith( + expect(mockDynamoDb.delete).toBeCalledWith( expect.objectContaining({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id: mockRecord.id } @@ -174,7 +190,7 @@ describe('transaction service', () => { expires: expect.any(Number) }) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.put).toBeCalledWith( + expect(mockDynamoDb.put).toBeCalledWith( expect.objectContaining({ TableName: TRANSACTION_STAGING_HISTORY_TABLE.TableName, Item: expectedRecord, @@ -196,7 +212,7 @@ describe('transaction service', () => { it('includes a FulfilmentRequest when the permit and contact are for postal fulfilment', async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.permissions[0].permitId = MOCK_12MONTH_SENIOR_PERMIT.id - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) expect(persist).toBeCalledWith( [ @@ -215,7 +231,7 @@ describe('transaction service', () => { it('does not include a FulfilmentRequest when the permit and contact are not for postal fulfilment', async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.permissions[0].permitId = MOCK_1DAY_SENIOR_PERMIT_ENTITY.id - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) expect(persist).toBeCalledWith( [ @@ -243,7 +259,7 @@ describe('transaction service', () => { it('does not include a FulfilmentRequest when the permit and contact are for postal fulfilment', async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.permissions[0].permitId = MOCK_12MONTH_SENIOR_PERMIT.id - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) expect(persist).toBeCalledWith( [ @@ -261,7 +277,7 @@ describe('transaction service', () => { it('does not include a FulfilmentRequest when the permit and contact are not for postal fulfilment', async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.permissions[0].permitId = MOCK_1DAY_SENIOR_PERMIT_ENTITY.id - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) expect(persist).toBeCalledWith( [ @@ -280,7 +296,7 @@ describe('transaction service', () => { it('sets isLicenceForYou to Yes on the transaction, if it is true on the permission', async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.permissions[0].isLicenceForYou = true - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) const persistMockFirstAgument = persist.mock.calls[0] expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeDefined() @@ -290,7 +306,7 @@ describe('transaction service', () => { it('sets isLicenceForYou to No on the transaction, if it is false on the permission', async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.permissions[0].isLicenceForYou = false - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) const persistMockFirstAgument = persist.mock.calls[0] expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeDefined() @@ -300,7 +316,7 @@ describe('transaction service', () => { it('does not set isLicenceForYou on the transaction, if it is undefined on the permission', async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.permissions[0].isLicenceForYou = undefined - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) const persistMockFirstAgument = persist.mock.calls[0] expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeUndefined() @@ -309,7 +325,7 @@ describe('transaction service', () => { it('does not set isLicenceForYou on the transaction, if it is null on the permission', async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.permissions[0].isLicenceForYou = null - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) const persistMockFirstAgument = persist.mock.calls[0] expect(persistMockFirstAgument[0][4].isLicenceForYou).toBeUndefined() @@ -319,7 +335,7 @@ describe('transaction service', () => { const transactionFilename = 'test-file.xml' const mockRecord = mockFinalisedTransactionRecord() mockRecord.transactionFile = transactionFilename - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) const transactionToFileBindingSpy = jest.spyOn(Transaction.prototype, 'bindToAlternateKey') const permissionToFileBindingSpy = jest.spyOn(Permission.prototype, 'bindToAlternateKey') const testPoclFileEntity = new PoclFile() @@ -331,7 +347,7 @@ describe('transaction service', () => { it('throws 404 not found error if a record cannot be found for the given id', async () => { const mockRecord = mockFinalisedTransactionRecord() - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: undefined }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: undefined }) try { await processQueue({ id: mockRecord.id }) } catch (e) { @@ -344,7 +360,7 @@ describe('transaction service', () => { const setup = async () => { const mockRecord = mockFinalisedTransactionRecord() mockRecord.payment.amount = cost - AwsMock.DynamoDB.DocumentClient.__setResponse('get', { Item: mockRecord }) + mockDynamoDb.get.mockResolvedValueOnce({ Item: mockRecord }) await processQueue({ id: mockRecord.id }) const { mock: { diff --git a/packages/sales-api-service/src/services/transactions/process-transaction-queue.js b/packages/sales-api-service/src/services/transactions/process-transaction-queue.js index 0af121ba1..4c5ae8a73 100644 --- a/packages/sales-api-service/src/services/transactions/process-transaction-queue.js +++ b/packages/sales-api-service/src/services/transactions/process-transaction-queue.js @@ -102,14 +102,12 @@ export async function processQueue ({ id }) { debug('Persisting %d entities for staging id %s', entities.length, id) await persist(entities, transactionRecord.createdBy) debug('Moving staging data to history table for staging id %s', id) - await docClient.delete({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id } }).promise() - await docClient - .put({ - TableName: TRANSACTION_STAGING_HISTORY_TABLE.TableName, - Item: Object.assign(transactionRecord, { expires: Math.floor(Date.now() / 1000) + TRANSACTION_STAGING_HISTORY_TABLE.Ttl }), - ConditionExpression: 'attribute_not_exists(id)' - }) - .promise() + await docClient.delete({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id } }) + await docClient.put({ + TableName: TRANSACTION_STAGING_HISTORY_TABLE.TableName, + Item: Object.assign(transactionRecord, { expires: Math.floor(Date.now() / 1000) + TRANSACTION_STAGING_HISTORY_TABLE.Ttl }), + ConditionExpression: 'attribute_not_exists(id)' + }) } const shouldCreateFulfilmentRequest = (permission, permit, contact) => { diff --git a/packages/sales-api-service/src/services/transactions/retrieve-transaction.js b/packages/sales-api-service/src/services/transactions/retrieve-transaction.js index b47513e12..3d6b6211f 100644 --- a/packages/sales-api-service/src/services/transactions/retrieve-transaction.js +++ b/packages/sales-api-service/src/services/transactions/retrieve-transaction.js @@ -6,17 +6,20 @@ const { docClient } = AWS() const debug = db('sales:transactions') export const retrieveStagedTransaction = async id => { - const result = await docClient.get({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id }, ConsistentRead: true }).promise() - if (!result.Item) { - debug('Failed to retrieve a transaction with staging id %s', id) - const historical = await docClient - .get({ TableName: TRANSACTION_STAGING_HISTORY_TABLE.TableName, Key: { id }, ConsistentRead: true }) - .promise() - if (historical.Item) { - throw Boom.resourceGone('The transaction has already been finalised', historical.Item) + try { + const result = await docClient.get({ TableName: TRANSACTION_STAGING_TABLE.TableName, Key: { id }, ConsistentRead: true }) + if (!result || !result.Item) { + debug('Failed to retrieve a transaction with staging id %s', id) + const historical = await docClient.get({ TableName: TRANSACTION_STAGING_HISTORY_TABLE.TableName, Key: { id }, ConsistentRead: true }) + if (historical && historical.Item) { + throw Boom.resourceGone('The transaction has already been finalised', historical.Item) + } + throw Boom.notFound('A transaction for the specified identifier was not found') } - throw Boom.notFound('A transaction for the specified identifier was not found') + debug('Retrieved transaction record for staging id %s', id) + return result.Item + } catch (error) { + debug('Error retrieving transaction for id %s: %O', id, error) + throw error } - debug('Retrieved transaction record for staging id %s', id) - return result.Item } From b806cb0db7bd19c21895fa0a97eb78b8687f0d0a Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Sun, 20 Oct 2024 01:15:45 +0100 Subject: [PATCH 15/17] revert last 6 commits --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4fbe80762..93e45d85e 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,8 @@ ], "testEnvironment": "node", "testRunner": "jest-circus/runner", - "silent": true + "silent": false, + "verbose": true }, "dependencies": { "@aws-sdk/client-dynamodb": "^3.665.0", From eb9d6aceaf3fc0b21f8aa24a5b3b63c5aff13d4e Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:22:53 +0100 Subject: [PATCH 16/17] implemented dynamo v3 to create-transaction --- .../__tests__/create-transaction.spec.js | 47 ++++++++++++------- .../transactions/create-transaction.js | 27 ++++++++--- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/packages/sales-api-service/src/services/transactions/__tests__/create-transaction.spec.js b/packages/sales-api-service/src/services/transactions/__tests__/create-transaction.spec.js index 767528221..ff3cd5203 100644 --- a/packages/sales-api-service/src/services/transactions/__tests__/create-transaction.spec.js +++ b/packages/sales-api-service/src/services/transactions/__tests__/create-transaction.spec.js @@ -6,9 +6,9 @@ import { MOCK_12MONTH_DISABLED_PERMIT } from '../../../__mocks__/test-data.js' import { TRANSACTION_STAGING_TABLE } from '../../../config.js' -import AwsMock from 'aws-sdk' import { getPermissionCost } from '@defra-fish/business-rules-lib' import { getReferenceDataForEntityAndId } from '../../reference-data.service.js' +import { DynamoDBDocumentClient, PutCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb' jest.mock('@defra-fish/business-rules-lib') jest.mock('../../reference-data.service.js', () => ({ @@ -22,12 +22,23 @@ jest.mock('../../reference-data.service.js', () => ({ }) })) +jest.mock('@aws-sdk/lib-dynamodb', () => ({ + DynamoDBDocumentClient: { + from: jest.fn().mockReturnValue({ + send: jest.fn() + }) + }, + PutCommand: jest.fn(), + BatchWriteCommand: jest.fn() +})) + describe('transaction service', () => { + let mockDynamoDb beforeAll(() => { TRANSACTION_STAGING_TABLE.TableName = 'TestTable' getPermissionCost.mockReturnValue(54) + mockDynamoDb = DynamoDBDocumentClient.from() }) - beforeEach(AwsMock.__resetAll) beforeEach(jest.clearAllMocks) describe('createTransaction', () => { @@ -43,12 +54,12 @@ describe('transaction service', () => { const result = await createTransaction(mockPayload) expect(result).toMatchObject(expectedResult) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.put).toBeCalledWith( - expect.objectContaining({ - TableName: TRANSACTION_STAGING_TABLE.TableName, - Item: expectedResult - }) - ) + expect(PutCommand).toHaveBeenCalledWith({ + TableName: TRANSACTION_STAGING_TABLE.TableName, + Item: expectedResult, + ConditionExpression: 'attribute_not_exists(id)' + }) + }) it.each([99, 115, 22, 87.99])('uses business rules lib to calculate price (%d)', async permitPrice => { @@ -74,7 +85,7 @@ describe('transaction service', () => { }) it('throws exceptions back up the stack', async () => { - AwsMock.DynamoDB.DocumentClient.__throwWithErrorOn('put') + mockDynamoDb.send.mockRejectedValueOnce(new Error('Test error')) await expect(createTransaction(mockTransactionPayload())).rejects.toThrow('Test error') }) }) @@ -92,17 +103,19 @@ describe('transaction service', () => { const result = await createTransactions([mockPayload, mockPayload]) expect(result).toEqual(expect.arrayContaining([expectedRecord, expectedRecord])) - expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.batchWrite).toBeCalledWith( - expect.objectContaining({ - RequestItems: { - [TRANSACTION_STAGING_TABLE.TableName]: [{ PutRequest: { Item: expectedRecord } }, { PutRequest: { Item: expectedRecord } }] - } - }) - ) + expect(BatchWriteCommand).toHaveBeenCalledWith({ + RequestItems: { + [TRANSACTION_STAGING_TABLE.TableName]: [ + { PutRequest: { Item: expectedRecord } }, + { PutRequest: { Item: expectedRecord } } + ] + } + }) + expect(mockDynamoDb.send).toHaveBeenCalledWith(expect.any(BatchWriteCommand)) }) it('throws exceptions back up the stack', async () => { - AwsMock.DynamoDB.DocumentClient.__throwWithErrorOn('put') + mockDynamoDb.send.mockRejectedValueOnce(new Error('Test error')) await expect(createTransaction(mockTransactionPayload())).rejects.toThrow('Test error') }) }) diff --git a/packages/sales-api-service/src/services/transactions/create-transaction.js b/packages/sales-api-service/src/services/transactions/create-transaction.js index 0d535d1d7..d362756db 100644 --- a/packages/sales-api-service/src/services/transactions/create-transaction.js +++ b/packages/sales-api-service/src/services/transactions/create-transaction.js @@ -2,12 +2,15 @@ import { TRANSACTION_STATUS } from './constants.js' import { getReferenceDataForEntityAndId } from '../reference-data.service.js' import { TRANSACTION_STAGING_TABLE } from '../../config.js' import { v4 as uuidv4 } from 'uuid' -import { AWS } from '@defra-fish/connectors-lib' +import { DynamoDBDocumentClient, PutCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb' +import { DynamoDBClient } from '@aws-sdk/client-dynamodb' import db from 'debug' import { Permit } from '@defra-fish/dynamics-lib' import { getPermissionCost } from '@defra-fish/business-rules-lib' -const { docClient } = AWS() + const debug = db('sales:transactions') +const client = new DynamoDBClient({}) +const docClient = DynamoDBDocumentClient.from(client) /** * Create a single new transaction @@ -16,9 +19,15 @@ const debug = db('sales:transactions') */ export async function createTransaction (payload) { const record = await createTransactionRecord(payload) - await docClient - .put({ TableName: TRANSACTION_STAGING_TABLE.TableName, Item: record, ConditionExpression: 'attribute_not_exists(id)' }) - .promise() + + await docClient.send( + new PutCommand({ + TableName: TRANSACTION_STAGING_TABLE.TableName, + Item: record, + ConditionExpression: 'attribute_not_exists(id)' + }) + ) + debug('Transaction %s successfully created in DynamoDB table %s', record.id, TRANSACTION_STAGING_TABLE.TableName) return record } @@ -33,10 +42,14 @@ export async function createTransactions (payload) { const records = await Promise.all(payload.map(i => createTransactionRecord(i))) const params = { RequestItems: { - [TRANSACTION_STAGING_TABLE.TableName]: records.map(record => ({ PutRequest: { Item: record } })) + [TRANSACTION_STAGING_TABLE.TableName]: records.map(record => ({ + PutRequest: { Item: record } + })) } } - await docClient.batchWriteAllPromise(params) + + await docClient.send(new BatchWriteCommand(params)) + debug('%d transactions created in batch', records.length) return records } From bec1de3452ef9f9d6cda34fe67056be539fce9d6 Mon Sep 17 00:00:00 2001 From: lailien3 <138867360+lailien3@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:24:02 +0100 Subject: [PATCH 17/17] prettier fixes --- .../transactions/__tests__/create-transaction.spec.js | 6 +----- .../src/services/transactions/create-transaction.js | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/sales-api-service/src/services/transactions/__tests__/create-transaction.spec.js b/packages/sales-api-service/src/services/transactions/__tests__/create-transaction.spec.js index ff3cd5203..b1341a38a 100644 --- a/packages/sales-api-service/src/services/transactions/__tests__/create-transaction.spec.js +++ b/packages/sales-api-service/src/services/transactions/__tests__/create-transaction.spec.js @@ -59,7 +59,6 @@ describe('transaction service', () => { Item: expectedResult, ConditionExpression: 'attribute_not_exists(id)' }) - }) it.each([99, 115, 22, 87.99])('uses business rules lib to calculate price (%d)', async permitPrice => { @@ -105,10 +104,7 @@ describe('transaction service', () => { expect(result).toEqual(expect.arrayContaining([expectedRecord, expectedRecord])) expect(BatchWriteCommand).toHaveBeenCalledWith({ RequestItems: { - [TRANSACTION_STAGING_TABLE.TableName]: [ - { PutRequest: { Item: expectedRecord } }, - { PutRequest: { Item: expectedRecord } } - ] + [TRANSACTION_STAGING_TABLE.TableName]: [{ PutRequest: { Item: expectedRecord } }, { PutRequest: { Item: expectedRecord } }] } }) expect(mockDynamoDb.send).toHaveBeenCalledWith(expect.any(BatchWriteCommand)) diff --git a/packages/sales-api-service/src/services/transactions/create-transaction.js b/packages/sales-api-service/src/services/transactions/create-transaction.js index d362756db..50cce713c 100644 --- a/packages/sales-api-service/src/services/transactions/create-transaction.js +++ b/packages/sales-api-service/src/services/transactions/create-transaction.js @@ -19,7 +19,7 @@ const docClient = DynamoDBDocumentClient.from(client) */ export async function createTransaction (payload) { const record = await createTransactionRecord(payload) - + await docClient.send( new PutCommand({ TableName: TRANSACTION_STAGING_TABLE.TableName, @@ -27,7 +27,7 @@ export async function createTransaction (payload) { ConditionExpression: 'attribute_not_exists(id)' }) ) - + debug('Transaction %s successfully created in DynamoDB table %s', record.id, TRANSACTION_STAGING_TABLE.TableName) return record }