diff --git a/.dockerignore b/.dockerignore old mode 100644 new mode 100755 diff --git a/.env b/.env old mode 100644 new mode 100755 diff --git a/.env.test b/.env.test old mode 100644 new mode 100755 diff --git a/.eslintrc b/.eslintrc old mode 100644 new mode 100755 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 diff --git a/Dockerfile-prod b/Dockerfile-prod old mode 100644 new mode 100755 diff --git a/Dockerfile-test b/Dockerfile-test old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/__mocks__/nodemailer.js b/__mocks__/nodemailer.js old mode 100644 new mode 100755 diff --git a/babel.config.json b/babel.config.json old mode 100644 new mode 100755 diff --git a/certs/.gitignore b/certs/.gitignore old mode 100644 new mode 100755 diff --git a/codecov.yaml b/codecov.yaml new file mode 100755 index 00000000..c04ab9be --- /dev/null +++ b/codecov.yaml @@ -0,0 +1,6 @@ +coverage: + status: + project: + default: + target: 80% + threshold: 5% diff --git a/docker-compose.yml b/docker-compose.yml old mode 100644 new mode 100755 diff --git a/documentation/.gitignore b/documentation/.gitignore old mode 100644 new mode 100755 diff --git a/documentation/README.md b/documentation/README.md old mode 100644 new mode 100755 diff --git a/documentation/babel.config.js b/documentation/babel.config.js old mode 100644 new mode 100755 diff --git a/documentation/docs-index.js b/documentation/docs-index.js old mode 100644 new mode 100755 diff --git a/documentation/docs/applications/approve.md b/documentation/docs/applications/approve.md old mode 100644 new mode 100755 diff --git a/documentation/docs/applications/create.md b/documentation/docs/applications/create.md old mode 100644 new mode 100755 diff --git a/documentation/docs/applications/reject.md b/documentation/docs/applications/reject.md old mode 100644 new mode 100755 diff --git a/documentation/docs/applications/search.md b/documentation/docs/applications/search.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/confirm.md b/documentation/docs/auth/confirm.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/finish-recovery.md b/documentation/docs/auth/finish-recovery.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/login.md b/documentation/docs/auth/login.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/logout.md b/documentation/docs/auth/logout.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/me.md b/documentation/docs/auth/me.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/recover.md b/documentation/docs/auth/recover.md old mode 100644 new mode 100755 diff --git a/documentation/docs/auth/register.md b/documentation/docs/auth/register.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/block.md b/documentation/docs/companies/block.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/concurrent-offers.md b/documentation/docs/companies/concurrent-offers.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/delete.md b/documentation/docs/companies/delete.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/disable.md b/documentation/docs/companies/disable.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/enable.md b/documentation/docs/companies/enable.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/finish-registration.md b/documentation/docs/companies/finish-registration.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/list.md b/documentation/docs/companies/list.md old mode 100644 new mode 100755 diff --git a/documentation/docs/companies/unblock.md b/documentation/docs/companies/unblock.md old mode 100644 new mode 100755 diff --git a/documentation/docs/intro.md b/documentation/docs/intro.md old mode 100644 new mode 100755 diff --git a/documentation/docs/intro/getting-started.md b/documentation/docs/intro/getting-started.md old mode 100644 new mode 100755 diff --git a/documentation/docs/intro/how-to-docs.md b/documentation/docs/intro/how-to-docs.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/archive.md b/documentation/docs/offers/archive.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/create.md b/documentation/docs/offers/create.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/disable.md b/documentation/docs/offers/disable.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/edit.md b/documentation/docs/offers/edit.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/enable.md b/documentation/docs/offers/enable.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/get-company.md b/documentation/docs/offers/get-company.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/get.md b/documentation/docs/offers/get.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/hide.md b/documentation/docs/offers/hide.md old mode 100644 new mode 100755 diff --git a/documentation/docs/offers/search.md b/documentation/docs/offers/search.md old mode 100644 new mode 100755 diff --git a/documentation/docusaurus.config.js b/documentation/docusaurus.config.js old mode 100644 new mode 100755 diff --git a/documentation/package-lock.json b/documentation/package-lock.json old mode 100644 new mode 100755 diff --git a/documentation/package.json b/documentation/package.json old mode 100644 new mode 100755 diff --git a/documentation/src/css/custom.css b/documentation/src/css/custom.css old mode 100644 new mode 100755 diff --git a/documentation/src/highlight.js b/documentation/src/highlight.js old mode 100644 new mode 100755 diff --git a/documentation/static/.nojekyll b/documentation/static/.nojekyll old mode 100644 new mode 100755 diff --git a/documentation/static/img/favicon.ico b/documentation/static/img/favicon.ico old mode 100644 new mode 100755 diff --git a/documentation/static/img/logo_2018.svg b/documentation/static/img/logo_2018.svg old mode 100644 new mode 100755 diff --git a/jest.config.js b/jest.config.js old mode 100644 new mode 100755 diff --git a/netlify.toml b/netlify.toml old mode 100644 new mode 100755 diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 index 276254f8..3d1b0735 --- a/package-lock.json +++ b/package-lock.json @@ -57,9 +57,27 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "optional": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, "node_modules/@aws-crypto/ie11-detection": { - "version": "2.0.2", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", "optional": true, "dependencies": { "tslib": "^1.11.1" @@ -67,19 +85,21 @@ }, "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-crypto/sha256-browser": { - "version": "2.0.0", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", "optional": true, "dependencies": { - "@aws-crypto/ie11-detection": "^2.0.0", - "@aws-crypto/sha256-js": "^2.0.0", - "@aws-crypto/supports-web-crypto": "^2.0.0", - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" @@ -87,27 +107,31 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-crypto/sha256-js": { - "version": "2.0.0", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", "optional": true, "dependencies": { - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" } }, "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-crypto/supports-web-crypto": { - "version": "2.0.2", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", "optional": true, "dependencies": { "tslib": "^1.11.1" @@ -115,502 +139,540 @@ }, "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-crypto/util": { - "version": "2.0.2", - "license": "Apache-2.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", "optional": true, "dependencies": { - "@aws-sdk/types": "^3.110.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" } }, "node_modules/@aws-crypto/util/node_modules/tslib": { "version": "1.14.1", - "license": "0BSD", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, "node_modules/@aws-sdk/abort-controller": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.347.0.tgz", + "integrity": "sha512-P/2qE6ntYEmYG4Ez535nJWZbXqgbkJx8CMz7ChEuEg3Gp3dvVYEKg+iEUEvlqQ2U5dWP5J3ehw5po9t86IsVPQ==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.229.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.354.0.tgz", + "integrity": "sha512-VYoPiup85Zn1uiqn6X7Kl1/5AsihyW0jOPpO5Xv39shRKFTLYWIgPxjg7k+dNPVAX62XrWoWNkGR6sB/JN9Qdg==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.354.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.354.0.tgz", + "integrity": "sha512-4jmvjJYDaaPmm1n2TG4LYfTEnHLKcJmImgBqhgzhMgaypb4u/k1iw0INV2r/afYPL/FsrLFwc46RM3HYx3nc4A==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.354.0.tgz", + "integrity": "sha512-XZcg4s2zKb4S8ltluiw5yxpm974uZqzo2HTECt1lbzUJgVgLsMAh/nPJ1fLqg4jadT+rf8Lq2FEFqOM/vxWT8A==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.354.0.tgz", + "integrity": "sha512-l9Ar/C/3PNlToM1ukHVfBtp4plbRUxLMYY2DOTMI0nb3jzfcvETBcdEGCP51fX4uAfJ2vc4g5qBF/qXKX0LMWA==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-sdk-sts": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "fast-xml-parser": "4.2.4", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/config-resolver": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.354.0.tgz", + "integrity": "sha512-K4XWie8yJPT8bpYVX54VJMQhiJRTw8PrjEs9QrKqvwoCcZ3G4qEt40tIu33XksuokXxk8rrVH5d7odOPBsAtdg==", "optional": true, "dependencies": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.354.0.tgz", + "integrity": "sha512-Q5UcqASJWqwD4AXpfv4Zpw5tUV/fzbhnEC9TzyB39zXcu4Qd0cQgVQOOq9FX1GbtLNOzkPnbvHsbv2PdEaNM4A==", "optional": true, "dependencies": { - "@aws-sdk/client-cognito-identity": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-cognito-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.353.0.tgz", + "integrity": "sha512-Y4VsNS8O1FAD5J7S5itOhnOghQ5LIXlZ44t35nF8cbcF+JPvY3ToKzYpjYN1jM7DXKqU4shtqgYpzSqxlvEgKQ==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-imds": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.354.0.tgz", + "integrity": "sha512-AB+PuDd1jX6qgz+JYvIyOn8Kz9/lQ60KuY1TFb7g3S8zURw+DSeMJNR1jzEsorWICTzhxXmyasHVMa4Eo4Uq+Q==", "optional": true, "dependencies": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.354.0.tgz", + "integrity": "sha512-bn2ifrRsxWpxzwXa25jRdUECQ1dC+NB3YlRYnGdIaIQLF559N2jnfCabYzqyfKI++WU7aQeMofPe2PxVGlbv9Q==", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.354.0.tgz", + "integrity": "sha512-ltKiRtHfqDaCcrb44DIoSHQ9MposFl/aDtNdu5OdQv/2Q1r7M/r2fQdq9DHOrxeQQjaUH4C6k6fGTsxALTHyNA==", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.229.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-ini": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.354.0.tgz", + "integrity": "sha512-AxpASm+tS8V1PY4PLfG9dtqa96lzBJ3niTQb+RAm4uYCddW7gxNDkGB+jSCzVdUPVa3xA2ITBS/ka3C5yM8YWg==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.354.0.tgz", + "integrity": "sha512-ihiaUxh8V/nQgTOgQZxWQcbckXhM+J6Wdc4F0z9soi48iSOqzRpzPw5E14wSZScEZjNY/gKEDz8gCt8WkT/G0w==", "optional": true, "dependencies": { - "@aws-sdk/client-sso": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.229.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/token-providers": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.354.0.tgz", + "integrity": "sha512-scx9mAf4m3Hc3uMX2Vh8GciEcC/5GqeDI8qc0zBj+UF/5c/GtihZA4WoCV3Sg3jMPDUKY81DiFCtcKHhtUqKfg==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-providers": { - "version": "3.229.0", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.229.0", - "@aws-sdk/client-sso": "3.229.0", - "@aws-sdk/client-sts": "3.229.0", - "@aws-sdk/credential-provider-cognito-identity": "3.229.0", - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.229.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.354.0.tgz", + "integrity": "sha512-GjkSKGWL+lbEVAYGRvE2kdKn8lnhLEBB98yKMz6k9VhqVBrMPZVGTFTlNNtPRZ7IfnnmgLnk6IHtue9xgaycfg==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.354.0", + "@aws-sdk/client-sso": "3.354.0", + "@aws-sdk/client-sts": "3.354.0", + "@aws-sdk/credential-provider-cognito-identity": "3.354.0", + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-ini": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/eventstream-codec": { + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.347.0.tgz", + "integrity": "sha512-61q+SyspjsaQ4sdgjizMyRgVph2CiW4aAtfpoH69EJFJfTxTR/OqnZ9Jx/3YiYi0ksrvDenJddYodfWWJqD8/w==", + "optional": true, + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, "node_modules/@aws-sdk/fetch-http-handler": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.353.0.tgz", + "integrity": "sha512-8ic2+4E6jzfDevd++QS1rOR05QFkAhEFbi5Ja3/Zzp7TkWIS8wv5wwMATjNkbbdsXYuB5Lhl/OsjfZmIv5aqRw==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/querystring-builder": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/hash-node": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.347.0.tgz", + "integrity": "sha512-96+ml/4EaUaVpzBdOLGOxdoXOjkPgkoJp/0i1fxOJEvl8wdAQSwc3IugVK9wZkCxy2DlENtgOe6DfIOhfffm/g==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/invalid-dependency": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.347.0.tgz", + "integrity": "sha512-8imQcwLwqZ/wTJXZqzXT9pGLIksTRckhGLZaXT60tiBOPKuerTsus2L59UstLs5LP8TKaVZKFFSsjRIn9dQdmQ==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/is-array-buffer": { - "version": "3.201.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-content-length": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.347.0.tgz", + "integrity": "sha512-i4qtWTDImMaDUtwKQPbaZpXsReiwiBomM1cWymCU4bhz81HL01oIxOxOBuiM+3NlDoCSPr3KI6txZSz/8cqXCQ==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-endpoint": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.347.0.tgz", + "integrity": "sha512-unF0c6dMaUL1ffU+37Ugty43DgMnzPWXr/Jup/8GbK5fzzWT5NQq6dj9KHPubMbWeEjQbmczvhv25JuJdK8gNQ==", "optional": true, "dependencies": { - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.347.0.tgz", + "integrity": "sha512-kpKmR9OvMlnReqp5sKcJkozbj1wmlblbVSbnQAIkzeQj2xD5dnVR3Nn2ogQKxSmU1Fv7dEroBtrruJ1o3fY38A==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.347.0.tgz", + "integrity": "sha512-NYC+Id5UCkVn+3P1t/YtmHt75uED06vwaKyxDy0UmB2K66PZLVtwWbLpVWrhbroaw1bvUHYcRyQ9NIfnVcXQjA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.347.0.tgz", + "integrity": "sha512-qfnSvkFKCAMjMHR31NdsT0gv5Sq/ZHTUD4yQsSLpbVQ6iYAS834lrzXt41iyEHt57Y514uG7F/Xfvude3u4icQ==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-retry": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.354.0.tgz", + "integrity": "sha512-dnG5Nd/mobbhcWCM71DQWI9+f6b6fDSzALXftFIP/8lsXKRcWDSQuYjrnVST2wZzk/QmdF8TnVD0C1xL14K6CQ==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/service-error-classification": "3.229.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/service-error-classification": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "@aws-sdk/util-retry": "3.347.0", + "tslib": "^2.5.0", "uuid": "^8.3.2" }, "engines": { @@ -618,406 +680,439 @@ } }, "node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.354.0.tgz", + "integrity": "sha512-L6vyAwYrdcOoB4YgCqNJNr+ZZtLHEF2Ym3CTfmFm2srXHqHuRB+mBu0NLV/grz77znIArK1H1ZL/ZaH2I5hclA==", "optional": true, "dependencies": { - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-serde": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.347.0.tgz", + "integrity": "sha512-x5Foi7jRbVJXDu9bHfyCbhYDH5pKK+31MmsSJ3k8rY8keXLBxm2XEEg/AIoV9/TUF9EeVvZ7F1/RmMpJnWQsEg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-signing": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.354.0.tgz", + "integrity": "sha512-Dd+vIhJL0VqqKWqlTKlKC5jkCaEIk73ZEXNfv44XbsI25a0vXbatHp1M8jB/cgkJC/Mri1TX9dmckP/C0FDEwA==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/signature-v4": "3.354.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-stack": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.347.0.tgz", + "integrity": "sha512-Izidg4rqtYMcKuvn2UzgEpPLSmyd8ub9+LQ2oIzG3mpIzCBITq7wp40jN1iNkMg+X6KEnX9vdMJIYZsPYMCYuQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.352.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.352.0.tgz", + "integrity": "sha512-QGqblMTsVDqeomy22KPm9LUW8PHZXBA2Hjk9Hcw8U1uFS8IKYJrewInG3ae2+9FAcTyug4LFWDf8CRr9YH2B3Q==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-endpoints": "3.352.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/node-config-provider": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.354.0.tgz", + "integrity": "sha512-pF1ZGWWvmwbrloNHYF3EDqCb9hq5wfZwDqAwAPhWkYnUYKkR7E7MZVuTwUDU48io8k6Z5pM52l/54w8e8aedTw==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/node-http-handler": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.350.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.350.0.tgz", + "integrity": "sha512-oD96GAlmpzYilCdC8wwyURM5lNfNHZCjm/kxBkQulHKa2kRbIrnD9GfDqdCkWA5cTpjh1NzGLT4D6e6UFDjt9w==", "optional": true, "dependencies": { - "@aws-sdk/abort-controller": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/abort-controller": "3.347.0", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/querystring-builder": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/property-provider": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.353.0.tgz", + "integrity": "sha512-Iu6J59hncaew7eBKroTcLjZ8cgrom0IWyZZ09rsow3rZDHVtw7LQSrUyuqsSbKGY9eRtL7Wa6ZtYHnXFiAE2kg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/protocol-http": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.347.0.tgz", + "integrity": "sha512-2YdBhc02Wvy03YjhGwUxF0UQgrPWEy8Iq75pfS42N+/0B/+eWX1aQgfjFxIpLg7YSjT5eKtYOQGlYd4MFTgj9g==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/querystring-builder": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.347.0.tgz", + "integrity": "sha512-phtKTe6FXoV02MoPkIVV6owXI8Mwr5IBN3bPoxhcPvJG2AjEmnetSIrhb8kwc4oNhlwfZwH6Jo5ARW/VEWbZtg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/querystring-parser": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.347.0.tgz", + "integrity": "sha512-5VXOhfZz78T2W7SuXf2avfjKglx1VZgZgp9Zfhrt/Rq+MTu2D+PZc5zmJHhYigD7x83jLSLogpuInQpFMA9LgA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/service-error-classification": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.347.0.tgz", + "integrity": "sha512-xZ3MqSY81Oy2gh5g0fCtooAbahqh9VhsF8vcKjVX8+XPbGC8y+kej82+MsMg4gYL8gRFB9u4hgYbNgIS6JTAvg==", "optional": true, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/shared-ini-file-loader": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.354.0.tgz", + "integrity": "sha512-UL9loGEsdzpHBu/PtlwUvkl/yRdmWXkySp22jUaeeRtBhiGAnyeYhxJLIt+u+UkX7Mwz+810SaZJqA9ptOXNAg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/signature-v4": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.354.0.tgz", + "integrity": "sha512-bDp43P5NkwwznpZqmsr78DuyqNcjtS4mriuajb8XPhFNo8DrMXUrdrKJ+5aNABW7YG8uK8PSKBpq88ado692/w==", "optional": true, "dependencies": { - "@aws-sdk/is-array-buffer": "3.201.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-hex-encoding": "3.201.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/eventstream-codec": "3.347.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.347.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/smithy-client": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.347.0.tgz", + "integrity": "sha512-PaGTDsJLGK0sTjA6YdYQzILRlPRN3uVFyqeBUkfltXssvUzkm8z2t1lz2H4VyJLAhwnG5ZuZTNEV/2mcWrU7JQ==", "optional": true, "dependencies": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.354.0.tgz", + "integrity": "sha512-KcijiySy0oIyafKQagcwgu0fo35mK+2K8pwxRU1WfXqe80Gn1qGceeWcG4iW+t/rUaxa/LVo857N0LcagxCrZA==", "optional": true, "dependencies": { - "@aws-sdk/client-sso-oidc": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso-oidc": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.347.0.tgz", + "integrity": "sha512-GkCMy79mdjU9OTIe5KT58fI/6uqdf8UmMdWqVHmFJ+UpEzOci7L/uw4sOXWo7xpPzLs6cJ7s5ouGZW4GRPmHFA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/url-parser": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.347.0.tgz", + "integrity": "sha512-lhrnVjxdV7hl+yCnJfDZOaVLSqKjxN20MIOiijRiqaWGLGEAiSqBreMhL89X1WKCifxAs4zZf9YB9SbdziRpAA==", "optional": true, "dependencies": { - "@aws-sdk/querystring-parser": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/querystring-parser": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-base64": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", "optional": true, "dependencies": { - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-body-length-browser": { - "version": "3.188.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-body-length-node": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-buffer-from": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", "optional": true, "dependencies": { - "@aws-sdk/is-array-buffer": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-config-provider": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-defaults-mode-browser": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.353.0.tgz", + "integrity": "sha512-ushvOQKJIH7S6E//xMDPyf2/Bbu0K2A0GJRB88qQV6VKRBo4PEbeHTb6BbzPhYVX0IbY3uR/X7+Xwk4FeEkMWg==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">= 10.0.0" } }, "node_modules/@aws-sdk/util-defaults-mode-node": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.354.0.tgz", + "integrity": "sha512-CaaRVBdOYX4wZadj+CDUxpO+4RjyYJcSv71A60jV6CZ/ya1+oYfmPbG5QZ4AlV6crdev2B+aUoR2LPIYqn/GnQ==", "optional": true, "dependencies": { - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">= 10.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.352.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.352.0.tgz", + "integrity": "sha512-PjWMPdoIUWfBPgAWLyOrWFbdSS/3DJtc0OmFb/JrE8C8rKFYl+VGW5f1p0cVdRWiDR0xCGr0s67p8itAakVqjw==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-hex-encoding": { - "version": "3.201.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.208.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-middleware": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.347.0.tgz", + "integrity": "sha512-8owqUA3ePufeYTUvlzdJ7Z0miLorTwx+rNol5lourGQZ9JXsVMo23+yGA7nOlFuXSGkoKpMOtn6S0BT2bcfeiw==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-retry": { - "version": "3.229.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.347.0.tgz", + "integrity": "sha512-NxnQA0/FHFxriQAeEgBonA43Q9/VPFQa8cfJDuT2A1YZruMasgjcltoZszi1dvoIRWSZsFTW42eY2gdOd0nffQ==", "optional": true, "dependencies": { - "@aws-sdk/service-error-classification": "3.229.0", - "tslib": "^2.3.1" + "@aws-sdk/service-error-classification": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@aws-sdk/util-uri-escape": { - "version": "3.201.0", - "license": "Apache-2.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.347.0.tgz", + "integrity": "sha512-ydxtsKVtQefgbk1Dku1q7pMkjDYThauG9/8mQkZUAVik55OUZw71Zzr3XO8J8RKvQG8lmhPXuAQ0FKAyycc0RA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", + "@aws-sdk/types": "3.347.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.226.0", - "license": "Apache-2.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.354.0.tgz", + "integrity": "sha512-2xkblZS3PGxxh//0lgCwJw2gvh9ZBcI9H9xv05YP7hcwlz9BmkAlbei2i6Uew6agJMLO4unfgWoBTpzp3WLaKg==", "optional": true, "dependencies": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" @@ -1031,24 +1126,26 @@ } } }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.188.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/util-utf8-node": { - "version": "3.208.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", "optional": true, "dependencies": { - "@aws-sdk/util-buffer-from": "3.208.0", "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" } }, "node_modules/@babel/code-frame": { @@ -3176,6 +3273,31 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@smithy/protocol-http": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.0.tgz", + "integrity": "sha512-H5y/kZOqfJSqRkwtcAoVbqONmhdXwSgYNJ1Glk5Ry8qlhVVy5qUzD9EklaCH8/XLnoCsLO/F/Giee8MIvaBRkg==", + "optional": true, + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.0.tgz", + "integrity": "sha512-KzmvisMmuwD2jZXuC9e65JrgsZM97y5NpDU7g347oB+Q+xQLU6hQZ5zFNNbEfwwOJHoOvEVTna+dk1h/lW7alw==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "license": "MIT", @@ -3766,7 +3888,8 @@ }, "node_modules/bowser": { "version": "2.11.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", "optional": true }, "node_modules/brace-expansion": { @@ -4256,9 +4379,10 @@ "license": "MIT" }, "node_modules/cookiejar": { - "version": "2.1.3", - "dev": true, - "license": "MIT" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true }, "node_modules/core-js": { "version": "3.6.5", @@ -5172,18 +5296,25 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "4.0.11", - "license": "MIT", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz", + "integrity": "sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "optional": true, "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" - }, - "funding": { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } }, "node_modules/fastq": { @@ -6650,8 +6781,9 @@ "license": "MIT" }, "node_modules/json5": { - "version": "2.2.1", - "license": "MIT", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -8450,7 +8582,8 @@ }, "node_modules/strnum": { "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "optional": true }, "node_modules/superagent": { @@ -8647,9 +8780,10 @@ "license": "MIT" }, "node_modules/tslib": { - "version": "2.4.1", - "devOptional": true, - "license": "0BSD" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "devOptional": true }, "node_modules/twostep": { "version": "0.4.2", @@ -8830,7 +8964,8 @@ }, "node_modules/uuid": { "version": "8.3.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "optional": true, "bin": { "uuid": "dist/bin/uuid" @@ -8864,9 +8999,9 @@ } }, "node_modules/vm2": { - "version": "3.9.17", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz", - "integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==", + "version": "3.9.19", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz", + "integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==", "optional": true, "dependencies": { "acorn": "^8.7.0", @@ -9063,8 +9198,29 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "optional": true, + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + } + } + }, "@aws-crypto/ie11-detection": { - "version": "2.0.2", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", "optional": true, "requires": { "tslib": "^1.11.1" @@ -9072,19 +9228,23 @@ "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-crypto/sha256-browser": { - "version": "2.0.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", "optional": true, "requires": { - "@aws-crypto/ie11-detection": "^2.0.0", - "@aws-crypto/sha256-js": "^2.0.0", - "@aws-crypto/supports-web-crypto": "^2.0.0", - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" @@ -9092,27 +9252,35 @@ "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-crypto/sha256-js": { - "version": "2.0.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", "optional": true, "requires": { - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" }, "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-crypto/supports-web-crypto": { - "version": "2.0.2", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", "optional": true, "requires": { "tslib": "^1.11.1" @@ -9120,710 +9288,846 @@ "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-crypto/util": { - "version": "2.0.2", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", "optional": true, "requires": { - "@aws-sdk/types": "^3.110.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" }, "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true } } }, "@aws-sdk/abort-controller": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.347.0.tgz", + "integrity": "sha512-P/2qE6ntYEmYG4Ez535nJWZbXqgbkJx8CMz7ChEuEg3Gp3dvVYEKg+iEUEvlqQ2U5dWP5J3ehw5po9t86IsVPQ==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-cognito-identity": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.229.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.354.0.tgz", + "integrity": "sha512-VYoPiup85Zn1uiqn6X7Kl1/5AsihyW0jOPpO5Xv39shRKFTLYWIgPxjg7k+dNPVAX62XrWoWNkGR6sB/JN9Qdg==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.354.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sso": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.354.0.tgz", + "integrity": "sha512-4jmvjJYDaaPmm1n2TG4LYfTEnHLKcJmImgBqhgzhMgaypb4u/k1iw0INV2r/afYPL/FsrLFwc46RM3HYx3nc4A==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sso-oidc": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.354.0.tgz", + "integrity": "sha512-XZcg4s2zKb4S8ltluiw5yxpm974uZqzo2HTECt1lbzUJgVgLsMAh/nPJ1fLqg4jadT+rf8Lq2FEFqOM/vxWT8A==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sts": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.229.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.226.0", - "@aws-sdk/util-defaults-mode-node": "3.226.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.354.0.tgz", + "integrity": "sha512-l9Ar/C/3PNlToM1ukHVfBtp4plbRUxLMYY2DOTMI0nb3jzfcvETBcdEGCP51fX4uAfJ2vc4g5qBF/qXKX0LMWA==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/fetch-http-handler": "3.353.0", + "@aws-sdk/hash-node": "3.347.0", + "@aws-sdk/invalid-dependency": "3.347.0", + "@aws-sdk/middleware-content-length": "3.347.0", + "@aws-sdk/middleware-endpoint": "3.347.0", + "@aws-sdk/middleware-host-header": "3.347.0", + "@aws-sdk/middleware-logger": "3.347.0", + "@aws-sdk/middleware-recursion-detection": "3.347.0", + "@aws-sdk/middleware-retry": "3.354.0", + "@aws-sdk/middleware-sdk-sts": "3.354.0", + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/middleware-user-agent": "3.352.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/node-http-handler": "3.350.0", + "@aws-sdk/smithy-client": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.353.0", + "@aws-sdk/util-defaults-mode-node": "3.354.0", + "@aws-sdk/util-endpoints": "3.352.0", + "@aws-sdk/util-retry": "3.347.0", + "@aws-sdk/util-user-agent-browser": "3.347.0", + "@aws-sdk/util-user-agent-node": "3.354.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "fast-xml-parser": "4.2.4", + "tslib": "^2.5.0" } }, "@aws-sdk/config-resolver": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.354.0.tgz", + "integrity": "sha512-K4XWie8yJPT8bpYVX54VJMQhiJRTw8PrjEs9QrKqvwoCcZ3G4qEt40tIu33XksuokXxk8rrVH5d7odOPBsAtdg==", "optional": true, "requires": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-cognito-identity": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.354.0.tgz", + "integrity": "sha512-Q5UcqASJWqwD4AXpfv4Zpw5tUV/fzbhnEC9TzyB39zXcu4Qd0cQgVQOOq9FX1GbtLNOzkPnbvHsbv2PdEaNM4A==", "optional": true, "requires": { - "@aws-sdk/client-cognito-identity": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-cognito-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-env": { - "version": "3.226.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.353.0.tgz", + "integrity": "sha512-Y4VsNS8O1FAD5J7S5itOhnOghQ5LIXlZ44t35nF8cbcF+JPvY3ToKzYpjYN1jM7DXKqU4shtqgYpzSqxlvEgKQ==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-imds": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.354.0.tgz", + "integrity": "sha512-AB+PuDd1jX6qgz+JYvIyOn8Kz9/lQ60KuY1TFb7g3S8zURw+DSeMJNR1jzEsorWICTzhxXmyasHVMa4Eo4Uq+Q==", "optional": true, "requires": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-ini": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.354.0.tgz", + "integrity": "sha512-bn2ifrRsxWpxzwXa25jRdUECQ1dC+NB3YlRYnGdIaIQLF559N2jnfCabYzqyfKI++WU7aQeMofPe2PxVGlbv9Q==", "optional": true, "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-node": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.229.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.354.0.tgz", + "integrity": "sha512-ltKiRtHfqDaCcrb44DIoSHQ9MposFl/aDtNdu5OdQv/2Q1r7M/r2fQdq9DHOrxeQQjaUH4C6k6fGTsxALTHyNA==", + "optional": true, + "requires": { + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-ini": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-process": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.354.0.tgz", + "integrity": "sha512-AxpASm+tS8V1PY4PLfG9dtqa96lzBJ3niTQb+RAm4uYCddW7gxNDkGB+jSCzVdUPVa3xA2ITBS/ka3C5yM8YWg==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-sso": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.354.0.tgz", + "integrity": "sha512-ihiaUxh8V/nQgTOgQZxWQcbckXhM+J6Wdc4F0z9soi48iSOqzRpzPw5E14wSZScEZjNY/gKEDz8gCt8WkT/G0w==", "optional": true, "requires": { - "@aws-sdk/client-sso": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.229.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/token-providers": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-web-identity": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.354.0.tgz", + "integrity": "sha512-scx9mAf4m3Hc3uMX2Vh8GciEcC/5GqeDI8qc0zBj+UF/5c/GtihZA4WoCV3Sg3jMPDUKY81DiFCtcKHhtUqKfg==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-providers": { - "version": "3.229.0", - "optional": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.229.0", - "@aws-sdk/client-sso": "3.229.0", - "@aws-sdk/client-sts": "3.229.0", - "@aws-sdk/credential-provider-cognito-identity": "3.229.0", - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.229.0", - "@aws-sdk/credential-provider-node": "3.229.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.229.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.354.0.tgz", + "integrity": "sha512-GjkSKGWL+lbEVAYGRvE2kdKn8lnhLEBB98yKMz6k9VhqVBrMPZVGTFTlNNtPRZ7IfnnmgLnk6IHtue9xgaycfg==", + "optional": true, + "requires": { + "@aws-sdk/client-cognito-identity": "3.354.0", + "@aws-sdk/client-sso": "3.354.0", + "@aws-sdk/client-sts": "3.354.0", + "@aws-sdk/credential-provider-cognito-identity": "3.354.0", + "@aws-sdk/credential-provider-env": "3.353.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/credential-provider-ini": "3.354.0", + "@aws-sdk/credential-provider-node": "3.354.0", + "@aws-sdk/credential-provider-process": "3.354.0", + "@aws-sdk/credential-provider-sso": "3.354.0", + "@aws-sdk/credential-provider-web-identity": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-codec": { + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.347.0.tgz", + "integrity": "sha512-61q+SyspjsaQ4sdgjizMyRgVph2CiW4aAtfpoH69EJFJfTxTR/OqnZ9Jx/3YiYi0ksrvDenJddYodfWWJqD8/w==", + "optional": true, + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/fetch-http-handler": { - "version": "3.226.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.353.0.tgz", + "integrity": "sha512-8ic2+4E6jzfDevd++QS1rOR05QFkAhEFbi5Ja3/Zzp7TkWIS8wv5wwMATjNkbbdsXYuB5Lhl/OsjfZmIv5aqRw==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/querystring-builder": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/hash-node": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.347.0.tgz", + "integrity": "sha512-96+ml/4EaUaVpzBdOLGOxdoXOjkPgkoJp/0i1fxOJEvl8wdAQSwc3IugVK9wZkCxy2DlENtgOe6DfIOhfffm/g==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/invalid-dependency": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.347.0.tgz", + "integrity": "sha512-8imQcwLwqZ/wTJXZqzXT9pGLIksTRckhGLZaXT60tiBOPKuerTsus2L59UstLs5LP8TKaVZKFFSsjRIn9dQdmQ==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/is-array-buffer": { - "version": "3.201.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-content-length": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.347.0.tgz", + "integrity": "sha512-i4qtWTDImMaDUtwKQPbaZpXsReiwiBomM1cWymCU4bhz81HL01oIxOxOBuiM+3NlDoCSPr3KI6txZSz/8cqXCQ==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-endpoint": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.347.0.tgz", + "integrity": "sha512-unF0c6dMaUL1ffU+37Ugty43DgMnzPWXr/Jup/8GbK5fzzWT5NQq6dj9KHPubMbWeEjQbmczvhv25JuJdK8gNQ==", "optional": true, "requires": { - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-serde": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/url-parser": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-host-header": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.347.0.tgz", + "integrity": "sha512-kpKmR9OvMlnReqp5sKcJkozbj1wmlblbVSbnQAIkzeQj2xD5dnVR3Nn2ogQKxSmU1Fv7dEroBtrruJ1o3fY38A==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-logger": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.347.0.tgz", + "integrity": "sha512-NYC+Id5UCkVn+3P1t/YtmHt75uED06vwaKyxDy0UmB2K66PZLVtwWbLpVWrhbroaw1bvUHYcRyQ9NIfnVcXQjA==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-recursion-detection": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.347.0.tgz", + "integrity": "sha512-qfnSvkFKCAMjMHR31NdsT0gv5Sq/ZHTUD4yQsSLpbVQ6iYAS834lrzXt41iyEHt57Y514uG7F/Xfvude3u4icQ==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-retry": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.354.0.tgz", + "integrity": "sha512-dnG5Nd/mobbhcWCM71DQWI9+f6b6fDSzALXftFIP/8lsXKRcWDSQuYjrnVST2wZzk/QmdF8TnVD0C1xL14K6CQ==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/service-error-classification": "3.229.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/service-error-classification": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "@aws-sdk/util-retry": "3.347.0", + "tslib": "^2.5.0", "uuid": "^8.3.2" } }, "@aws-sdk/middleware-sdk-sts": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.354.0.tgz", + "integrity": "sha512-L6vyAwYrdcOoB4YgCqNJNr+ZZtLHEF2Ym3CTfmFm2srXHqHuRB+mBu0NLV/grz77znIArK1H1ZL/ZaH2I5hclA==", "optional": true, "requires": { - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-signing": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-serde": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.347.0.tgz", + "integrity": "sha512-x5Foi7jRbVJXDu9bHfyCbhYDH5pKK+31MmsSJ3k8rY8keXLBxm2XEEg/AIoV9/TUF9EeVvZ7F1/RmMpJnWQsEg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-signing": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.354.0.tgz", + "integrity": "sha512-Dd+vIhJL0VqqKWqlTKlKC5jkCaEIk73ZEXNfv44XbsI25a0vXbatHp1M8jB/cgkJC/Mri1TX9dmckP/C0FDEwA==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/signature-v4": "3.354.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-middleware": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-stack": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.347.0.tgz", + "integrity": "sha512-Izidg4rqtYMcKuvn2UzgEpPLSmyd8ub9+LQ2oIzG3mpIzCBITq7wp40jN1iNkMg+X6KEnX9vdMJIYZsPYMCYuQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-user-agent": { - "version": "3.226.0", + "version": "3.352.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.352.0.tgz", + "integrity": "sha512-QGqblMTsVDqeomy22KPm9LUW8PHZXBA2Hjk9Hcw8U1uFS8IKYJrewInG3ae2+9FAcTyug4LFWDf8CRr9YH2B3Q==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-endpoints": "3.352.0", + "tslib": "^2.5.0" } }, "@aws-sdk/node-config-provider": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.354.0.tgz", + "integrity": "sha512-pF1ZGWWvmwbrloNHYF3EDqCb9hq5wfZwDqAwAPhWkYnUYKkR7E7MZVuTwUDU48io8k6Z5pM52l/54w8e8aedTw==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/node-http-handler": { - "version": "3.226.0", + "version": "3.350.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.350.0.tgz", + "integrity": "sha512-oD96GAlmpzYilCdC8wwyURM5lNfNHZCjm/kxBkQulHKa2kRbIrnD9GfDqdCkWA5cTpjh1NzGLT4D6e6UFDjt9w==", "optional": true, "requires": { - "@aws-sdk/abort-controller": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/abort-controller": "3.347.0", + "@aws-sdk/protocol-http": "3.347.0", + "@aws-sdk/querystring-builder": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/property-provider": { - "version": "3.226.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.353.0.tgz", + "integrity": "sha512-Iu6J59hncaew7eBKroTcLjZ8cgrom0IWyZZ09rsow3rZDHVtw7LQSrUyuqsSbKGY9eRtL7Wa6ZtYHnXFiAE2kg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/protocol-http": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.347.0.tgz", + "integrity": "sha512-2YdBhc02Wvy03YjhGwUxF0UQgrPWEy8Iq75pfS42N+/0B/+eWX1aQgfjFxIpLg7YSjT5eKtYOQGlYd4MFTgj9g==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/querystring-builder": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.347.0.tgz", + "integrity": "sha512-phtKTe6FXoV02MoPkIVV6owXI8Mwr5IBN3bPoxhcPvJG2AjEmnetSIrhb8kwc4oNhlwfZwH6Jo5ARW/VEWbZtg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/querystring-parser": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.347.0.tgz", + "integrity": "sha512-5VXOhfZz78T2W7SuXf2avfjKglx1VZgZgp9Zfhrt/Rq+MTu2D+PZc5zmJHhYigD7x83jLSLogpuInQpFMA9LgA==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/service-error-classification": { - "version": "3.229.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.347.0.tgz", + "integrity": "sha512-xZ3MqSY81Oy2gh5g0fCtooAbahqh9VhsF8vcKjVX8+XPbGC8y+kej82+MsMg4gYL8gRFB9u4hgYbNgIS6JTAvg==", "optional": true }, "@aws-sdk/shared-ini-file-loader": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.354.0.tgz", + "integrity": "sha512-UL9loGEsdzpHBu/PtlwUvkl/yRdmWXkySp22jUaeeRtBhiGAnyeYhxJLIt+u+UkX7Mwz+810SaZJqA9ptOXNAg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/signature-v4": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.354.0.tgz", + "integrity": "sha512-bDp43P5NkwwznpZqmsr78DuyqNcjtS4mriuajb8XPhFNo8DrMXUrdrKJ+5aNABW7YG8uK8PSKBpq88ado692/w==", "optional": true, "requires": { - "@aws-sdk/is-array-buffer": "3.201.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-hex-encoding": "3.201.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/eventstream-codec": "3.347.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.347.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.347.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/smithy-client": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.347.0.tgz", + "integrity": "sha512-PaGTDsJLGK0sTjA6YdYQzILRlPRN3uVFyqeBUkfltXssvUzkm8z2t1lz2H4VyJLAhwnG5ZuZTNEV/2mcWrU7JQ==", "optional": true, "requires": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-stack": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/token-providers": { - "version": "3.229.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.354.0.tgz", + "integrity": "sha512-KcijiySy0oIyafKQagcwgu0fo35mK+2K8pwxRU1WfXqe80Gn1qGceeWcG4iW+t/rUaxa/LVo857N0LcagxCrZA==", "optional": true, "requires": { - "@aws-sdk/client-sso-oidc": "3.229.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso-oidc": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/shared-ini-file-loader": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/types": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.347.0.tgz", + "integrity": "sha512-GkCMy79mdjU9OTIe5KT58fI/6uqdf8UmMdWqVHmFJ+UpEzOci7L/uw4sOXWo7xpPzLs6cJ7s5ouGZW4GRPmHFA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/url-parser": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.347.0.tgz", + "integrity": "sha512-lhrnVjxdV7hl+yCnJfDZOaVLSqKjxN20MIOiijRiqaWGLGEAiSqBreMhL89X1WKCifxAs4zZf9YB9SbdziRpAA==", "optional": true, "requires": { - "@aws-sdk/querystring-parser": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/querystring-parser": "3.347.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-base64": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", "optional": true, "requires": { - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-body-length-browser": { - "version": "3.188.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-body-length-node": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-buffer-from": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", "optional": true, "requires": { - "@aws-sdk/is-array-buffer": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-config-provider": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-defaults-mode-browser": { - "version": "3.226.0", + "version": "3.353.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.353.0.tgz", + "integrity": "sha512-ushvOQKJIH7S6E//xMDPyf2/Bbu0K2A0GJRB88qQV6VKRBo4PEbeHTb6BbzPhYVX0IbY3uR/X7+Xwk4FeEkMWg==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-defaults-mode-node": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.354.0.tgz", + "integrity": "sha512-CaaRVBdOYX4wZadj+CDUxpO+4RjyYJcSv71A60jV6CZ/ya1+oYfmPbG5QZ4AlV6crdev2B+aUoR2LPIYqn/GnQ==", "optional": true, "requires": { - "@aws-sdk/config-resolver": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/config-resolver": "3.354.0", + "@aws-sdk/credential-provider-imds": "3.354.0", + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/property-provider": "3.353.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-endpoints": { - "version": "3.226.0", + "version": "3.352.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.352.0.tgz", + "integrity": "sha512-PjWMPdoIUWfBPgAWLyOrWFbdSS/3DJtc0OmFb/JrE8C8rKFYl+VGW5f1p0cVdRWiDR0xCGr0s67p8itAakVqjw==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-hex-encoding": { - "version": "3.201.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-locate-window": { - "version": "3.208.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-middleware": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.347.0.tgz", + "integrity": "sha512-8owqUA3ePufeYTUvlzdJ7Z0miLorTwx+rNol5lourGQZ9JXsVMo23+yGA7nOlFuXSGkoKpMOtn6S0BT2bcfeiw==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-retry": { - "version": "3.229.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.347.0.tgz", + "integrity": "sha512-NxnQA0/FHFxriQAeEgBonA43Q9/VPFQa8cfJDuT2A1YZruMasgjcltoZszi1dvoIRWSZsFTW42eY2gdOd0nffQ==", "optional": true, "requires": { - "@aws-sdk/service-error-classification": "3.229.0", - "tslib": "^2.3.1" + "@aws-sdk/service-error-classification": "3.347.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-uri-escape": { - "version": "3.201.0", + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-user-agent-browser": { - "version": "3.226.0", + "version": "3.347.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.347.0.tgz", + "integrity": "sha512-ydxtsKVtQefgbk1Dku1q7pMkjDYThauG9/8mQkZUAVik55OUZw71Zzr3XO8J8RKvQG8lmhPXuAQ0FKAyycc0RA==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", + "@aws-sdk/types": "3.347.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-user-agent-node": { - "version": "3.226.0", + "version": "3.354.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.354.0.tgz", + "integrity": "sha512-2xkblZS3PGxxh//0lgCwJw2gvh9ZBcI9H9xv05YP7hcwlz9BmkAlbei2i6Uew6agJMLO4unfgWoBTpzp3WLaKg==", "optional": true, "requires": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.354.0", + "@aws-sdk/types": "3.347.0", + "tslib": "^2.5.0" } }, - "@aws-sdk/util-utf8-browser": { - "version": "3.188.0", + "@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" } }, - "@aws-sdk/util-utf8-node": { - "version": "3.208.0", + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", "optional": true, "requires": { - "@aws-sdk/util-buffer-from": "3.208.0", "tslib": "^2.3.1" } }, @@ -11144,6 +11448,25 @@ "@sinonjs/commons": "^1.7.0" } }, + "@smithy/protocol-http": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.0.tgz", + "integrity": "sha512-H5y/kZOqfJSqRkwtcAoVbqONmhdXwSgYNJ1Glk5Ry8qlhVVy5qUzD9EklaCH8/XLnoCsLO/F/Giee8MIvaBRkg==", + "optional": true, + "requires": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@smithy/types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.0.tgz", + "integrity": "sha512-KzmvisMmuwD2jZXuC9e65JrgsZM97y5NpDU7g347oB+Q+xQLU6hQZ5zFNNbEfwwOJHoOvEVTna+dk1h/lW7alw==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + } + }, "@tootallnate/once": { "version": "1.1.2", "optional": true @@ -11546,6 +11869,8 @@ }, "bowser": { "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", "optional": true }, "brace-expansion": { @@ -11824,7 +12149,9 @@ "version": "1.0.6" }, "cookiejar": { - "version": "2.1.3", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, "core-js": { @@ -12383,7 +12710,9 @@ "dev": true }, "fast-xml-parser": { - "version": "4.0.11", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz", + "integrity": "sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ==", "optional": true, "requires": { "strnum": "^1.0.5" @@ -13338,7 +13667,9 @@ "dev": true }, "json5": { - "version": "2.2.1" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonfile": { "version": "4.0.0", @@ -14460,6 +14791,8 @@ }, "strnum": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "optional": true }, "superagent": { @@ -14582,7 +14915,9 @@ "version": "0.0.3" }, "tslib": { - "version": "2.4.1", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", "devOptional": true }, "twostep": { @@ -14682,6 +15017,8 @@ }, "uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "optional": true }, "v8-to-istanbul": { @@ -14700,9 +15037,9 @@ "version": "1.1.2" }, "vm2": { - "version": "3.9.17", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz", - "integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==", + "version": "3.9.19", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz", + "integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==", "optional": true, "requires": { "acorn": "^8.7.0", diff --git a/package.json b/package.json old mode 100644 new mode 100755 diff --git a/src/api/APIErrorTypes.js b/src/api/APIErrorTypes.js old mode 100644 new mode 100755 diff --git a/src/api/index.js b/src/api/index.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/auth.js b/src/api/middleware/auth.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/company.js b/src/api/middleware/company.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/errorHandler.js b/src/api/middleware/errorHandler.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/files.js b/src/api/middleware/files.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/offer.js b/src/api/middleware/offer.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/utils.js b/src/api/middleware/utils.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/application.js b/src/api/middleware/validators/application.js old mode 100644 new mode 100755 index 59cd85c9..5f5b4698 --- a/src/api/middleware/validators/application.js +++ b/src/api/middleware/validators/application.js @@ -60,6 +60,13 @@ export const reject = useExpressValidators([ .withMessage(ValidationReasons.TOO_SHORT(CompanyApplicationConstants.rejectReason.min_length)), ]); +const isAfterSubmissionDateFrom = (submissionDateTo, { req }) => { + + const { submissionDateFrom } = req.query; + + return submissionDateFrom <= submissionDateTo; +}; + const sortByParamValidator = (val) => { const regex = /^(\w+(:(desc|asc))?)(,\w+(:(desc|asc))?)*$/; @@ -84,31 +91,43 @@ const parseSortByField = (val) => val.split(","); export const search = useExpressValidators([ query("limit", ValidationReasons.DEFAULT) .optional() - .isInt({ min: 1, max: MAX_LIMIT_RESULTS }) - .withMessage(ValidationReasons.MAX(MAX_LIMIT_RESULTS)), + .isInt().withMessage(ValidationReasons.INT).bail() + .toInt() + /* + Split validation checks in order to provide better error messages. + Another solution would be to return a "compound" error message, aka, one that contains both pieces of information. + The latter could help keep validation chains smaller. + */ + .isInt({ min: 1 }).withMessage(ValidationReasons.MIN(1)).bail() + .isInt({ max: MAX_LIMIT_RESULTS }).withMessage(ValidationReasons.MAX(MAX_LIMIT_RESULTS)).bail() + .toInt(), query("offset", ValidationReasons.DEFAULT) .optional() - .isInt({ min: 0 }) - .withMessage(ValidationReasons.MIN(0)), + .isInt().withMessage(ValidationReasons.INT).bail() + .toInt() + .isInt({ min: 0 }).withMessage(ValidationReasons.MIN(0)).bail() + .toInt(), query("companyName", ValidationReasons.DEFAULT) .optional() - .isString().withMessage(ValidationReasons.STRING), + .isString().withMessage(ValidationReasons.STRING).bail(), query("state", ValidationReasons.DEFAULT) .optional() - .customSanitizer(ensureArray) .isArray().withMessage(ValidationReasons.ARRAY).bail() + .customSanitizer(ensureArray) .custom(valuesInSet(Object.keys(ApplicationStatus))), query("submissionDateFrom", ValidationReasons.DEFAULT) .optional() - .toDate() - .isISO8601().withMessage(ValidationReasons.DATE), + .isISO8601().withMessage(ValidationReasons.DATE).bail() + .toDate(), query("submissionDateTo", ValidationReasons.DEFAULT) .optional() + .isISO8601().withMessage(ValidationReasons.DATE).bail() .toDate() - .isISO8601().withMessage(ValidationReasons.DATE), + .if((_, { req }) => req.query.submissionDateFrom !== undefined) + .custom(isAfterSubmissionDateFrom).withMessage(ValidationReasons.MUST_BE_AFTER("submissionDateFrom")), query("sortBy", ValidationReasons.DEFAULT) .optional() - .isString().withMessage(ValidationReasons.STRING) + .isString().withMessage(ValidationReasons.STRING).bail() .custom(sortByParamValidator) .customSanitizer(parseSortByField), ]); diff --git a/src/api/middleware/validators/auth.js b/src/api/middleware/validators/auth.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/company.js b/src/api/middleware/validators/company.js old mode 100644 new mode 100755 index 82fe9fa4..1ba36a9a --- a/src/api/middleware/validators/company.js +++ b/src/api/middleware/validators/company.js @@ -10,6 +10,8 @@ export const MAX_LIMIT_RESULTS = 100; const DEFAULT_PUBLISH_DATE = new Date(Date.now()).toISOString(); export const finish = useExpressValidators([ + /* body("logo", ValidationReasons.DEFAULT) + .exists().withMessage(ValidationReasons.REQUIRED).bail(), */ body("bio", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() .isString().withMessage(ValidationReasons.STRING) @@ -25,7 +27,9 @@ export const finish = useExpressValidators([ export const list = useExpressValidators([ query("limit", ValidationReasons.DEFAULT) .optional() - .isInt({ min: 1, max: MAX_LIMIT_RESULTS }) + .isInt({ min: 1 }) + .withMessage(ValidationReasons.MIN(1)).bail() + .isInt({ max: MAX_LIMIT_RESULTS }) .withMessage(ValidationReasons.MAX(MAX_LIMIT_RESULTS)), query("offset", ValidationReasons.DEFAULT) .optional() @@ -126,9 +130,10 @@ export const edit = useExpressValidators([ .withMessage(ValidationReasons.TOO_LONG(CompanyConstants.bio.max_length)), body("contacts", ValidationReasons.DEFAULT) .optional() - .customSanitizer(ensureArray) + .isArray().withMessage(ValidationReasons.ARRAY).bail() .isArray({ min: CompanyConstants.contacts.min_length, max: CompanyConstants.contacts.max_length }) - .withMessage(ValidationReasons.ARRAY_SIZE(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length)), + .withMessage(ValidationReasons.ARRAY_SIZE(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length)) + .customSanitizer(ensureArray), body("logo", ValidationReasons.DEFAULT) .optional() .isString().withMessage(ValidationReasons.STRING).bail() diff --git a/src/api/middleware/validators/offer.js b/src/api/middleware/validators/offer.js old mode 100644 new mode 100755 index c306f099..ad76ce25 --- a/src/api/middleware/validators/offer.js +++ b/src/api/middleware/validators/offer.js @@ -80,7 +80,6 @@ export const create = useExpressValidators([ .custom(publishEndDateAfterPublishDate) .custom(publishEndDateLimit), - body("jobMinDuration", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT), @@ -119,7 +118,7 @@ export const create = useExpressValidators([ body("jobType", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() .isString().withMessage(ValidationReasons.STRING).bail() - .isIn(JobTypes).withMessage(ValidationReasons.IN_ARRAY(JobTypes)), + .isIn(JobTypes).withMessage((value) => ValidationReasons.IN_ARRAY(JobTypes, value)), body("fields", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() @@ -388,7 +387,7 @@ export const edit = useExpressValidators([ body("jobType", ValidationReasons.DEFAULT) .optional() .isString().withMessage(ValidationReasons.STRING).bail() - .isIn(JobTypes).withMessage(ValidationReasons.IN_ARRAY(JobTypes)), + .isIn(JobTypes).withMessage((value) => ValidationReasons.IN_ARRAY(JobTypes, value)), body("fields", ValidationReasons.DEFAULT) .optional() @@ -495,7 +494,7 @@ export const get = useExpressValidators([ query("jobType") .optional() .isString().withMessage(ValidationReasons.STRING).bail() - .isIn(JobTypes).withMessage(ValidationReasons.IN_ARRAY(JobTypes)), + .isIn(JobTypes).withMessage((value) => ValidationReasons.IN_ARRAY(JobTypes, value)), query("jobMinDuration", ValidationReasons.DEFAULT) .optional() @@ -522,7 +521,7 @@ export const get = useExpressValidators([ query("sortBy", ValidationReasons.DEFAULT) .optional() .isString().withMessage(ValidationReasons.STRING).bail() - .isIn(OfferConstants.SortableFields).withMessage(ValidationReasons.IN_ARRAY(OfferConstants.SortableFields)), + .isIn(OfferConstants.SortableFields).withMessage((value) => ValidationReasons.IN_ARRAY(OfferConstants.SortableFields, value)), query("descending", ValidationReasons.DEFAULT) .optional() diff --git a/src/api/middleware/validators/validationReasons.js b/src/api/middleware/validators/validationReasons.js old mode 100644 new mode 100755 diff --git a/src/api/middleware/validators/validatorUtils.js b/src/api/middleware/validators/validatorUtils.js old mode 100644 new mode 100755 diff --git a/src/api/routes/application.js b/src/api/routes/application.js old mode 100644 new mode 100755 diff --git a/src/api/routes/auth.js b/src/api/routes/auth.js old mode 100644 new mode 100755 diff --git a/src/api/routes/company.js b/src/api/routes/company.js old mode 100644 new mode 100755 index 01db8ab2..e7d346ad --- a/src/api/routes/company.js +++ b/src/api/routes/company.js @@ -13,7 +13,7 @@ import { concurrentOffersNotExceeded } from "../middleware/validators/validatorU import { or } from "../middleware/utils.js"; -import * as fileMiddleware from "../middleware/files.js"; +import * as fileMiddleware from "../middleware/files.js"; import OfferService from "../../services/offer.js"; import AccountService from "../../services/account.js"; import Offer from "../../models/Offer.js"; diff --git a/src/api/routes/offer.js b/src/api/routes/offer.js old mode 100644 new mode 100755 diff --git a/src/api/routes/review.js b/src/api/routes/review.js old mode 100644 new mode 100755 diff --git a/src/config/env.js b/src/config/env.js old mode 100644 new mode 100755 diff --git a/src/config/multer.js b/src/config/multer.js old mode 100644 new mode 100755 diff --git a/src/config/passport.js b/src/config/passport.js old mode 100644 new mode 100755 diff --git a/src/email-templates/accountManagement.js b/src/email-templates/accountManagement.js old mode 100644 new mode 100755 diff --git a/src/email-templates/approval_notification.handlebars b/src/email-templates/approval_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/companyApplicationApproval.js b/src/email-templates/companyApplicationApproval.js old mode 100644 new mode 100755 diff --git a/src/email-templates/companyManagement.js b/src/email-templates/companyManagement.js old mode 100644 new mode 100755 diff --git a/src/email-templates/companyOfferDisabled.js b/src/email-templates/companyOfferDisabled.js old mode 100644 new mode 100755 diff --git a/src/email-templates/company_blocked_notification.handlebars b/src/email-templates/company_blocked_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/company_deleted_notification.handlebars b/src/email-templates/company_deleted_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/company_disabled_notification.handlebars b/src/email-templates/company_disabled_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/company_enabled_notification.handlebars b/src/email-templates/company_enabled_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/company_unblocked_notification.handlebars b/src/email-templates/company_unblocked_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/layouts/main.handlebars b/src/email-templates/layouts/main.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/new_company_application_admins.handlebars b/src/email-templates/new_company_application_admins.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/new_company_application_company.handlebars b/src/email-templates/new_company_application_company.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/offer_disabled_notification.handlebars b/src/email-templates/offer_disabled_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/rejection_notification.handlebars b/src/email-templates/rejection_notification.handlebars old mode 100644 new mode 100755 diff --git a/src/email-templates/request_password_recovery.handlebars b/src/email-templates/request_password_recovery.handlebars old mode 100644 new mode 100755 diff --git a/src/index.js b/src/index.js old mode 100644 new mode 100755 diff --git a/src/lib/emailService.js b/src/lib/emailService.js old mode 100644 new mode 100755 diff --git a/src/lib/passwordHashing.js b/src/lib/passwordHashing.js old mode 100644 new mode 100755 diff --git a/src/lib/token.js b/src/lib/token.js old mode 100644 new mode 100755 diff --git a/src/loaders/emailService.js b/src/loaders/emailService.js old mode 100644 new mode 100755 diff --git a/src/loaders/express.js b/src/loaders/express.js old mode 100644 new mode 100755 diff --git a/src/loaders/index.js b/src/loaders/index.js old mode 100644 new mode 100755 diff --git a/src/loaders/mongoose.js b/src/loaders/mongoose.js old mode 100644 new mode 100755 diff --git a/src/loaders/requestEnhancers.js b/src/loaders/requestEnhancers.js old mode 100644 new mode 100755 diff --git a/src/loaders/static.js b/src/loaders/static.js old mode 100644 new mode 100755 diff --git a/src/models/Account.js b/src/models/Account.js old mode 100644 new mode 100755 diff --git a/src/models/Company.js b/src/models/Company.js old mode 100644 new mode 100755 diff --git a/src/models/CompanyApplication.js b/src/models/CompanyApplication.js old mode 100644 new mode 100755 diff --git a/src/models/ExampleUser.js b/src/models/ExampleUser.js deleted file mode 100644 index d66d3db1..00000000 --- a/src/models/ExampleUser.js +++ /dev/null @@ -1,15 +0,0 @@ -import mongoose from "mongoose"; -const { Schema } = mongoose; - -// First we create our schema -const ExampleUserSchema = new Schema({ - username: { type: String, unique: true }, - // The schemas can have very flexible type definition options, see https://mongoosejs.com/docs/schematypes.html - age: { type: Number, default: 420 }, -}); - -// Then we enable using it by converting it into a model -const ExampleUser = mongoose.model("ExampleUser", ExampleUserSchema); - -// Which we can then export -export default ExampleUser; diff --git a/src/models/Offer.js b/src/models/Offer.js old mode 100644 new mode 100755 diff --git a/src/models/Point.js b/src/models/Point.js old mode 100644 new mode 100755 diff --git a/src/models/constants/Account.js b/src/models/constants/Account.js old mode 100644 new mode 100755 diff --git a/src/models/constants/ApplicationStatus.js b/src/models/constants/ApplicationStatus.js old mode 100644 new mode 100755 diff --git a/src/models/constants/Company.js b/src/models/constants/Company.js old mode 100644 new mode 100755 diff --git a/src/models/constants/CompanyApplication.js b/src/models/constants/CompanyApplication.js old mode 100644 new mode 100755 diff --git a/src/models/constants/FieldTypes.js b/src/models/constants/FieldTypes.js old mode 100644 new mode 100755 diff --git a/src/models/constants/JobTypes.js b/src/models/constants/JobTypes.js old mode 100644 new mode 100755 diff --git a/src/models/constants/Offer.js b/src/models/constants/Offer.js old mode 100644 new mode 100755 diff --git a/src/models/constants/TechnologyTypes.js b/src/models/constants/TechnologyTypes.js old mode 100644 new mode 100755 diff --git a/src/models/constants/TimeConstants.js b/src/models/constants/TimeConstants.js old mode 100644 new mode 100755 diff --git a/src/models/modelUtils.js b/src/models/modelUtils.js old mode 100644 new mode 100755 diff --git a/src/services/account.js b/src/services/account.js old mode 100644 new mode 100755 diff --git a/src/services/application.js b/src/services/application.js old mode 100644 new mode 100755 diff --git a/src/services/company.js b/src/services/company.js old mode 100644 new mode 100755 diff --git a/src/services/offer.js b/src/services/offer.js old mode 100644 new mode 100755 diff --git a/src/setupTests.js b/src/setupTests.js old mode 100644 new mode 100755 diff --git a/test/data/logo-niaefeup-10mb.png b/test/data/logo-niaefeup-10mb.png old mode 100644 new mode 100755 diff --git a/test/data/logo-niaefeup.png b/test/data/logo-niaefeup.png old mode 100644 new mode 100755 diff --git a/test/data/not-a-logo.txt b/test/data/not-a-logo.txt old mode 100644 new mode 100755 diff --git a/test/end-to-end/application.js b/test/end-to-end/application.js deleted file mode 100644 index 28fb316f..00000000 --- a/test/end-to-end/application.js +++ /dev/null @@ -1,185 +0,0 @@ -import EmailService from "../../src/lib/emailService"; -import { StatusCodes as HTTPStatus } from "http-status-codes"; -import CompanyApplication, { CompanyApplicationRules } from "../../src/models/CompanyApplication"; -import Account from "../../src/models/Account"; -import ValidatorTester from "../utils/ValidatorTester"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; -import CompanyApplicationConstants from "../../src/models/constants/CompanyApplication"; -import AccountConstants from "../../src/models/constants/Account"; -import CompanyConstants from "../../src/models/constants/Company"; -import { NEW_COMPANY_APPLICATION_ADMINS, NEW_COMPANY_APPLICATION_COMPANY } from "../../src/email-templates/companyApplicationApproval"; -import config from "../../src/config/env"; - - -describe("Company application endpoint test", () => { - describe("POST /application", () => { - describe("Input Validation (unsuccessful application)", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/apply/company").send(params)); - const BodyValidatorTester = EndpointValidatorTester("body"); - describe("email", () => { - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - describe("password", () => { - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); - FieldValidatorTester.hasNumber(); - }); - - describe("motivation", () => { - const FieldValidatorTester = BodyValidatorTester("motivation"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(CompanyApplicationConstants.motivation.min_length); - FieldValidatorTester.hasMaxLength(CompanyApplicationConstants.motivation.max_length); - }); - - describe("companyName", () => { - const FieldValidatorTester = BodyValidatorTester("companyName"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(CompanyConstants.companyName.min_length); - FieldValidatorTester.hasMaxLength(CompanyConstants.companyName.max_length); - }); - }); - - describe("Without any existing application and accounts", () => { - beforeAll(async () => { - await CompanyApplication.deleteMany({}); - await Account.deleteMany({}); - }); - - const RealDateNow = Date.now; - const mockCurrentDate = new Date("2019-11-23"); - - beforeEach(() => { - Date.now = () => mockCurrentDate.getTime(); - }); - - afterEach(() => { - Date.now = RealDateNow; - }); - - test("Valid creation", async () => { - const application = { - email: "test@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation because otherwise, the tests would not exist.", - }; - const res = await request() - .post("/apply/company") - .send(application); - - expect(res.status).toBe(HTTPStatus.OK); - const created_application_id = res.body._id; - - const created_application = await CompanyApplication.findById(created_application_id); - - expect(created_application).toBeDefined(); - expect(created_application).toHaveProperty("email", application.email); - expect(created_application).toHaveProperty("companyName", application.companyName); - expect(created_application).toHaveProperty("motivation", application.motivation); - expect(created_application).toHaveProperty("submittedAt", mockCurrentDate); - }); - - test("Should send an email to admin and to company user", async () => { - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation because otherwise, the tests would not exist.", - }; - const res = await request() - .post("/apply/company") - .send(application); - - expect(res.status).toBe(HTTPStatus.OK); - - const adminEmailOptions = NEW_COMPANY_APPLICATION_ADMINS( - application.email, application.companyName, application.motivation); - const companyEmailOptions = NEW_COMPANY_APPLICATION_COMPANY( - application.companyName, res.body._id); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: adminEmailOptions.subject, - to: config.mail_from, - template: adminEmailOptions.template, - context: adminEmailOptions.context, - })); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: companyEmailOptions.subject, - to: application.email, - template: companyEmailOptions.template, - context: { ...companyEmailOptions.context }, - })); - }); - - describe("Invalid input", () => { - test("Should fail while using an email with an associated Account", async () => { - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - }; - - await Account.create({ - email: application.email, - password: application.password, - isAdmin: true, - }); - const res = await request() - .post("/apply/company") - .send(application); - - expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.ALREADY_EXISTS("email"), - "param": "email", - "value": application.email, - }); - }); - - test("Should fail while using an email with an associated application that was not rejected", async () => { - - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - }; - - await CompanyApplication.deleteMany({}); - // Guarantees that the company application will succeed regarding account rules - await Account.deleteOne({ email: application.email }); - - - // Existing Application - Default `Pending` state - await CompanyApplication.create({ - ...application, - submittedAt: Date.now(), - }); - - const res = await request() - .post("/apply/company") - .send(application); - - expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": CompanyApplicationRules.ONLY_ONE_APPLICATION_ACTIVE_PER_EMAIL.msg, - "param": "email", - "value": application.email, - }); - }); - }); - }); - }); -}); diff --git a/test/end-to-end/applications/company/search.js b/test/end-to-end/applications/company/search.js new file mode 100755 index 00000000..92271eff --- /dev/null +++ b/test/end-to-end/applications/company/search.js @@ -0,0 +1,398 @@ +import { StatusCodes } from "http-status-codes"; +import CompanyApplication, { CompanyApplicationProps } from "../../../../src/models/CompanyApplication"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import ApplicationStatus from "../../../../src/models/constants/ApplicationStatus"; + +import { MAX_LIMIT_RESULTS } from "../../../../src/api/middleware/validators/application"; + +import ValidatorTester from "../../../utils/ValidatorTester"; +// import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; + +describe("GET /applications/company/search", () => { + + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + const pendingApplication = { + email: "test2@test.com", + password: "password123", + companyName: "testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + submittedAt: new Date("2019-11-25").toISOString(), + }; + + const approvedApplication = { + ...pendingApplication, + submittedAt: new Date("2019-11-24").toISOString(), + approvedAt: new Date(Date.parse(pendingApplication.submittedAt) + (24 * 60 * 60 * 1000)).toISOString(), + companyName: "approved Testing company", + email: `approved${pendingApplication.email}`, + }; + + const rejectedApplication = { + ...pendingApplication, + submittedAt: new Date("2019-11-23").toISOString(), + rejectedAt: new Date(Date.parse(pendingApplication.submittedAt) + (24 * 60 * 60 * 1000)).toISOString(), + companyName: "rejected Testing company", + email: `rejected${pendingApplication.email}`, + rejectReason: "2bad4nij0bs", + }; + + beforeAll(async () => { + await Account.deleteMany({}); + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + }); + + beforeEach(async () => { + // Login by default + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await CompanyApplication.deleteMany({}); + }); + + describe("Input Validation", () => { + + const EndpointValidatorTester = ValidatorTester( + (params) => test_agent.get("/applications/company/search").query(params) + ); + const QueryValidatorTester = EndpointValidatorTester("query"); + + describe("limit", () => { + const FieldValidatorTester = QueryValidatorTester("limit"); + FieldValidatorTester.mustBeNumber(); + FieldValidatorTester.mustBeGreaterThanOrEqualTo(1); + FieldValidatorTester.mustBeLessThanOrEqualTo(MAX_LIMIT_RESULTS); + }); + + describe("offset", () => { + const FieldValidatorTester = QueryValidatorTester("offset"); + FieldValidatorTester.mustBeNumber(); + FieldValidatorTester.mustBeGreaterThanOrEqualTo(0); + }); + + describe("companyName", () => { + // the only validation that could be done on this is to test if the value is a string. + // However, since this is coming from the query, it is always parsed as a string, so this check would never be exercised + }); + + describe("state", () => { + const FieldValidatorTester = QueryValidatorTester("state"); + FieldValidatorTester.mustBeArray(); + }); + + describe("submissionDateFrom", () => { + const FieldValidatorTester = QueryValidatorTester("submissionDateFrom"); + FieldValidatorTester.mustBeDate(); + }); + + describe("submissionDateTo", () => { + const FieldValidatorTester = QueryValidatorTester("submissionDateTo"); + FieldValidatorTester.mustBeDate(); + FieldValidatorTester.mustBeAfter("submissionDateFrom"); + }); + + describe("sortBy", () => { + const FieldValidatorTester = QueryValidatorTester("sortBy"); + // FieldValidatorTester.mustBeString(); Same reason as above + + // Validation for this is harder to perform since there is custom validation employed + // Perhaps we could employ a custom test, leaving as TODO + FieldValidatorTester.mustBeInArray(Object.keys(CompanyApplicationProps)); + }); + }); + + test("Should fail to search company applications if not logged in", async () => { + + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + + await request() + .get("/applications/company/search") + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("Should return empty list if no applications exist", async () => { + + await CompanyApplication.deleteMany({}); + + const emptyRes = await test_agent + .get("/applications/company/search") + .expect(StatusCodes.OK); + + expect(emptyRes.body.applications).toEqual([]); + }); + + test("Should list existing applications", async () => { + await CompanyApplication.create({ + ...pendingApplication, + submittedAt: Date.now(), + }); + + const nonEmptyRes = await test_agent + .get("/applications/company/search"); + + expect(nonEmptyRes.status).toBe(StatusCodes.OK); + expect(nonEmptyRes.body.applications.length).toBe(1); + expect(nonEmptyRes.body.applications[0]).toHaveProperty("email", pendingApplication.email); + }); + + describe("Filter application results", () => { + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + beforeEach(async () => { + await CompanyApplication.create([pendingApplication, approvedApplication, rejectedApplication]); + }); + + afterEach(async () => { + await CompanyApplication.deleteMany({}); + }); + + describe("Should filter by company name", () => { + + test("Should filter by company name with full name query", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + companyName: approvedApplication.companyName + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(1); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + }); + + test("Should filter by company name with partial name query", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + companyName: "Testing company" + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + }); + }); + + describe("Should filter by state", () => { + + test("Should fail with badly formatted query", async () => { + + const res = await test_agent + // FIXME: having only one element makes it so that state is parsed as a single value + .get(`/applications/company/search?state[]=<["${ApplicationStatus.APPROVED}"]`) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body.errors[0]).toStrictEqual({ + location: "query", + msg: "must-be-in:[PENDING,APPROVED,REJECTED]", // FIXME: ValidationReasons.IN_ARRAY(ApplicationStatus), + param: "state", + value: [`<["${ApplicationStatus.APPROVED}"]`] + }); + }); + + test("Should succeed with single state query", async () => { + + const res = await test_agent + .get(`/applications/company/search?state[]=${ApplicationStatus.APPROVED}`) + .expect(StatusCodes.OK); + + expect(res.body.applications.length).toBe(1); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + + }); + + test("Should succeed with multi state query", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + state: [ + ApplicationStatus.APPROVED, + ApplicationStatus.PENDING + ] + }) + .expect(StatusCodes.OK); + + expect(res.body.applications.length).toBe(2); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + }); + }); + + describe("Should filter by date", () => { + + test("Should succeed when searching after date", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + submissionDateFrom: approvedApplication.submittedAt + }) + .expect(StatusCodes.OK); + + expect(res.body.applications.length).toBe(2); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + }); + + test("Should succeed when searching before date", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + submissionDateTo: approvedApplication.submittedAt + }) + .expect(StatusCodes.OK); + + expect(res.body.applications.length).toBe(2); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + }); + + test("Should succeed when searching between dates", async () => { + const res = await test_agent + .get("/applications/company/search?" + + `submissionDateFrom=${approvedApplication.submittedAt}&` + + `submissionDateTo=${approvedApplication.submittedAt}`); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.applications.length).toBe(1); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + }); + }); + }); + + describe("Sort application results", () => { + + const buildOrderingParam = (field, order = undefined) => `${field}${order ? `:${order}` : ""}`; + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + }); + + beforeEach(async () => { + await CompanyApplication.create([pendingApplication, approvedApplication, rejectedApplication]); + }); + + afterEach(async () => { + await CompanyApplication.deleteMany({}); + }); + + describe("Should sort by company name", () => { + + test("Should sort by company name using default ordering", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("companyName") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", approvedApplication.companyName); + }); + + test("Should sort by company name in descending order", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("companyName", "desc") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", approvedApplication.companyName); + }); + + test("Should sort by company name in ascending order", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("companyName", "asc") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", pendingApplication.companyName); + }); + }); + + describe("Should sort by submission date", () => { + + test("Should sort by submission date using default ordering", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("submittedAt") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + }); + + test("Should sort by submission date in descending order", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("submittedAt", "desc") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); + }); + + test("Should sort by submission date in ascending order", async () => { + const res = await test_agent + .get("/applications/company/search") + .query({ + sortBy: buildOrderingParam("submittedAt", "asc") + }) + .expect(StatusCodes.OK); + + expect(res.body.applications).toHaveLength(3); + expect(res.body.applications[0]).toHaveProperty("companyName", rejectedApplication.companyName); + expect(res.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); + expect(res.body.applications[2]).toHaveProperty("companyName", pendingApplication.companyName); + }); + }); + }); +}); diff --git a/test/end-to-end/apply/company.js b/test/end-to-end/apply/company.js new file mode 100755 index 00000000..aa00a7ea --- /dev/null +++ b/test/end-to-end/apply/company.js @@ -0,0 +1,182 @@ +import EmailService from "../../../src/lib/emailService"; +import { StatusCodes } from "http-status-codes"; +import CompanyApplication, { CompanyApplicationRules } from "../../../src/models/CompanyApplication"; +import Account from "../../../src/models/Account"; +import ValidatorTester from "../../utils/ValidatorTester"; +import ValidationReasons from "../../../src/api/middleware/validators/validationReasons"; +import CompanyApplicationConstants from "../../../src/models/constants/CompanyApplication"; +import AccountConstants from "../../../src/models/constants/Account"; +import CompanyConstants from "../../../src/models/constants/Company"; +import { NEW_COMPANY_APPLICATION_ADMINS, NEW_COMPANY_APPLICATION_COMPANY } from "../../../src/email-templates/companyApplicationApproval"; +import config from "../../../src/config/env"; + +describe("POST /apply/company", () => { + describe("Input Validation", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/apply/company").send(params)); + const BodyValidatorTester = EndpointValidatorTester("body"); + + describe("email", () => { + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + + describe("password", () => { + const FieldValidatorTester = BodyValidatorTester("password"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); + FieldValidatorTester.hasNumber(); + }); + + describe("motivation", () => { + const FieldValidatorTester = BodyValidatorTester("motivation"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(CompanyApplicationConstants.motivation.min_length); + FieldValidatorTester.hasMaxLength(CompanyApplicationConstants.motivation.max_length); + }); + + describe("companyName", () => { + const FieldValidatorTester = BodyValidatorTester("companyName"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(CompanyConstants.companyName.min_length); + FieldValidatorTester.hasMaxLength(CompanyConstants.companyName.max_length); + }); + }); + + describe("Without any existing application and accounts", () => { + + const RealDateNow = Date.now; + const mockCurrentDate = new Date("2019-11-23"); + + beforeAll(async () => { + await CompanyApplication.deleteMany({}); + await Account.deleteMany({}); + + Date.now = () => mockCurrentDate.getTime(); + }); + + afterAll(async () => { + await CompanyApplication.deleteMany({}); + await Account.deleteMany({}); + + Date.now = RealDateNow; + }); + + test("Valid creation", async () => { + const application = { + email: "test@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation because otherwise, the tests would not exist.", + }; + const res = await request() + .post("/apply/company") + .send(application) + .expect(StatusCodes.OK); + + // eslint-disable-next-line no-unused-vars + const { password, ...rest } = application; + expect(res.body).toMatchObject(rest); + }); + + test("Should send an email to admin and to company user", async () => { + const application = { + email: "test2@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation because otherwise, the tests would not exist.", + }; + const res = await request() + .post("/apply/company") + .send(application) + .expect(StatusCodes.OK); + + const adminEmailOptions = NEW_COMPANY_APPLICATION_ADMINS( + application.email, application.companyName, application.motivation); + const companyEmailOptions = NEW_COMPANY_APPLICATION_COMPANY( + application.companyName, res.body._id); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: adminEmailOptions.subject, + to: config.mail_from, + template: adminEmailOptions.template, + context: adminEmailOptions.context, + })); + + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: companyEmailOptions.subject, + to: application.email, + template: companyEmailOptions.template, + context: { ...companyEmailOptions.context }, + })); + }); + + describe("Invalid input", () => { + + const application = { + email: "test2@test.com", + password: "password123", + companyName: "Testing company", + motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", + }; + + beforeAll(async () => { + await Account.deleteMany({}); + await CompanyApplication.deleteMany({}); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await CompanyApplication.deleteMany({}); + }); + + test("Should fail while using an email with an associated Account", async () => { + + await Account.create({ + email: application.email, + password: application.password, + isAdmin: true, + }); + + const res = await request() + .post("/apply/company") + .send(application) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.ALREADY_EXISTS("email"), + "param": "email", + "value": application.email, + }); + }); + + test("Should fail while using an email with an associated application that was not rejected", async () => { + + // Guarantees that the company application will succeed regarding account rules + await Account.deleteOne({ email: application.email }); + + // Existing Application - Default `Pending` state + await CompanyApplication.create({ + ...application, + submittedAt: Date.now(), + }); + + const res = await request() + .post("/apply/company") + .send(application) + .expect(StatusCodes.UNPROCESSABLE_ENTITY); + + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": CompanyApplicationRules.ONLY_ONE_APPLICATION_ACTIVE_PER_EMAIL.msg, + "param": "email", + "value": application.email, + }); + }); + }); + }); +}); diff --git a/test/end-to-end/auth.js b/test/end-to-end/auth.js deleted file mode 100644 index 3d005b60..00000000 --- a/test/end-to-end/auth.js +++ /dev/null @@ -1,477 +0,0 @@ -import { StatusCodes as HTTPStatus } from "http-status-codes"; -import { ErrorTypes } from "../../src/api/middleware/errorHandler"; -import Account from "../../src/models/Account"; -import ValidatorTester from "../utils/ValidatorTester"; -import withGodToken from "../utils/GodToken"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; -import hash from "../../src/lib/passwordHashing"; -import AccountConstants, { RECOVERY_LINK_EXPIRATION } from "../../src/models/constants/Account"; -import Company from "../../src/models/Company"; -import EmailService from "../../src/lib/emailService"; -import * as token from "../../src/lib/token"; -import { REQUEST_ACCOUNT_RECOVERY } from "../../src/email-templates/accountManagement"; -import env from "../../src/config/env"; -import { SECOND_IN_MS } from "../../src/models/constants/TimeConstants"; - -const generateTokenSpy = jest.spyOn(token, "generateToken"); -jest.spyOn(token, "verifyAndDecodeToken"); - -describe("Register endpoint test", () => { - describe("Input Validation (unsuccessful registration)", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/register").send(withGodToken(params))); - const BodyValidatorTester = EndpointValidatorTester("body"); - describe("email", () => { - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - describe("password", () => { - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); - FieldValidatorTester.hasNumber(); - }); - }); - - describe("Without pre-existing users", () => { - beforeAll(async () => { - await Account.deleteMany({}); - }); - - test("Should return forbidden", async () => { - const user = { - email: "user@email.com", - password: "password123", - }; - - const res = await request() - .post("/auth/register") - .send(user); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - - test("Should make a successful registration", async () => { - const user = { - email: "user@email.com", - password: "password123", - }; - - const res = await request() - .post("/auth/register") - .send(withGodToken(user)); - - expect(res.status).toBe(HTTPStatus.OK); - - const registered_user = await Account.findOne({ email: user.email }); - expect(registered_user).toBeDefined(); - expect(registered_user).toHaveProperty("email", user.email); - }); - }); - -}); - -describe("Login endpoint test", () => { - describe("Input Validation", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/login").send(withGodToken(params))); - const BodyValidatorTester = EndpointValidatorTester("body"); - describe("email", () => { - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - describe("password", () => { - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - }); - }); - - describe("Using already resgistered user", () => { - const test_agent = agent(); - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company = { - email: "company@email.com", - password: "password123", - }; - let test_company; - - beforeAll(async () => { - await Account.deleteMany({}); - await Account.create({ email: test_user_admin.email, password: await hash(test_user_admin.password), isAdmin: true }); - test_company = await Company.create({ name: "test comapny" }); - await Account.create({ - email: test_user_company.email, - password: await hash(test_user_admin.password), - company: test_company._id }); - }); - - test("should return an error when registering with an already existing email", async () => { - const res = await request() - .post("/auth/register") - .send(withGodToken(test_user_admin)); - - expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.ALREADY_EXISTS("email"), - "param": "email", - "value": test_user_admin.email, - }); - }); - - test("should return forbidden when retrieving the information of the logged in user", - async () => { - const res = await request() - .get("/auth/me") - .send(); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - } - ); - - test("should unsuccessfully login with registered account (wrong password)", async () => { - const res = await test_agent - .post("/auth/login") - .send({ - email: "user@gmail.com", - password: "password", - }); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - - - test("should successfully login with registered account", async () => { - const res = await test_agent - .post("/auth/login") - .send(test_user_admin); - - // TODO: Reimplement res.should.have.cookie("connect.sid"); - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should return the informations of the logged in user (admin)", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin); - - const res = await test_agent - .get("/auth/me") - .send(); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body).toHaveProperty("data.email", test_user_admin.email); - expect(res.body).toHaveProperty("data.isAdmin", true); - expect(res.body).not.toHaveProperty("data.company"); - }); - - test("should return the informations of the logged in user (company)", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company); - - const res = await test_agent - .get("/auth/me") - .send(); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body).toHaveProperty("data.email", test_user_company.email); - expect(res.body).toHaveProperty("data.isAdmin", false); - expect(res.body).toHaveProperty("data.company", expect.objectContaining( - JSON.parse(JSON.stringify(test_company.toObject())) // Necessary since mongoose objects don't play well with jest... - )); - }); - - test("should be successful when loging out the current user", async () => { - const res = await test_agent - .delete("/auth/login") - .send(); - - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should return an error since no user is logged in", async () => { - const res = await test_agent - .get("/auth/me") - .send(); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - }); - - describe("Using logged out user", () => { - const logged_out_agent = agent(); - - test("should return OK since the logout is idempotent", async () => { - const res = await logged_out_agent - .delete("/auth/login") - .send(); - - expect(res.status).toBe(HTTPStatus.OK); - }); - }); -}); - -describe("Password recovery endpoint test", () => { - const test_account = { - email: "recover_email@gmail.com", - password: "password123", - }; - - const newPassword = "new_password_123"; - - beforeEach(async () => { - await Account.deleteMany({ email: test_account.email }); - await Account.create({ - email: test_account.email, - password: await hash(test_account.password), - isAdmin: true, - }); - jest.clearAllMocks(); - }); - - describe("POST /auth/recover/request", () => { - describe("email", () => { - const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/recover/request").send(params)); - const BodyValidatorTester = EndpointValidatorTester("body"); - const FieldValidatorTester = BodyValidatorTester("email"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeEmail(); - }); - - test("should return ok and not send email nor generate a token if account not found", async () => { - const res = await request() - .post("/auth/recover/request") - .send({ email: "not_valid_email@email.com" }); - - expect(EmailService.sendMail).not.toHaveBeenCalled(); - expect(token.generateToken).not.toHaveBeenCalled(); - - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should generate token and send email if account found", async () => { - const res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - expect(res.status).toBe(HTTPStatus.OK); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - - const emailOptions = REQUEST_ACCOUNT_RECOVERY(`${env.password_recovery_link}/${generatedToken}`); - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_account.email, - template: emailOptions.template, - context: emailOptions.context, - })); - - expect(res.status).toBe(HTTPStatus.OK); - }); - }); - - describe("GET /auth/recover/:token/confirm", () => { - test("should fail if invalid token", async () => { - const res = await request() - .get("/auth/recover/token/confirm"); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - }); - - test("should accept if valid token", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(HTTPStatus.OK); - - res = await request() - .get(`/auth/recover/${generatedToken}/confirm`); - - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should fail if valid token expired", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(HTTPStatus.OK); - - - const realTime = Date.now; - const mockDate = Date.now() + (RECOVERY_LINK_EXPIRATION * SECOND_IN_MS); - Date.now = () => mockDate; - - res = await request() - .get(`/auth/recover/${generatedToken}/confirm`); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - - Date.now = realTime; - }); - }); - - describe("POST /auth/recover/:token/confirm", () => { - test("should fail if invalid token", async () => { - const res = await request() - .post("/auth/recover/token/confirm") - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - }); - - test("should accept if valid token", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(HTTPStatus.OK); - - res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.OK); - }); - - test("should fail if valid token expired", async () => { - let res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - const generatedToken = generateTokenSpy.mock.results[0].value; - expect(res.status).toBe(HTTPStatus.OK); - - - const realTime = Date.now; - const mockDate = Date.now() + (RECOVERY_LINK_EXPIRATION * SECOND_IN_MS); - Date.now = () => mockDate; - - res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INVALID_TOKEN); - - Date.now = realTime; - }); - - describe("password", () => { - let generatedToken; - - beforeAll(async () => { - await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); - - generatedToken = generateTokenSpy.mock.results[0].value; - }); - - const EndpointValidatorTester = - ValidatorTester((params) => request().post(`/auth/recover/${generatedToken}/confirm`).send(params)); - const BodyValidatorTester = EndpointValidatorTester("body"); - const FieldValidatorTester = BodyValidatorTester("password"); - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeString(); - FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); - FieldValidatorTester.hasNumber(); - }); - - test("should succeed to complete the whole password recovery process", async () => { - const test_agent = agent(); - let res = await test_agent - .post("/auth/login") - .send(test_account); - - expect(res.status).toBe(HTTPStatus.OK); - - await test_agent.delete("/auth/login"); - - res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(res.status).toBe(HTTPStatus.OK); - const generatedToken = generateTokenSpy.mock.results[0].value; - - res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.OK); - - res = await test_agent - .post("/auth/login") - .send({ email: test_account.email, password: newPassword }); - - expect(res.status).toBe(HTTPStatus.OK); - - }); - - test("should change password in database after whole password recovery process", async () => { - const oldPassword = (await Account.findOne({ email: test_account.email })).password; - - const test_agent = agent(); - let res = await test_agent - .post("/auth/login") - .send(test_account); - - expect(res.status).toBe(HTTPStatus.OK); - - await test_agent.delete("/auth/login"); - - res = await request() - .post("/auth/recover/request") - .send({ email: test_account.email }); - - expect(res.status).toBe(HTTPStatus.OK); - const generatedToken = generateTokenSpy.mock.results[0].value; - - res = await request() - .post(`/auth/recover/${generatedToken}/confirm`) - .send({ password: newPassword }); - - expect(res.status).toBe(HTTPStatus.OK); - - const password = (await Account.findOne({ email: test_account.email })).password; - - expect(password).not.toBe(oldPassword); - - }); - }); - -}); diff --git a/test/end-to-end/auth/login.js b/test/end-to-end/auth/login.js new file mode 100755 index 00000000..2251e887 --- /dev/null +++ b/test/end-to-end/auth/login.js @@ -0,0 +1,118 @@ +import { StatusCodes } from "http-status-codes"; +import Account from "../../../src/models/Account"; +import Company from "../../../src/models/Company"; +import ValidatorTester from "../../utils/ValidatorTester"; +import hash from "../../../src/lib/passwordHashing"; + +describe("POST /auth/login", () => { + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + const test_user_company = { + email: "company@email.com", + password: "password123", + }; + + beforeAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + const test_company = await Company.create({ name: "test company" }); + + await Account.create({ + email: test_user_company.email, + password: await hash(test_user_company.password), + company: test_company._id + }); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + describe("Input Validation", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/login").send(params)); + const BodyValidatorTester = EndpointValidatorTester("body"); + + describe("email", () => { + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + + describe("password", () => { + const FieldValidatorTester = BodyValidatorTester("password"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + }); + }); + + test("should fail to login if password is wrong", async () => { + await test_agent + .post("/auth/login") + .send({ + email: "user@gmail.com", + password: "password", + }) + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("should successfully login with registered account", async () => { + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + }); +}); + +describe("DELETE /auth/login", () => { + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + + beforeAll(async () => { + await Account.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + }); + + afterAll(async () => { + await Account.deleteMany({}); + }); + + test("should return OK since the logout is idempotent", async () => { + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + test("should be successful when logging out the current user", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); +}); diff --git a/test/end-to-end/auth/me.js b/test/end-to-end/auth/me.js new file mode 100755 index 00000000..24e6977d --- /dev/null +++ b/test/end-to-end/auth/me.js @@ -0,0 +1,98 @@ +import { StatusCodes } from "http-status-codes"; +import Account from "../../../src/models/Account"; +import Company from "../../../src/models/Company"; +import hash from "../../../src/lib/passwordHashing"; + +describe("GET /auth/me", () => { + + const test_agent = agent(); + + const test_user_admin = { + email: "admin@email.com", + password: "password123" + }; + + const test_user_company = { + email: "company@email.com", + password: "password123", + }; + + let test_company; + + beforeAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + test_company = await Company.create({ name: "test company" }); + + await Account.create({ + email: test_user_company.email, + password: await hash(test_user_company.password), + company: test_company._id + }); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + + afterEach(async () => { + await test_agent + .delete("/auth/login") + .send() + .expect(StatusCodes.OK); + }); + + afterAll(async () => { + await Account.deleteMany({}); + await Company.deleteMany({}); + }); + + test("should return the information of the logged in user (admin)", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin); + + const res = await test_agent + .get("/auth/me") + .send(); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body).toHaveProperty("data.email", test_user_admin.email); + expect(res.body).toHaveProperty("data.isAdmin", true); + expect(res.body).not.toHaveProperty("data.company"); + }); + + test("should return the information of the logged in user (company)", async () => { + await test_agent + .post("/auth/login") + .send(test_user_company); + + const res = await test_agent + .get("/auth/me") + .send(); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body).toHaveProperty("data.email", test_user_company.email); + expect(res.body).toHaveProperty("data.isAdmin", false); + expect(res.body).toHaveProperty("data.company", expect.objectContaining( + JSON.parse(JSON.stringify(test_company.toObject()) // Necessary since mongoose objects don't play well with jest... + ))); + }); + + test("should return an error since no user is logged in", async () => { + await test_agent + .get("/auth/me") + .send() + .expect(StatusCodes.UNAUTHORIZED); + }); +}); diff --git a/test/end-to-end/auth/recover/request.js b/test/end-to-end/auth/recover/request.js new file mode 100755 index 00000000..23341fe5 --- /dev/null +++ b/test/end-to-end/auth/recover/request.js @@ -0,0 +1,76 @@ +import { StatusCodes } from "http-status-codes"; +import ValidatorTester from "../../../utils/ValidatorTester"; +import { RECOVERY_LINK_EXPIRATION } from "../../../../src/models/constants/Account"; +import EmailService from "../../../../src/lib/emailService"; +import * as token from "../../../../src/lib/token"; +import { REQUEST_ACCOUNT_RECOVERY } from "../../../../src/email-templates/accountManagement"; +import env from "../../../../src/config/env"; +import Account from "../../../../src/models/Account"; +import hash from "../../../../src/lib/passwordHashing"; + +const generateTokenSpy = jest.spyOn(token, "generateToken"); +jest.spyOn(token, "verifyAndDecodeToken"); + +describe("POST /recover/request", () => { + + const test_account = { + email: "recover_email@gmail.com", + password: "password123", + }; + + beforeEach(async () => { + await Account.deleteMany({ email: test_account.email }); + + await Account.create({ + email: test_account.email, + password: await hash(test_account.password), + isAdmin: true, + }); + + jest.clearAllMocks(); + }); + + afterAll(async () => { + await Account.deleteMany({}); + }); + + describe("Input Validation", () => { + describe("email", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/recover/request").send(params)); + const BodyValidatorTester = EndpointValidatorTester("body"); + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + }); + + test("should return ok and not send email nor generate a token if account not found", async () => { + const res = await request() + .post("/auth/recover/request") + .send({ email: "not_valid_email@email.com" }); + + expect(EmailService.sendMail).not.toHaveBeenCalled(); + expect(token.generateToken).not.toHaveBeenCalled(); + + expect(res.status).toBe(StatusCodes.OK); + }); + + test("should generate token and send email if account found", async () => { + await request() + .post("/auth/recover/request") + .send({ email: test_account.email }) + .expect(StatusCodes.OK); + + expect(token.generateToken).toHaveBeenCalledWith({ email: test_account.email }, env.jwt_secret, RECOVERY_LINK_EXPIRATION); + + const generatedToken = generateTokenSpy.mock.results[0].value; + + const emailOptions = REQUEST_ACCOUNT_RECOVERY(`${env.password_recovery_link}/${generatedToken}`); + expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + subject: emailOptions.subject, + to: test_account.email, + template: emailOptions.template, + context: emailOptions.context, + })); + }); +}); diff --git a/test/end-to-end/auth/register.js b/test/end-to-end/auth/register.js new file mode 100755 index 00000000..5e470e4d --- /dev/null +++ b/test/end-to-end/auth/register.js @@ -0,0 +1,91 @@ +import { StatusCodes } from "http-status-codes"; +import Account from "../../../src/models/Account"; +import ValidatorTester from "../../utils/ValidatorTester"; +import withGodToken from "../../utils/GodToken"; +import AccountConstants from "../../../src/models/constants/Account"; +import { ErrorTypes } from "../../../src/api/middleware/errorHandler"; +import ValidationReasons from "../../../src/api/middleware/validators/validationReasons"; + +describe("POST /auth/register", () => { + + describe("Input Validation", () => { + const EndpointValidatorTester = ValidatorTester((params) => request().post("/auth/register").send(withGodToken(params))); + const BodyValidatorTester = EndpointValidatorTester("body"); + + describe("email", () => { + const FieldValidatorTester = BodyValidatorTester("email"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeEmail(); + }); + + describe("password", () => { + const FieldValidatorTester = BodyValidatorTester("password"); + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeString(); + FieldValidatorTester.hasMinLength(AccountConstants.password.min_length); + FieldValidatorTester.hasNumber(); + }); + }); + + describe("Without pre-existing users", () => { + beforeAll(async () => { + await Account.deleteMany({}); + }); + + afterAll(async () => { + await Account.deleteMany({}); + }); + + test("Should return forbidden if attempting to register an account while not providing the god token", async () => { + const user = { + email: "user@email.com", + password: "password123", + }; + + const res = await request() + .post("/auth/register") + .send(user); + + expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + }); + + test("Should make a successful registration if god token given and email not in use", async () => { + const user = { + email: "user@email.com", + password: "password123", + }; + + const res = await request() + .post("/auth/register") + .send(withGodToken(user)); + + expect(res.status).toBe(StatusCodes.OK); + + const registered_user = await Account.findOne({ email: user.email }); + expect(registered_user).toBeDefined(); + expect(registered_user).toHaveProperty("email", user.email); + }); + + test("should return an error when registering with an already existing email", async () => { + + const test_user = { + email: "user@email.com", + password: "password123", + }; + + const res = await request() + .post("/auth/register") + .send(withGodToken(test_user)); + + expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); + expect(res.body).toHaveProperty("errors"); + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.ALREADY_EXISTS("email"), + "param": "email", + "value": test_user.email, + }); + }); + }); +}); diff --git a/test/end-to-end/company.js b/test/end-to-end/company.js deleted file mode 100644 index 3bad03e0..00000000 --- a/test/end-to-end/company.js +++ /dev/null @@ -1,2819 +0,0 @@ -import config from "../../src/config/env"; -import { StatusCodes as HTTPStatus } from "http-status-codes"; -import Account from "../../src/models/Account"; -import Company from "../../src/models/Company"; -import Offer from "../../src/models/Offer"; -import hash from "../../src/lib/passwordHashing"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; -import CompanyConstants from "../../src/models/constants/Company"; -import OfferConstants from "../../src/models/constants/Offer"; -import withGodToken from "../utils/GodToken"; -import { DAY_TO_MS } from "../utils/TimeConstants"; -import fs from "fs"; -import path from "path"; -import { ErrorTypes } from "../../src/api/middleware/errorHandler"; -import EmailService from "../../src/lib/emailService"; -import { COMPANY_UNBLOCKED_NOTIFICATION, - COMPANY_BLOCKED_NOTIFICATION, - COMPANY_ENABLED_NOTIFICATION, - COMPANY_DISABLED_NOTIFICATION, - COMPANY_DELETED_NOTIFICATION } from "../../src/email-templates/companyManagement"; -import { MAX_FILE_SIZE_MB } from "../../src/api/middleware/utils"; -import { fileURLToPath } from "url"; - -const getCompanies = async (options) => - [...(await Company.find(options) - .sort({ name: "asc" }) // sort them to match what gets returned by the service - .exec())] - .map((company) => ({ - ...company.toObject(), - _id: company._id.toString(), - })); - -describe("Company endpoint", () => { - - const generateTestOffer = (params) => ({ - title: "Test Offer", - publishDate: (new Date()).toISOString(), - publishEndDate: (new Date(Date.now() + (DAY_TO_MS))).toISOString(), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - isHidden: false, - requirements: ["The candidate must be tested", "Fluent in testJS"], - ...params, - }); - - const generateTestCompany = (params) => ({ - name: "Big Company", - bio: "Big Company Bio", - logo: "http://awebsite.com/alogo.jpg", - contacts: ["112", "122"], - hasFinishedRegistration: true, - ...params, - }); - - - describe("GET /company", () => { - beforeAll(async () => { - await Company.deleteMany({}); - }); - - test("should return an empty array", async () => { - const res = await request() - .get("/company"); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual([]); - expect(res.body.totalDocCount).toEqual(0); - }); - - test("should return the newly created company", async () => { - await Company.create({ name: "Company" }); - const res = await request() - .get("/company"); - const companies = await getCompanies({}); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(1); - }); - - test("should not return blocked created company if not logged in", async () => { - await Company.deleteMany({}); - await Company.create({ name: "Company", isBlocked: true }); - const res = await request() - .get("/company"); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual([]); - expect(res.body.totalDocCount).toEqual(0); - }); - - - describe("With Auth", () => { - const test_agent = agent(); - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - beforeEach(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - }); - - test("should return blocked created company logged in as admin", async () => { - await Company.create({ name: "Company", isBlocked: true }); - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(200); - - const res = await test_agent - .get("/company"); - const companies = await getCompanies({}); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(1); - }); - - }); - - describe("Disabled companies", () => { - let test_company, disabled_test_company; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company = { - email: "company@email.com", - password: "password123", - }; - - const test_agent = agent(); - - beforeAll(async () => { - - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - test_company = { - name: "test-company" - }; - - disabled_test_company = { - name: "disabled-test-company", - isDisabled: true - }; - - const companies = await Company.create([test_company, disabled_test_company]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company.email, - password: await hash(test_user_company.password), - company: companies[0]._id - }); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - }); - - test("should return both companies if god token is sent", async () => { - - const res = await test_agent - .get("/company") - .send(withGodToken()); - - const companies = await getCompanies({}); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(2); - - }); - - test("should return both companies if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get("/company"); - - const companies = await getCompanies({}); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(2); - - }); - - test("should return only the enabled company if logged as unprivileged user", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get("/company"); - - const companies = await getCompanies({ isDisabled: { $ne: true } }); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(1); - }); - - test("should return only the enabled company if not logged", async () => { - - const res = await test_agent - .get("/company"); - - const companies = await getCompanies({ isDisabled: { $ne: true } }); - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.companies).toEqual(companies); - expect(res.body.totalDocCount).toEqual(1); - }); - }); - }); - - describe("GET /company/:companyId", () => { - let test_company_without_offers, - test_company_with_offers_below_limit, - test_company_with_offers_at_limit, - test_company_with_offers_above_limit, - test_company_with_hidden_offer, - test_offer_hidden, - test_company_disabled, - test_company_blocked, - test_company_with_unfinished_registration; - - const test_user_with_unfinished_registration = { - email: "unfinished@email.com", - password: "password123", - }; - const test_user_with_hidden_offer = { - email: "hidden@email.com", - password: "password123", - }; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - isAdmin: true, - }; - const test_user_disabled_company = { - email: "disabled@email.com", - password: "password123", - }; - const test_user_blocked_company = { - email: "blocked@email.com", - password: "password123", - }; - const test_company_data = { - name: "test-company", - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg", - }; - - const test_agent = agent(); - - const createTestOffers = (length, company) => - Promise.all( - Array.from({ length }, () => - Offer.create( - generateTestOffer({ - publishDate: new Date( - Date.now() - DAY_TO_MS - ).toISOString(), - publishEndDate: new Date( - Date.now() + DAY_TO_MS - ).toISOString(), - owner: company._id, - ownerName: company.name, - ownerLogo: company.logo, - }) - ) - ) - ); - - beforeAll(async () => { - await Company.deleteMany({}); - - [ - test_company_without_offers, - test_company_with_offers_below_limit, - test_company_with_offers_at_limit, - test_company_with_offers_above_limit, - test_company_with_hidden_offer, - test_company_disabled, - test_company_blocked, - test_company_with_unfinished_registration, - ] = await Company.create([ - test_company_data, - test_company_data, - test_company_data, - test_company_data, - test_company_data, - { ...test_company_data, isDisabled: true }, - { ...test_company_data, isBlocked: true }, - { ...test_company_data, hasFinishedRegistration: false }, - ]); - - await Account.deleteMany({}); - - await Promise.all([ - Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true, - }), - Account.create({ - email: test_user_with_unfinished_registration.email, - password: await hash( - test_user_with_unfinished_registration.password - ), - company: test_company_with_unfinished_registration._id, - }), - Account.create({ - email: test_user_with_hidden_offer.email, - password: await hash(test_user_with_hidden_offer.password), - company: test_company_with_hidden_offer._id, - }), - Account.create({ - email: test_user_disabled_company.email, - password: await hash(test_user_disabled_company.password), - company: test_company_disabled._id, - }), - Account.create({ - email: test_user_blocked_company.email, - password: await hash(test_user_blocked_company.password), - company: test_company_blocked._id, - }), - ]); - - test_offer_hidden = await Offer.create( - generateTestOffer({ - isHidden: true, - owner: test_company_with_hidden_offer._id.toString(), - ownerName: test_company_with_hidden_offer.name, - ownerLogo: test_company_with_hidden_offer.logo, - }) - ); - }); - - afterEach(async () => { - await test_agent.delete("/auth/login").expect(HTTPStatus.OK); - }); - - describe("Without Auth", () => { - test("should fail if invalid id", async () => { - const res = await test_agent - .get("/company/123") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.OBJECT_ID, - }), - ]) - ); - }); - - test("should fail if company does not exist", async () => { - const id = "111111111111111111111111"; - const res = await test_agent - .get(`/company/${id}`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND(id), - }), - ]) - ); - }); - - test("should succeed when the company has no offers", async () => { - const res = await test_agent - .get(`/company/${test_company_without_offers.id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", []); - expect(res.body).toHaveProperty( - "company._id", - test_company_without_offers._id.toString() - ); - }); - - test("should return all offers when below limit", async () => { - const offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible - 1, - test_company_with_offers_below_limit - ); - - const res = await test_agent - .get(`/company/${test_company_with_offers_below_limit._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - 1 - ); - expect(res.body.offers.map((x) => x._id).sort()).toEqual( - offers.map((x) => x._id.toString()).sort() - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_below_limit._id.toString() - ); - }); - - test("should return all offers when at limit", async () => { - const offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible, - test_company_with_offers_at_limit - ); - - const res = await test_agent - .get(`/company/${test_company_with_offers_at_limit._id}`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - ); - expect(res.body.offers.map((x) => x._id).sort()).toEqual( - offers.map((x) => x._id.toString()).sort() - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_at_limit._id.toString() - ); - }); - - test("should limit number of offers", async () => { - const offers = await createTestOffers( - CompanyConstants.offers.max_profile_visible + 10, - test_company_with_offers_above_limit - ); - - const res = await test_agent - .get(`/company/${test_company_with_offers_above_limit._id}`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("offers"); - expect(res.body.offers).toHaveLength( - CompanyConstants.offers.max_profile_visible - ); - expect(offers.map((x) => x._id.toString())).toEqual( - expect.arrayContaining(res.body.offers.map((x) => x._id)) - ); - - expect(res.body).toHaveProperty( - "company._id", - test_company_with_offers_above_limit._id.toString() - ); - }); - - test("should not return hidden offers", async () => { - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", []); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should fail if company is disabled", async () => { - const res = await test_agent - .get(`/company/${test_company_disabled._id}`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_company_disabled._id - ), - }), - ]) - ); - }); - - test("should fail if company is blocked", async () => { - const res = await test_agent - .get(`/company/${test_company_blocked._id}`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_company_blocked._id - ), - }), - ]) - ); - }); - - test("should fail if company hasn't finished registration", async () => { - const res = await test_agent - .get( - `/company/${test_company_with_unfinished_registration._id}` - ) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.VALIDATION_ERROR - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - param: "companyId", - msg: ValidationReasons.COMPANY_NOT_FOUND( - test_company_with_unfinished_registration._id - ), - }), - ]) - ); - }); - }); - - describe("With Auth", () => { - test("should return hidden offers when user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_with_hidden_offer) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", [ - expect.objectContaining({ - _id: test_offer_hidden._id.toString(), - }), - ]); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should succeed if company is disabled and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_disabled_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_disabled._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_disabled._id.toString() - ); - }); - - test("should fail if company is blocked and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_blocked_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_blocked._id}`) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.COMPANY_BLOCKED, - }), - ]) - ); - }); - - test("should fail if company hasn't finished registration and user is owner", async () => { - await test_agent - .post("/auth/login") - .send(test_user_with_unfinished_registration) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get( - `/company/${test_company_with_unfinished_registration._id}` - ) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - - test("should return hidden offers when user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", [ - expect.objectContaining({ - _id: test_offer_hidden._id.toString(), - }), - ]); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should succeed if company is disabled and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_disabled._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_disabled._id.toString() - ); - }); - - test("should succeed if company is blocked and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_blocked._id}`) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_blocked._id.toString() - ); - }); - - test("should fail if company hasn't finished registration and user is admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get( - `/company/${test_company_with_unfinished_registration._id}` - ) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - - test("should return hidden offers when user is god", async () => { - const res = await test_agent - .get(`/company/${test_company_with_hidden_offer._id}`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("offers", [ - expect.objectContaining({ - _id: test_offer_hidden._id.toString(), - }), - ]); - expect(res.body).toHaveProperty( - "company._id", - test_company_with_hidden_offer._id.toString() - ); - }); - - test("should succeed if company is disabled and user is god", async () => { - const res = await test_agent - .get(`/company/${test_company_disabled._id}`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_disabled._id.toString() - ); - }); - - test("should succeed if company is blocked and user is god", async () => { - const res = await test_agent - .get(`/company/${test_company_blocked._id}`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty( - "company._id", - test_company_blocked._id.toString() - ); - }); - - test("should fail if company hasn't finished registration and user is god", async () => { - const res = await test_agent - .get( - `/company/${test_company_with_unfinished_registration._id}` - ) - .send(withGodToken()) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty( - "error_code", - ErrorTypes.FORBIDDEN - ); - expect(res.body).toHaveProperty( - "errors", - expect.arrayContaining([ - expect.objectContaining({ - msg: ValidationReasons.REGISTRATION_NOT_FINISHED, - }), - ]) - ); - }); - }); - }); - - describe("POST /company/application/finish", () => { - - describe("Without Auth", () => { - test("should respond with forbidden", async () => { - const emptyRes = await request() - .post("/company/application/finish"); - - expect(emptyRes.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - }); - - describe("With Auth", () => { - const test_agent = agent(); - const test_user = { - email: "user@email.com", - password: "password123", - }; - - const company_data = { - name: "Company Ltd", - }; - - beforeEach(async () => { - await Company.deleteMany({}); - const test_company = await Company.create({ name: company_data.name }); - await Account.deleteMany({}); - await Account.create({ email: test_user.email, password: await hash(test_user.password), company: test_company._id }); - - // Login - await test_agent - .post("/auth/login") - .send(test_user) - .expect(200); - }); - - test("should finish the application", async () => { - await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", ["contact1", "contact2"]) - .expect(HTTPStatus.OK); - - const test_company = [... await Company.find({})][0]; - expect([...test_company.contacts]).toEqual(["contact1", "contact2"]); - expect(test_company.hasFinishedRegistration).toBe(true); - expect(test_company.bio).toBe("A very interesting and compelling bio"); - const filename = path.join(`${config.upload_folder}/${test_company.id}.png`); - expect(fs.existsSync(filename)).toBe(true); - - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", ["contact1", "contact2"]) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body.errors).toContainEqual( - { msg: ValidationReasons.REGISTRATION_FINISHED } - ); - - // clean up file created - fs.unlinkSync(filename); - }); - - test("should finish the application with single contact", async () => { - await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", "contact1") - .expect(HTTPStatus.OK); - - const test_company = [... await Company.find({})][0]; - expect([...test_company.contacts]).toEqual(["contact1"]); - expect(test_company.hasFinishedRegistration).toBe(true); - expect(test_company.bio).toBe("A very interesting and compelling bio"); - const filename = path.join(`${config.upload_folder}/${test_company.id}.png`); - expect(fs.existsSync(filename)).toBe(true); - - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", "A very interesting and compelling bio") - .field("contacts", "contact2") - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body.errors).toContainEqual( - { msg: ValidationReasons.REGISTRATION_FINISHED } - ); - - // clean up file created - fs.unlinkSync(filename); - }); - - describe("logo", () => { - - test("should return error when the logo is missing", async () => { - const res = await test_agent - .post("/company/application/finish") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.REQUIRED, - "param": "logo", - }); - }); - - test("should return error when the logo is missing", async () => { - const res = await test_agent - .post("/company/application/finish") - .attach("logo", fileURLToPath(import.meta.url)) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.IMAGE_FORMAT, - "param": "logo", - }); - }); - - test("should return an error when the image size is greater than the max size", async () => { - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup-10mb.png") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.FILE_TOO_LARGE(MAX_FILE_SIZE_MB), - "param": "logo", - }); - }); - - }); - - describe("bio", () => { - test("should return an error because the bio is required", async () => { - - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.REQUIRED, - "param": "bio", - }); - - }); - - - test("should return an error because the bio is too long", async () => { - const long_bio = "a".repeat(CompanyConstants.bio.max_length + 1); - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("bio", long_bio) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.TOO_LONG(CompanyConstants.bio.max_length), - "param": "bio", - "value": long_bio - }); - - }); - }); - - describe("contacts", () => { - - test("should return an error because the contacts are required", async () => { - - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.REQUIRED, - "param": "contacts", - }); - - }); - - - test("should return an error because the contacts is too long", async () => { - const contacts = new Array(CompanyConstants.contacts.max_length + 1) - .fill("contact"); - const res = await test_agent - .post("/company/application/finish") - .attach("logo", "test/data/logo-niaefeup.png") - .field("contacts", contacts) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.ARRAY_SIZE(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length), - "param": "contacts", - "value": contacts - }); - - }); - }); - - - }); - }); - - describe("PUT /company/:companyId/block", () => { - const test_agent = agent(); - - const company_data = { - name: "Company Ltd" - }; - - const test_users = Array(4).fill({}).map((_c, idx) => ({ - email: `test_email_${idx}@email.com`, - password: "password123", - })); - - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - const adminReason = "An admin reason!"; - - let test_company_1, test_company_2, blocked_test_company_2, test_email_company; - - beforeAll(async () => { - await Company.deleteMany({}); - test_company_1 = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - test_email_company = await Company.create({ name: company_data.name, hasFinishedRegistration: true }); - blocked_test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); - await Account.deleteMany({}); - [test_email_company, test_company_1, test_company_2, blocked_test_company_2] - .forEach(async (company, idx) => { - await Account.create({ - email: test_users[idx].email, - password: await hash(test_users[idx].password), - company: company._id - }); - }); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - }); - - - test("should fail if not logged in", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(HTTPStatus.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("should fail if logged in as company", async () => { - await test_agent - .post("/auth/login") - .send(test_users[1]) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(HTTPStatus.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - - test("should fail if admin reason not provided", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "adminReason"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.REQUIRED); - }); - - test("should allow if logged in as admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/block`) - .send({ adminReason }) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - }); - - test("should fail if not a valid id", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put("/company/123/block") - .send({ adminReason }) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("should fail if company does not exist", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/block`) - .send({ adminReason }) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - - test("should allow with god token", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company_2.id}/block`) - .send(withGodToken({ adminReason })) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - }); - - test("should send an email to the company user when it is blocked", async () => { - await test_agent - .del("/auth/login"); - await test_agent - .put(`/company/${test_email_company._id}/block`) - .send(withGodToken({ adminReason })) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_BLOCKED_NOTIFICATION( - test_email_company.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_users[0].email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - - describe("testing with offers", () => { - let test_company; - - beforeEach(async () => { - const company = { - email: "test_company_email_@email.com", - password: "password123", - }; - - await Company.deleteMany({}); - test_company = await Company.create({ - name: company_data.name, - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg" - }); - - - await Account.deleteMany({}); - await Account.create({ - email: company.email, - password: await hash(company.password), - company: test_company._id }); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - }); - - test("should block active offers", async () => { - - const offers = Array(3).fill(await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo - })); - - const res = await test_agent - .put(`/company/${test_company.id}/block`) - .send({ adminReason }) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - - - for (const offer of offers) { - const updated_offer = await Offer.findById(offer._id); - - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED); - expect(updated_offer).toHaveProperty("isHidden", true); - } - }); - - test("should not override offers already hidden", async () => { - - const offer = await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK - }); - - const res = await test_agent - .put(`/company/${test_company.id}/block`) - .send({ adminReason }) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", true); - expect(res.body).toHaveProperty("adminReason", adminReason); - - - const updated_offer = await Offer.findById(offer._id); - - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.ADMIN_BLOCK); - expect(updated_offer).toHaveProperty("isHidden", true); - - }); - }); - }); - - describe("PUT /company/:companyId/unblock", () => { - const test_agent = agent(); - - const company_data = { - name: "Company Ltd" - }; - const test_user_1 = { - email: "user1@email.com", - password: "password123", - }; - const test_user_2 = { - email: "user2@email.com", - password: "password123", - }; - const test_user_email = { - email: "test_email@email.com", - password: "password123", - }; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - - let test_company_1, test_company_2, test_company_email; - - beforeAll(async () => { - await Company.deleteMany({}); - test_company_1 = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); - test_company_2 = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); - test_company_email = await Company.create({ name: company_data.name, hasFinishedRegistration: true, isBlocked: true }); - await Account.deleteMany({}); - await Account.create({ email: test_user_1.email, password: await hash(test_user_1.password), company: test_company_1._id }); - await Account.create({ email: test_user_2.email, password: await hash(test_user_2.password), company: test_company_2._id }); - await Account.create({ - email: test_user_email.email, - password: await hash(test_user_email.password), - company: test_company_email._id - }); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - }); - - - test("should fail if not logged in", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company_1.id}/unblock`) - .expect(HTTPStatus.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("should fail if logged in as company", async () => { - await test_agent - .post("/auth/login") - .send(test_user_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/unblock`) - .expect(HTTPStatus.UNAUTHORIZED); - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("should allow if logged in as admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1.id}/unblock`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - expect(res.body).not.toHaveProperty("adminReason"); - }); - - test("should fail if not a valid id", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put("/company/123/unblock") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("should fail if company does not exist", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/unblock`) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - - test("should allow with god token", async () => { - await test_agent - .del("/auth/login"); - - const res = await test_agent - .put(`/company/${test_company_2.id}/unblock`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - }); - - test("should send an email to the company user when it is unblocked", async () => { - await test_agent - .del("/auth/login"); - await test_agent - .put(`/company/${test_company_email._id}/unblock`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_UNBLOCKED_NOTIFICATION( - test_company_email.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_email.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - - - describe("testing with offers", () => { - let test_company; - - beforeEach(async () => { - const company = { - email: "test_company_email_@email.com", - password: "password123", - }; - - await Company.deleteMany({}); - test_company = await Company.create({ - name: company_data.name, - hasFinishedRegistration: true, - logo: "http://awebsite.com/alogo.jpg" - }); - - - await Account.deleteMany({}); - await Account.create({ - email: company.email, - password: await hash(company.password), - company: test_company._id }); - - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - }); - - test("should unblock offers blocked by company block", async () => { - - const offers = Array(3).fill(await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_BLOCKED - })); - - const res = await test_agent - .put(`/company/${test_company.id}/unblock`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - - - for (const offer of offers) { - expect(await Offer.findById(offer._id)).toHaveProperty("isHidden", false); - } - }); - - test("should not unblock offers hidden by company request", async () => { - - const offer = await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_REQUEST - }); - - const res = await test_agent - .put(`/company/${test_company.id}/unblock`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - - - const updated_offer = await Offer.findById(offer._id); - - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.COMPANY_REQUEST); - expect(updated_offer).toHaveProperty("isHidden", true); - - }); - - test("should not unblock offers hidden by admin request", async () => { - - const offer = await Offer.create({ - ...generateTestOffer({ - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - }), - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.ADMIN_BLOCK - }); - - const res = await test_agent - .put(`/company/${test_company.id}/unblock`) - .expect(HTTPStatus.OK); - expect(res.body).toHaveProperty("isBlocked", false); - - - const updated_offer = await Offer.findById(offer._id); - - expect(updated_offer).toHaveProperty("hiddenReason", OfferConstants.HiddenOfferReasons.ADMIN_BLOCK); - expect(updated_offer).toHaveProperty("isHidden", true); - - }); - - }); - - - }); - - describe("PUT /company/enable", () => { - - let disabled_test_company_1, disabled_test_company_2, disabled_test_company_3, disabled_test_company_4, disabled_test_company_mail; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - const test_user_company_3 = { - email: "company3@email.com", - password: "password123", - }; - const test_user_company_4 = { - email: "company4@email.com", - password: "password123", - }; - const test_user_mail = { - email: "company_mail@email.com", - password: "password123", - }; - - const test_agent = agent(); - - beforeAll(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - [ - disabled_test_company_1, - disabled_test_company_2, - disabled_test_company_3, - disabled_test_company_4, - disabled_test_company_mail - ] = await Company.create([ - { - name: "disabled-test-company-1", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-2", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-3", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-4", - logo: "http://awebsite.com/alogo.jpg", - isDisabled: true, - hasFinishedRegistration: true - }, { - name: "disabled-test-company-mail", - isDisabled: true, - hasFinishedRegistration: true - } - ]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: disabled_test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: disabled_test_company_2._id - }); - await Account.create({ - email: test_user_company_3.email, - password: await hash(test_user_company_3.password), - company: disabled_test_company_3._id - }); - await Account.create({ - email: test_user_company_4.email, - password: await hash(test_user_company_4.password), - company: disabled_test_company_4._id - }); - await Account.create({ - email: test_user_mail.email, - password: await hash(test_user_mail.password), - company: disabled_test_company_mail._id - }); - - const offer = { - title: "Test Offer", - publishDate: new Date(Date.now()), - publishEndDate: new Date(Date.now() + (DAY_TO_MS)), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 1, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - requirements: ["The candidate must be tested", "Fluent in testJS"], - owner: disabled_test_company_4._id, - ownerName: disabled_test_company_4.name, - ownerLogo: disabled_test_company_4.logo, - isHidden: true, - hiddenReason: OfferConstants.HiddenOfferReasons.COMPANY_DISABLED, - }; - - await Offer.create([offer, offer]); - - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using invalid id", async () => { - - const res = await test_agent - .put("/company/123/enable") - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/enable`) - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - }); - - test("should fail to enable if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); - - }); - - test("should fail to enable if not logged", async () => { - - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - - }); - - test("Should enable company if god token is sent", async () => { - - const res = await test_agent - .put(`/company/${disabled_test_company_3._id}/enable`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(false); - }); - - test("Should enable company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_2._id}/enable`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(false); - }); - - test("Should enable company if logged as same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${disabled_test_company_1._id}/enable`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(false); - }); - - test("should change offers' 'isHidden' on company enable", async () => { - - const offersBefore = await Offer.find({ owner: disabled_test_company_4._id }); - - expect(offersBefore.every(({ isHidden }) => isHidden === true)).toBe(true); - expect(offersBefore.every( - ({ hiddenReason }) => hiddenReason === OfferConstants.HiddenOfferReasons.COMPANY_DISABLED) - ).toBe(true); - - const res = await test_agent - .put(`/company/${disabled_test_company_4._id}/enable`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(false); - - const offersAfter = await Offer.find({ owner: disabled_test_company_4._id }); - - expect(offersAfter.every(({ isHidden }) => isHidden === false)).toBe(true); - expect(offersAfter.every(({ hiddenReason }) => hiddenReason === undefined)).toBe(true); - }); - - test("should send an email to the company user when it is enabled", async () => { - await test_agent - .put(`/company/${disabled_test_company_mail._id}/enable`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_ENABLED_NOTIFICATION( - disabled_test_company_mail.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_mail.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - }); - - describe("PUT /company/disable", () => { - - let test_company_1, test_company_2, test_company_3, test_company_mail; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - const test_user_company_3 = { - email: "company3@email.com", - password: "password123", - }; - const test_user_company_mail = { - email: "company_mail@email.com", - password: "password123", - }; - - const test_agent = agent(); - - beforeAll(async () => { - - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - [test_company_1, test_company_2, test_company_3, test_company_mail] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, { - name: "test-company-2", - hasFinishedRegistration: true - }, { - name: "test-company-3", - logo: "http://awebsite.com/alogo.jpg", - hasFinishedRegistration: true - }, { - name: "test-company-mail", - hasFinishedRegistration: true - } - ]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }); - await Account.create({ - email: test_user_company_3.email, - password: await hash(test_user_company_3.password), - company: test_company_3._id - }); - await Account.create({ - email: test_user_company_mail.email, - password: await hash(test_user_company_mail.password), - company: test_company_mail._id - }); - - const offer = { - title: "Test Offer", - publishDate: new Date(Date.now()), - publishEndDate: new Date(Date.now() + (DAY_TO_MS)), - description: "For Testing Purposes", - contacts: ["geral@niaefeup.pt", "229417766"], - jobType: "SUMMER INTERNSHIP", - jobMinDuration: 2, - jobMaxDuration: 6, - fields: ["DEVOPS", "BACKEND", "OTHER"], - technologies: ["React", "CSS"], - location: "Testing Street, Test City, 123", - requirements: ["The candidate must be tested", "Fluent in testJS"], - owner: test_company_3._id, - ownerName: test_company_3.name, - ownerLogo: test_company_3.logo, - }; - - await Offer.create([offer, offer]); - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using invalid id", async () => { - - const res = await test_agent - .put("/company/123/disable") - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .put(`/company/${id}/disable`) - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - }); - - test("should fail to disable company if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); - }); - - test("should fail to disable company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - - test("should fail to disable company if not logged", async () => { - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - - test("Should disable company if god token is sent", async () => { - - const res = await test_agent - .put(`/company/${test_company_2._id}/disable`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(true); - }); - - test("Should disable company if logged as same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_1._id}/disable`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(true); - }); - - test("should change offers' 'isHidden' on company disable", async () => { - - const offersBefore = await Offer.find({ owner: test_company_3._id }); - - expect(offersBefore.every(({ isHidden }) => isHidden === false)).toBe(true); - expect(offersBefore.every(({ hiddenReason }) => hiddenReason === undefined)).toBe(true); - - const res = await test_agent - .put(`/company/${test_company_3._id}/disable`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.isDisabled).toBe(true); - - const offersAfter = await Offer.find({ owner: test_company_3._id }); - - expect(offersAfter.every(({ isHidden }) => isHidden === true)).toBe(true); - expect(offersAfter.every(({ hiddenReason }) => hiddenReason === OfferConstants.HiddenOfferReasons.COMPANY_DISABLED)).toBe(true); - }); - - test("should send an email to the company user when it is disabled", async () => { - await test_agent - .put(`/company/${test_company_mail._id}/disable`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_DISABLED_NOTIFICATION( - test_company_mail.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_company_mail.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - }); - - describe("POST /company/delete", () => { - let test_company_1, test_company_2; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - - const test_agent = agent(); - - beforeEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - [test_company_1, test_company_2] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, { - name: "test-company-2", - hasFinishedRegistration: true, - logo: "http://oniebuedafixe.com/wow.png" - } - ]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }); - - const offer = generateTestOffer({ - owner: test_company_2._id, - ownerName: test_company_2.name, - ownerLogo: test_company_2.logo, - }); - - await Offer.create([offer, offer]); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - await Offer.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using invalid id", async () => { - - const res = await test_agent - .post("/company/123/delete") - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .post(`/company/${id}/delete`) - .send(withGodToken()) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - }); - - test("should fail to delete company if logged as different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`); - - expect(res.status).toBe(HTTPStatus.FORBIDDEN); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); - }); - - test("should fail to delete company if logged as admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - - test("should fail to delete company if not logged", async () => { - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`); - - expect(res.status).toBe(HTTPStatus.UNAUTHORIZED); - expect(res.body.error_code).toBe(ErrorTypes.FORBIDDEN); - expect(res.body.errors).toContainEqual({ msg: ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - - test("Should delete company if god token is sent", async () => { - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(await Company.exists({ _id: test_company_1._id })).toBeNull(); - expect(await Account.exists({ company: test_company_1._id })).toBeNull(); - }); - - test("Should delete company if logged as the same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .post(`/company/${test_company_1._id}/delete`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(await Company.exists({ _id: test_company_1._id })).toBeNull(); - expect(await Account.exists({ company: test_company_1._id })).toBeNull(); - }); - - test("Should delete company's offers when it is deleted", async () => { - const res = await test_agent - .post(`/company/${test_company_2._id}/delete`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - expect(await Company.exists({ _id: test_company_2._id })).toBeNull(); - expect(await Account.exists({ company: test_company_2._id })).toBeNull(); - expect(await Offer.exists({ owner: test_company_2._id })).toBeNull(); - }); - - test("should send an email to the company user when it is deleted", async () => { - await test_agent - .post(`/company/${test_company_1._id}/delete`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - const emailOptions = COMPANY_DELETED_NOTIFICATION( - test_company_1.name - ); - - expect(EmailService.sendMail).toHaveBeenCalledWith(expect.objectContaining({ - subject: emailOptions.subject, - to: test_user_company_1.email, - template: emailOptions.template, - context: emailOptions.context, - })); - }); - }); - - describe("GET /company/:companyId/hasReachedMaxConcurrentOffersBetweenDates", () => { - let test_company_1, test_company_2; - const test_user_admin = { - email: "admin@email.com", - password: "password123", - }; - const test_user_company_1 = { - email: "company1@email.com", - password: "password123", - }; - const test_user_company_2 = { - email: "company2@email.com", - password: "password123", - }; - - const test_agent = agent(); - - const publishDate = (new Date(Date.now())).toISOString(); - const publishEndDate = (new Date(Date.now() + (2 * DAY_TO_MS))).toISOString(); - - beforeEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - - await Company.deleteMany({}); - - [test_company_1, test_company_2] = await Company.create([ - { - name: "test-company-1", - hasFinishedRegistration: true - }, { - name: "test-company-2", - hasFinishedRegistration: true, - logo: "http://oniebuedafixe.com/wow.png" - } - ]); - - await Account.deleteMany({}); - await Account.create({ - email: test_user_admin.email, - password: await hash(test_user_admin.password), - isAdmin: true - }); - await Account.create({ - email: test_user_company_1.email, - password: await hash(test_user_company_1.password), - company: test_company_1._id - }); - await Account.create({ - email: test_user_company_2.email, - password: await hash(test_user_company_2.password), - company: test_company_2._id - }); - - const testOffers = Array(CompanyConstants.offers.max_concurrent) - .fill(generateTestOffer({ - owner: test_company_2._id, - ownerName: test_company_2.name, - ownerLogo: test_company_2.logo, - "publishDate": (new Date(Date.now())).toISOString(), - "publishEndDate": (new Date(Date.now() + (DAY_TO_MS))).toISOString() - })); - - await Offer.deleteMany({}); - await Offer.create(testOffers); - }); - - afterAll(async () => { - await Account.deleteMany({}); - await Company.deleteMany({}); - await Offer.deleteMany({}); - }); - - describe("Id validation", () => { - test("Should fail if using an invalid id", async () => { - - const res = await test_agent - .get("/company/123/hasReachedMaxConcurrentOffersBetweenDates") - .send(withGodToken({ publishDate, publishEndDate })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("Should fail if company does not exist", async () => { - - const id = "111111111111111111111111"; - const res = await test_agent - .get(`/company/${id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "companyId"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(id)); - }); - }); - - describe("Date validation", () => { - test("Should succeed if publishDate is not specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishEndDate })) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if publishEndDate is not specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate })) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if neither publishDate or publishEndDate are specified", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken()) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should fail if publishDate is after publishEndDate", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ - publishDate: publishEndDate, - publishEndDate: publishDate, - })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishEndDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_AFTER("publishDate")); - }); - - test("Should fail if publishDate doesn't have a date format", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate: "123", publishEndDate })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.DATE); - }); - - test("Should fail if publishEndDate doesn't have a date format", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate: "123" })) - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "publishEndDate"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.DATE); - }); - }); - - test("Should fail if not logged in", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.UNAUTHORIZED); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS); - }); - - test("Should fail if logged as a different company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body).toHaveProperty("error_code", ErrorTypes.FORBIDDEN); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS); - }); - - test("Should succeed if god token is sent", async () => { - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send(withGodToken({ publishDate, publishEndDate })) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if logged as an admin", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should succeed if logged as the same company", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_1) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_1._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", false); - }); - - test("Should return true if the company has reached max offers in the time interval", async () => { - - await test_agent - .post("/auth/login") - .send(test_user_company_2) - .expect(HTTPStatus.OK); - - const res = await test_agent - .get(`/company/${test_company_2._id}/hasReachedMaxConcurrentOffersBetweenDates`) - .send({ publishDate, publishEndDate }) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("maxOffersReached", true); - }); - }); - - describe("PUT /company/edit", () => { - let test_companies; - let test_company, test_company_blocked, test_company_disabled; - let test_offer; - - const changing_values = { - name: "Changed name", - bio: "Changed bio", - logo: "test/data/logo-niaefeup.png", - contacts: ["123", "456"], - }; - - /* Admin, Company, Blocked, Disabled*/ - const test_users = Array(4).fill({}).map((_c, idx) => ({ - email: `test_email_${idx}@email.com`, - password: "password123", - })); - - const [test_user_admin, test_user_company, test_user_company_blocked, test_user_company_disabled] = test_users; - - const test_agent = agent(); - - beforeAll(async () => { - await Account.deleteMany({}); - - const test_company_data = await generateTestCompany(); - const test_company_blocked_data = await generateTestCompany({ isBlocked: true }); - const test_company_disabled_data = await generateTestCompany({ isDisabled: true }); - - test_companies = await Company.create( - [test_company_data, test_company_blocked_data, test_company_disabled_data], - { session: null } - ); - - [test_company, test_company_blocked, test_company_disabled] = test_companies; - - test_offer = await Offer.create( - generateTestOffer({ - owner: test_company._id, - ownerName: test_company.name, - ownerLogo: test_company.logo, - }) - ); - - for (let i = 0; i < test_users.length; i++) { - if (i === 0) { // Admin - await Account.create({ - email: test_users[i].email, - password: await hash(test_users[i].password), - isAdmin: true, - }); - } else { // Company - await Account.create({ - email: test_users[i].email, - password: await hash(test_users[i].password), - company: test_companies[i - 1]._id, - }); - } - } - }); - - afterEach(async () => { - await test_agent - .delete("/auth/login") - .expect(HTTPStatus.OK); - }); - - afterAll(async () => { - await Company.deleteMany({}); - await Account.deleteMany({}); - }); - - describe("ID Validation", () => { - beforeEach(async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - }); - - test("Should fail if id is not a valid ObjectID", async () => { - const id = "123"; - const res = await test_agent - .put(`/company/${id}/edit`) - .send() - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual( - { "location": "params", "msg": ValidationReasons.OBJECT_ID, "param": "companyId", "value": id } - ); - }); - - test("Should fail if id is not a valid company", async () => { - const id = "111111111111111111111111"; - - const res = await test_agent - .put(`/company/${id}/edit`) - .send() - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual( - { "location": "params", "msg": ValidationReasons.COMPANY_NOT_FOUND(id), "param": "companyId", "value": id } - ); - }); - }); - - describe("Using a bad user", () => { - test("Should fail if different user", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_blocked) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send({ - name: changing_values.name, - }) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS_COMPANY_SETTINGS }); - }); - - test("Should fail if not logged in", async () => { - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send({ - bio: changing_values.bio, - contacts: changing_values.contacts, - }) - .expect(HTTPStatus.UNAUTHORIZED); - - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.INSUFFICIENT_PERMISSIONS }); - }); - }); - - describe("Using a good user", () => { - test("Should pass if god", async () => { - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send(withGodToken({ - name: changing_values.name, - bio: changing_values.bio, - })) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("name", changing_values.name); - expect(res.body).toHaveProperty("bio", changing_values.bio); - }); - - test("Should pass if admin", async () => { - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .field("name", changing_values.name) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("name", changing_values.name); - }); - - test("Should pass if same company", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .field("name", changing_values.name) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("name", changing_values.name); - }); - }); - - test("Offer should be updated", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .send({ - name: changing_values.name, - contacts: changing_values.contacts, - }) - .expect(HTTPStatus.OK); - - test_offer = await Offer.findById(test_offer._id); - - expect(res.body).toHaveProperty("name", changing_values.name); - expect(res.body).toHaveProperty("contacts", changing_values.contacts); - - expect(test_offer.ownerName).toEqual(changing_values.name); - expect(test_offer.contacts).toEqual(changing_values.contacts); - }); - - describe("Updating company logo", () => { - test("Should fail if not an image", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .attach("logo", "test/data/not-a-logo.txt") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.IMAGE_FORMAT, - "param": "logo" - }); - }); - - test("Should fail if image is too big", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .attach("logo", "test/data/logo-niaefeup-10mb.png") - .expect(HTTPStatus.UNPROCESSABLE_ENTITY); - - expect(res.body.errors).toContainEqual({ - "location": "body", - "msg": ValidationReasons.FILE_TOO_LARGE(MAX_FILE_SIZE_MB), - "param": "logo" - }); - }); - - test("Should succeed if image is valid", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company._id}/edit`) - .attach("logo", changing_values.logo) - .expect(HTTPStatus.OK); - - expect(res.body).toHaveProperty("logo"); - }); - }); - - describe("Using disabled/blocked company (god)", () => { - test("Should fail if company is blocked (god)", async () => { - const res = await test_agent - .put(`/company/${test_company_blocked._id}/edit`) - .send(withGodToken({ - name: "Changing Blocked Company", - })) - .expect(HTTPStatus.FORBIDDEN); - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.COMPANY_BLOCKED }); - }); - - test("Should fail if company is disabled (god)", async () => { - const res = await test_agent - .put(`/company/${test_company_disabled._id}/edit`) - .send(withGodToken({ - name: "Changing Disabled Company", - })) - .expect(HTTPStatus.FORBIDDEN); - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.COMPANY_DISABLED }); - }); - }); - - describe("Using disabled/blocked company (user)", () => { - test("Should fail if company is blocked (user)", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_blocked) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_blocked._id}/edit`) - .send({ - name: "Changing Blocked Company", - }) - .expect(HTTPStatus.FORBIDDEN); - - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.COMPANY_BLOCKED }); - }); - - test("Should fail if company is disabled (user)", async () => { - await test_agent - .post("/auth/login") - .send(test_user_company_disabled) - .expect(HTTPStatus.OK); - - const res = await test_agent - .put(`/company/${test_company_disabled._id}/edit`) - .send({ - bio: "As user", - }) - .expect(HTTPStatus.FORBIDDEN); - expect(res.body.errors).toContainEqual({ "msg": ValidationReasons.COMPANY_DISABLED }); - }); - }); - }); -}); diff --git a/test/end-to-end/company/application/finish.js b/test/end-to-end/company/application/finish.js new file mode 100755 index 00000000..428c6805 --- /dev/null +++ b/test/end-to-end/company/application/finish.js @@ -0,0 +1,352 @@ +import fs from "fs"; +import { StatusCodes } from "http-status-codes"; +import path from "path"; +import { fileURLToPath } from "url"; +import { MAX_FILE_SIZE_MB } from "../../../../src/api/middleware/utils"; +import ValidationReasons from "../../../../src/api/middleware/validators/validationReasons"; +import config from "../../../../src/config/env"; +import hash from "../../../../src/lib/passwordHashing"; +import Account from "../../../../src/models/Account"; +import Company from "../../../../src/models/Company"; +import CompanyConstants from "../../../../src/models/constants/Company"; +import withGodToken from "../../../utils/GodToken"; +import ValidatorTester from "../../../utils/ValidatorTester"; + +describe("POST /company/application/finish", () => { + + const test_agent = agent(); + + beforeAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + }); + + afterAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + }); + + describe("Input Validation", () => { + + const validationUser = { + email: "validation@email.com", + password: "password123", + }; + const validationCompany = { + name: "Validation Company", + }; + + beforeAll(async () => { + const test_company = await Company.create(validationCompany); + await Account.create({ + email: validationUser.email, + password: await hash(validationUser.password), + company: test_company._id + }); + }); + + beforeEach(async () => { + await test_agent + .post("/auth/login") + .send(validationUser) + .expect(StatusCodes.OK); + }); + + afterAll(async () => { + await Company.deleteMany({ name: validationCompany.name }); + await Account.deleteMany({ email: validationUser.email }); + }); + + const endpointRequest = async (params) => { + + let requestBuilder = test_agent + .post("/company/application/finish") + .attach("logo", params.logo || "test/data/logo-niaefeup.png"); + + if (params.bio) { + requestBuilder = requestBuilder + .field("bio", params.bio); + } + + if (params.contacts) { + requestBuilder = requestBuilder + .field("contacts", params.contacts); + } + + const res = await requestBuilder; + + return res; + }; + + const EndpointValidator = ValidatorTester(endpointRequest); + const BodyValidator = EndpointValidator("body"); + + describe("contacts", () => { + const FieldValidator = BodyValidator("contacts"); + FieldValidator.isRequired(); + + // Can't exactly test this unless we increase the min length + // This is due to the fact that empty arrays are treated as an empty string in multipart form data + // FieldValidator.mustHaveAtLeast(CompanyConstants.contacts.min_length); + + FieldValidator.mustBeArrayBetween(CompanyConstants.contacts.min_length, CompanyConstants.contacts.max_length); + }); + + describe("bio", () => { + const FieldValidator = BodyValidator("bio"); + FieldValidator.isRequired(); + FieldValidator.hasMaxLength(CompanyConstants.bio.max_length); + }); + + describe("logo", () => { + // test this manually since there are no image specific validator testers + test("should fail if file size is too large", async () => { + const res = await endpointRequest({ + logo: "test/data/logo-niaefeup-10mb.png", + contacts: ["Some test contact"], + bio: "some tet bio" + }); + + expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.FILE_TOO_LARGE(MAX_FILE_SIZE_MB), + "param": "logo", + }); + }); + + test("should fail if file is invalid format", async () => { + const res = await endpointRequest({ + logo: fileURLToPath(import.meta.url), + contacts: ["Some test contact"], + bio: "some tet bio" + }); + + expect(res.status).toBe(StatusCodes.UNPROCESSABLE_ENTITY); + expect(res.body.errors).toContainEqual({ + "location": "body", + "msg": ValidationReasons.IMAGE_FORMAT, + "param": "logo", + }); + }); + }); + }); + + describe("Without Auth", () => { + test("should fail if making unauthenticated request", async () => { + await test_agent + .post("/company/application/finish") + .expect(StatusCodes.UNAUTHORIZED); + }); + }); + + describe("With Auth", () => { + + const testUserAdmin = { + email: "admin@email.com", + password: "password123", + }; + + const testFinishedUser = { + email: "finishedUsser@email.com", + password: "password123", + }; + const finishedCompanyData = { + name: "Company Ltd", + hasFinishedRegistration: true, + }; + + beforeAll(async () => { + await Account.create({ + email: testUserAdmin.email, + password: await hash(testUserAdmin.password), + isAdmin: true, + }); + + const testFinishedCompany = await Company.create(finishedCompanyData); + await Account.create({ + email: testFinishedUser.email, + password: await hash(testFinishedUser.password), + company: testFinishedCompany._id + }); + }); + + afterAll(async () => { + await Company.deleteMany({ name: finishedCompanyData.name }); + await Account.deleteMany({ isAdmin: true }); + await Account.deleteMany({ email: testFinishedUser.email }); + }); + + afterEach(async () => { + // Logout + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + }); + + test("should fail if authenticated as admin", async () => { + // Login + await test_agent + .post("/auth/login") + .send(testUserAdmin) + .expect(StatusCodes.OK); + + await request() + .post("/company/application/finish") + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("should fail if sending god token", async () => { + + await test_agent + .delete("/auth/login") + .expect(StatusCodes.OK); + + await test_agent + .post("/company/application/finish") + .send(withGodToken()) + .expect(StatusCodes.UNAUTHORIZED); + }); + + test("should fail if company has already finished registration", async () => { + // Login + await test_agent + .post("/auth/login") + .send(testFinishedUser) + .expect(StatusCodes.OK); + + const res = await test_agent + .post("/company/application/finish") + .expect(StatusCodes.FORBIDDEN); + + expect(res.body).toHaveProperty("errors", expect.arrayContaining( + [ + expect.objectContaining({ + "msg": ValidationReasons.REGISTRATION_FINISHED, + }), + ] + )); + }); + + describe("Contacts", () => { + + const testUser = { + email: "user@email.com", + password: "password123", + }; + const nonFinishedCompanyData = { + name: "Company Ltd", + }; + let testCompany; + + const testSingleContactUser = { + email: "userSingleContact@email.com", + password: "password123", + }; + const nonFinishedSingleContactCompanyData = { + name: "Company Ltd2", + }; + let testSingleContactCompany; + + beforeAll(async () => { + [ + testCompany, + testSingleContactCompany + ] = await Company.create([ + nonFinishedCompanyData, + nonFinishedSingleContactCompanyData + ]); + + await Account.create([ + { + email: testUser.email, + password: await hash(testUser.password), + company: testCompany._id + }, + { + email: testSingleContactUser.email, + password: await hash(testSingleContactUser.password), + company: testSingleContactCompany._id + }, + ]); + }); + + afterAll(async () => { + await Company.deleteMany({ name: nonFinishedCompanyData.name }); + await Company.deleteMany({ name: nonFinishedSingleContactCompanyData.name }); + await Account.deleteMany({ email: testUser.email }); + await Account.deleteMany({ email: testSingleContactUser.email }); + }); + + test("should finish the application with multiple contacts", async () => { + await test_agent + .post("/auth/login") + .send(testUser) + .expect(StatusCodes.OK); + + const contacts = ["contact1", "contact2"]; + + await test_agent + .post("/company/application/finish") + .attach("logo", "test/data/logo-niaefeup.png") + .field("bio", "A very interesting and compelling bio") + .field("contacts", contacts) + .expect(StatusCodes.OK); + + const test_companies = await Company.find({ hasFinishedRegistration: true }); + expect(test_companies).toHaveLength(2); + expect(test_companies).toEqual(expect.arrayContaining( + [ + expect.objectContaining({ + name: testCompany.name, + hasFinishedRegistration: true, + bio: "A very interesting and compelling bio", + contacts, + }), + ] + )); + + const filename = path.join(`${config.upload_folder}/${testCompany.id}.png`); + expect(fs.existsSync(filename)).toBe(true); // TODO: change to async + + // clean up file created + await fs.promises.unlink(filename); + }); + + test("should finish the application with single contact", async () => { + await test_agent + .post("/auth/login") + .send(testSingleContactUser) + .expect(StatusCodes.OK); + + const contacts = ["contact1"]; + + await test_agent + .post("/company/application/finish") + .attach("logo", "test/data/logo-niaefeup.png") + .field("bio", "A very interesting and compelling bio") + .field("contacts", contacts) + .expect(StatusCodes.OK); + + const test_companies = await Company.find({ hasFinishedRegistration: true }); + expect(test_companies).toHaveLength(3); + expect(test_companies).toEqual(expect.arrayContaining( + [ + expect.objectContaining({ + name: testSingleContactCompany.name, + hasFinishedRegistration: true, + bio: "A very interesting and compelling bio", + contacts, + }), + ] + )); + + const filename = path.join(`${config.upload_folder}/${testSingleContactCompany.id}.png`); + expect(fs.existsSync(filename)).toBe(true); // TODO: change to async + + // clean up file created + await fs.promises.unlink(filename); + }); + }); + }); +}); diff --git a/test/end-to-end/company/index.js b/test/end-to-end/company/index.js new file mode 100755 index 00000000..aa67be1d --- /dev/null +++ b/test/end-to-end/company/index.js @@ -0,0 +1,191 @@ +import { StatusCodes } from "http-status-codes"; +import hash from "../../../src/lib/passwordHashing"; +import Account from "../../../src/models/Account"; +import Company from "../../../src/models/Company"; +import withGodToken from "../../utils/GodToken"; + +describe("GET /company", () => { + + const sanitizeCompany = (company) => ({ ...company.toObject(), _id: company._id.toString() }); + + const test_agent = agent(); + + beforeAll(async () => { + await Company.deleteMany({}); + }); + + afterAll(async () => { + await Company.deleteMany({}); + }); + + test("should return an empty array if there are no companies", async () => { + const res = await request() + .get("/company").expect(StatusCodes.OK); + + expect(res.body.companies).toEqual([]); + expect(res.body.totalDocCount).toEqual(0); + }); + + describe("Without Auth", () => { + + const basicCompanyData = { + name: "Company", + }; + + let company, blockedCompany, disabledCompany; + + beforeAll(async () => { + await Company.deleteMany({}); + + [ + company, + blockedCompany, + disabledCompany + ] = await Company.create([ + basicCompanyData, + { ...basicCompanyData, isBlocked: true }, + { ...basicCompanyData, isDisabled: true }, + ]); + }); + + afterAll(async () => { + await Company.deleteMany({}); + }); + + test("should return \"valid\" company", async () => { + + const res = await request() + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).toContainEqual(sanitizeCompany(company)); + }); + + describe("Blocked companies", () => { + test("should not return blocked created company", async () => { + + const res = await request() + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).not.toContainEqual(sanitizeCompany(blockedCompany)); + }); + }); + + describe("Disabled companies", () => { + test("should not return disabled created company", async () => { + + const res = await request() + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).not.toContainEqual(sanitizeCompany(disabledCompany)); + }); + }); + }); + + describe("With Auth", () => { + + const test_user_admin = { + email: "admin@email.com", + password: "password123", + }; + const test_user_company = { + email: "user@email.com", + password: "password123" + }; + + const basicCompanyData = { + name: "Company", + }; + + let company, blockedCompany, disabledCompany; + + beforeAll(async () => { + + await Company.deleteMany({}); + await Account.deleteMany({}); + + await Account.create({ + email: test_user_admin.email, + password: await hash(test_user_admin.password), + isAdmin: true + }); + + [ + company, + blockedCompany, + disabledCompany + ] = await Company.create([ + basicCompanyData, + { ...basicCompanyData, isBlocked: true }, + { ...basicCompanyData, isDisabled: true }, + ]); + + await Account.create({ + email: test_user_company.email, + password: await hash(test_user_company.password), + company: company._id + }); + }); + + afterAll(async () => { + await Company.deleteMany({}); + await Account.deleteMany({}); + }); + + test("should only return \"valid\" company if logged in as company", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_company) + .expect(StatusCodes.OK); + + const res = await test_agent + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).toEqual(expect.arrayContaining([ + sanitizeCompany(company), + ])); + + expect(res.body.totalDocCount).toEqual(1); + }); + + test("should return every company if logged in as admin", async () => { + + await test_agent + .post("/auth/login") + .send(test_user_admin) + .expect(StatusCodes.OK); + + const res = await test_agent + .get("/company") + .expect(StatusCodes.OK); + + expect(res.body.companies).toEqual(expect.arrayContaining([ + sanitizeCompany(company), + sanitizeCompany(blockedCompany), + sanitizeCompany(disabledCompany), + ])); + + expect(res.body.totalDocCount).toEqual(3); + }); + + test("should return every company if god token is sent", async () => { + + const res = await test_agent + .get("/company") + .send(withGodToken({})) + .expect(StatusCodes.OK); + + expect(res.body.companies).toEqual(expect.arrayContaining([ + sanitizeCompany(company), + sanitizeCompany(blockedCompany), + sanitizeCompany(disabledCompany), + ])); + + expect(res.body.totalDocCount).toEqual(3); + }); + }); +}); diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js old mode 100644 new mode 100755 index d79cfe6b..b88d2d2a --- a/test/end-to-end/offer.js +++ b/test/end-to-end/offer.js @@ -1,30 +1,31 @@ +import base64url from "base64url"; import { StatusCodes as HTTPStatus } from "http-status-codes"; -import Offer from "../../src/models/Offer"; -import JobTypes from "../../src/models/constants/JobTypes"; -import * as FieldConstants from "../../src/models/constants/FieldTypes"; -import * as TechnologyConstants from "../../src/models/constants/TechnologyTypes"; import { ErrorTypes } from "../../src/api/middleware/errorHandler"; -import ValidatorTester from "../utils/ValidatorTester"; -import withGodToken from "../utils/GodToken"; -import { DAY_TO_MS } from "../utils/TimeConstants"; -import OfferConstants from "../../src/models/constants/Offer"; +import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; +import { concurrentOffersNotExceeded } from "../../src/api/middleware/validators/validatorUtils"; +import { OFFER_DISABLED_NOTIFICATION } from "../../src/email-templates/companyOfferDisabled"; +import EmailService from "../../src/lib/emailService"; +import hash from "../../src/lib/passwordHashing"; import Account from "../../src/models/Account"; import Company from "../../src/models/Company"; -import hash from "../../src/lib/passwordHashing"; -import ValidationReasons from "../../src/api/middleware/validators/validationReasons"; +import Offer from "../../src/models/Offer"; import CompanyConstants from "../../src/models/constants/Company"; +import * as FieldConstants from "../../src/models/constants/FieldTypes"; +import JobTypes from "../../src/models/constants/JobTypes"; +import OfferConstants from "../../src/models/constants/Offer"; +import * as TechnologyConstants from "../../src/models/constants/TechnologyTypes"; import { MONTH_IN_MS, OFFER_MAX_LIFETIME_MONTHS } from "../../src/models/constants/TimeConstants"; import OfferService from "../../src/services/offer"; -import EmailService from "../../src/lib/emailService"; -import { concurrentOffersNotExceeded } from "../../src/api/middleware/validators/validatorUtils"; -import { OFFER_DISABLED_NOTIFICATION } from "../../src/email-templates/companyOfferDisabled"; -import base64url from "base64url"; +import withGodToken from "../utils/GodToken"; +import { DAY_TO_MS } from "../utils/TimeConstants"; +import ValidatorTester from "../utils/ValidatorTester"; //---------------------------------------------------------------- describe("Offer endpoint tests", () => { + const generateTestOffer = (params) => ({ title: "Test Offer", publishDate: (new Date(Date.now())).toISOString(), @@ -2704,6 +2705,7 @@ describe("Offer endpoint tests", () => { await Offer.deleteMany({}); }); + // TODO: This is perfect for the "test.each" Jest construct test("should sort by publishDate by default", async () => { const res = await request() .get("/offers"); @@ -3042,146 +3044,6 @@ describe("Offer endpoint tests", () => { }); }); - describe("GET /offers/company/:companyId", () => { - beforeAll(async () => { - await Offer.deleteMany({}); - }); - - describe("Id Validation", () => { - test("should fail if requested an invalid companyId", async () => { - const companyId = "123"; - const res = await request() - .get(`/offers/company/${companyId}`); - - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); - }); - - test("should fail if there isn't a company with that id", async () => { - const missingCompanyId = "60ddb0bb2849830020883f91"; - const res = await request().get(`/offers/company/${missingCompanyId}`); - - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.COMPANY_NOT_FOUND(missingCompanyId)); - }); - }); - - describe("Get offer by companyId", () => { - const test_offers = [{}, {}, {}, {}]; - const test_agent = agent(); - - beforeAll(async () => { - await Offer.deleteMany({}); - - const createOffer = async (offer) => { - const { _id, owner, ownerName, ownerLogo } = await Offer.create({ - ...offer, - owner: test_company._id.toString(), - ownerName: test_company.name, - ownerLogo: test_company.logo, - }); - return { - ...offer, - owner: owner.toString(), - ownerName, - ownerLogo, - _id: _id.toString() - }; - }; - - (await Promise.all(test_offers - .map((_, i) => createOffer({ ...generateTestOffer(), isHidden: i === 2 })))) - .forEach((elem, i) => { - test_offers[i] = elem; - }); - }); - - test("should return hidden company offers as company", async () => { - // Login wiht test_user_company - await test_agent - .post("/auth/login") - .send(test_user_company) - .expect(HTTPStatus.OK); - - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - - // Logout - await test_agent - .del("/auth/login") - .expect(HTTPStatus.OK); - }); - - test("should return non-hidden offers", async () => { - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.filter((offer) => offer.isHidden === false).map((offer) => offer._id).sort() - ); - }); - - test("should return non-hidden offers, even if target owner is set", async () => { - const res = await test_agent - .get(`/offers/company/${test_company._id}`) - .send({ - owner: test_company._id - }); - - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.filter((offer) => offer.isHidden === false).map((offer) => offer._id).sort() - ); - }); - - test("should return hidden company offers as admin", async () => { - // Login with test_user_company - await test_agent - .post("/auth/login") - .send(test_user_admin) - .expect(HTTPStatus.OK); - - const res = await test_agent.get(`/offers/company/${test_company._id}`); - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - - // Logout - await test_agent - .del("/auth/login") - .expect(HTTPStatus.OK); - }); - - test("should return hidden company offers with god token", async () => { - // Send request with god token - const res = await test_agent - .get(`/offers/company/${test_company._id}`) - .send(withGodToken()); - - expect(res.status).toBe(HTTPStatus.OK); - - const extractedData = res.body; - expect(extractedData.map((offer) => offer._id).sort()) - .toMatchObject( - test_offers.map((offer) => offer._id).sort() - ); - }); - }); - }); - describe("GET /offers/:offerId", () => { beforeAll(async () => { @@ -3195,6 +3057,7 @@ describe("Offer endpoint tests", () => { expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.OBJECT_ID); }); + test("should fail if an offer does not exist", async () => { const id = "5facf0cdb8bc30016ee58952"; const res = await request() @@ -3642,6 +3505,7 @@ describe("Offer endpoint tests", () => { expect(res.body.errors[0]).toHaveProperty("param", "jobMinDuration"); expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_BEFORE("jobMaxDuration")); }); + test("should fail if maxDuration smaller than offer's minDuration", async () => { const res = await test_agent .post(`/offers/edit/${future_test_offer._id.toString()}`) @@ -3650,6 +3514,7 @@ describe("Offer endpoint tests", () => { expect(res.body.errors[0]).toHaveProperty("param", "jobMaxDuration"); expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_AFTER("jobMinDuration")); }); + test("should fail if invalid combination of jobDuration in request", async () => { const res = await test_agent .post(`/offers/edit/${future_test_offer._id.toString()}`) @@ -4136,6 +4001,7 @@ describe("Offer endpoint tests", () => { describe("POST /offers/:offerId/disable", () => { let test_offer, test_offer_2, hidden_default_test_offer, hidden_user_test_offer, email_test_offer; + beforeAll(async () => { test_offer = await Offer.create({ ...generateTestOffer({ diff --git a/test/end-to-end/offer/index.js b/test/end-to-end/offer/index.js new file mode 100755 index 00000000..491247ec --- /dev/null +++ b/test/end-to-end/offer/index.js @@ -0,0 +1,3 @@ +test("bruh", () => { + expect(true).toBe(true); +}); diff --git a/test/end-to-end/review.js b/test/end-to-end/review.js deleted file mode 100644 index aab25cdc..00000000 --- a/test/end-to-end/review.js +++ /dev/null @@ -1,456 +0,0 @@ -jest.mock("../../src/lib/emailService"); -import EmailService, { EmailService as EmailServiceClass } from "../../src/lib/emailService"; -jest.spyOn(EmailServiceClass.prototype, "verifyConnection").mockImplementation(() => Promise.resolve()); -import { StatusCodes as HTTPStatus } from "http-status-codes"; -import CompanyApplication, { CompanyApplicationRules } from "../../src/models/CompanyApplication"; -import hash from "../../src/lib/passwordHashing"; -import Account from "../../src/models/Account"; -import { ErrorTypes } from "../../src/api/middleware/errorHandler"; -import ApplicationStatus from "../../src/models/constants/ApplicationStatus"; -import { APPROVAL_NOTIFICATION, REJECTION_NOTIFICATION } from "../../src/email-templates/companyApplicationApproval"; -import mongoose from "mongoose"; - -const { ObjectId } = mongoose.Types; - -describe("Company application review endpoint test", () => { - - describe("/applications/company", () => { - - describe("Without Auth", () => { - beforeEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should return HTTP 401 error", async () => { - const emptyRes = await request() - .get("/applications/company/search"); - - expect(emptyRes.status).toBe(HTTPStatus.UNAUTHORIZED); - }); - - }); - - describe("With Auth", () => { - const test_agent = agent(); - const test_user = { - email: "user@email.com", - password: "password123", - }; - - beforeAll(async () => { - await Account.deleteMany({}); - await Account.create({ email: test_user.email, password: await hash(test_user.password), isAdmin: true }); - - // Login - await test_agent - .post("/auth/login") - .send(test_user) - .expect(200); - }); - - beforeEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should list existing applications", async () => { - const emptyRes = await test_agent - .get("/applications/company/search"); - - expect(emptyRes.status).toBe(HTTPStatus.OK); - expect(emptyRes.body.applications).toEqual([]); - - const application = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - }; - - await CompanyApplication.create({ - ...application, - submittedAt: Date.now(), - }); - - const nonEmptyRes = await test_agent - .get("/applications/company/search"); - - expect(nonEmptyRes.status).toBe(HTTPStatus.OK); - expect(nonEmptyRes.body.applications.length).toBe(1); - expect(nonEmptyRes.body.applications[0]).toHaveProperty("email", application.email); - - }); - - describe("Filter application results", () => { - - const pendingApplication = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - const approvedApplication = { - ...pendingApplication, - submittedAt: new Date("2019-11-24"), - approvedAt: pendingApplication.submittedAt.getTime() + 1, - companyName: "approved Testing company", - email: `approved${pendingApplication.email}`, - }; - const rejectedApplication = { ...pendingApplication, - submittedAt: new Date("2019-11-23"), - rejectedAt: pendingApplication.submittedAt.getTime() + 1, - companyName: "rejected Testing company", - email: `rejected${pendingApplication.email}`, - rejectReason: "2bad4nij0bs", - }; - - beforeEach(async () => { - await CompanyApplication.create(pendingApplication); - await CompanyApplication.create(approvedApplication); - await CompanyApplication.create(rejectedApplication); - }); - - afterEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should filter by company name", async () => { - const fullNameQuery = await test_agent - .get(`/applications/company/search?companyName=${"approved Testing company"}`); - - expect(fullNameQuery.status).toBe(HTTPStatus.OK); - expect(fullNameQuery.body.applications.length).toBe(1); - expect(fullNameQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - - const partialNameQuery = await test_agent - .get(`/applications/company/search?companyName=${"Testing company"}`); - - expect(partialNameQuery.status).toBe(HTTPStatus.OK); - expect(partialNameQuery.body.applications.length).toBe(3); - expect(partialNameQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(partialNameQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(partialNameQuery.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); - }); - - test("Should filter by state", async () => { - - const wrongFormatQuery = await test_agent - .get(`/applications/company/search?state=<["${ApplicationStatus.APPROVED}"]`); - - expect(wrongFormatQuery.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(wrongFormatQuery.body.errors[0]).toStrictEqual({ - location: "query", - msg: "must-be-in:[PENDING,APPROVED,REJECTED]", - param: "state", - value: [`<["${ApplicationStatus.APPROVED}"]`] - }); - - - const singleStateQuery = await test_agent - .get("/applications/company/search").query({ state: [ApplicationStatus.APPROVED] }); - - expect(singleStateQuery.status).toBe(HTTPStatus.OK); - expect(singleStateQuery.body.applications.length).toBe(1); - expect(singleStateQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - - const multiStateQuery = await test_agent - .get("/applications/company/search?").query({ state: [ApplicationStatus.APPROVED, ApplicationStatus.PENDING] }); - - expect(multiStateQuery.status).toBe(HTTPStatus.OK); - expect(multiStateQuery.body.applications.length).toBe(2); - expect(multiStateQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(multiStateQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - }); - - test("Should filter by date", async () => { - - const afterQuery = await test_agent - .get(`/applications/company/search?submissionDateFrom=${approvedApplication.submittedAt}`); - - expect(afterQuery.status).toBe(HTTPStatus.OK); - expect(afterQuery.body.applications.length).toBe(2); - expect(afterQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(afterQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - - const untilQuery = await test_agent - .get(`/applications/company/search?submissionDateTo=${approvedApplication.submittedAt}`); - - expect(untilQuery.status).toBe(HTTPStatus.OK); - expect(untilQuery.body.applications.length).toBe(2); - expect(untilQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - expect(untilQuery.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); - - const intervalQuery = await test_agent - .get("/applications/company/search?" + - `submissionDateFrom=${approvedApplication.submittedAt}&` + - `submissionDateTo=${approvedApplication.submittedAt}`); - - - expect(intervalQuery.status).toBe(HTTPStatus.OK); - expect(intervalQuery.body.applications.length).toBe(1); - expect(intervalQuery.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - - }); - - }); - - describe("Sort application results", () => { - - const pendingApplication = { - email: "test2@test.com", - password: "password123", - companyName: "testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - const approvedApplication = { - ...pendingApplication, - submittedAt: new Date("2019-11-24"), - approvedAt: pendingApplication.submittedAt.getTime() + 1, - companyName: "approved Testing company", - email: `approved${pendingApplication.email}`, - }; - const rejectedApplication = { ...pendingApplication, - submittedAt: new Date("2019-11-23"), - rejectedAt: pendingApplication.submittedAt.getTime() + 1, - companyName: "rejected Testing company", - email: `rejected${pendingApplication.email}`, - rejectReason: "2bad4nij0bs", - }; - - beforeEach(async () => { - await CompanyApplication.create(pendingApplication); - await CompanyApplication.create(approvedApplication); - await CompanyApplication.create(rejectedApplication); - }); - - afterEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should sort by company name ascending", async () => { - const query = await test_agent - .get("/applications/company/search?sortBy=companyName:asc"); - - expect(query.status).toBe(HTTPStatus.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", approvedApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", pendingApplication.companyName); - }); - - test("Should sort by company name descending", async () => { - const query = await test_agent - .get("/applications/company/search?sortBy=companyName:desc"); - - - expect(query.status).toBe(HTTPStatus.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", rejectedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", approvedApplication.companyName); - }); - - test("Should sort by submissionDate descending", async () => { - const defaultQuery = await test_agent - .get("/applications/company/search"); - - expect(defaultQuery.status).toBe(HTTPStatus.OK); - expect(defaultQuery.body.applications.length).toBe(3); - expect(defaultQuery.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(defaultQuery.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(defaultQuery.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); - - const query = await test_agent - .get("/applications/company/search?sortBy=submittedAt:desc"); - - expect(query.status).toBe(HTTPStatus.OK); - expect(query.body.applications.length).toBe(3); - expect(query.body.applications[0]).toHaveProperty("companyName", pendingApplication.companyName); - expect(query.body.applications[1]).toHaveProperty("companyName", approvedApplication.companyName); - expect(query.body.applications[2]).toHaveProperty("companyName", rejectedApplication.companyName); - }); - - }); - - describe("Approval/Rejection", () => { - let application; - const pendingApplication = { - email: "test2@test.com", - password: "password123", - companyName: "Testing company", - motivation: "This company has a very valid motivation, because otherwise the tests would not exist.", - submittedAt: new Date("2019-11-25"), - }; - - - describe("Approve application", () => { - - beforeEach(async () => { - await Account.deleteMany({ email: pendingApplication.email }); - application = await CompanyApplication.create(pendingApplication); - }); - - afterEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should approve pending application", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.email).toBe(pendingApplication.email); - expect(res.body.companyName).toBe(pendingApplication.companyName); - }); - - test("Should send approval email to company email", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.OK); - - const emailOptions = APPROVAL_NOTIFICATION(application.companyName); - - expect(EmailService.sendMail).toHaveBeenCalledWith({ - subject: emailOptions.subject, - to: application.email, - template: emailOptions.template, - context: emailOptions.context, - }); - - }); - - test("Should fail if trying to approve inexistent application", async () => { - - const res = await test_agent - .post(`/applications/company/${new ObjectId()}/approve`); - - expect(res.status).toBe(HTTPStatus.NOT_FOUND); - }); - - test("Should fail if trying to approve already approved application", async () => { - await test_agent - .post(`/applications/company/${application._id}/approve`); - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.CONFLICT); - }); - - test("Should fail if trying to approve already rejected application", async () => { - await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.CONFLICT); - }); - - test("Should fail if approving application with an existing account with same email, and then rollback", async () => { - await Account.create({ email: application.email, password: "passwordHashedButNotReally", isAdmin: true }); - - const res = await test_agent - .post(`/applications/company/${application._id}/approve`); - - expect(res.status).toBe(HTTPStatus.CONFLICT); - expect(res.body.error_code).toBe(ErrorTypes.VALIDATION_ERROR); - expect(res.body.errors[0].msg).toBe(CompanyApplicationRules.EMAIL_ALREADY_IN_USE.msg); - - const result_application = await CompanyApplication.findById(application._id); - expect(result_application.state).toBe(ApplicationStatus.PENDING); - }); - }); - - describe("Reject application", () => { - - beforeEach(async () => { - await Account.deleteMany({ email: pendingApplication.email }); - application = await CompanyApplication.create(pendingApplication); - }); - - afterEach(async () => { - await CompanyApplication.deleteMany({}); - }); - - test("Should fail if no rejectReason provided", async () => { - const res = await test_agent - .post(`/applications/company/${application._id}/reject`); - - expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body.errors[0]).toStrictEqual({ location: "body", msg: "required", param: "rejectReason" }); - - }); - - test("Should reject pending application", async () => { - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(HTTPStatus.OK); - expect(res.body.email).toBe(pendingApplication.email); - expect(res.body.companyName).toBe(pendingApplication.companyName); - }); - - test("Should send rejection email to company email", async () => { - - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(HTTPStatus.OK); - - const emailOptions = REJECTION_NOTIFICATION(application.companyName); - - expect(EmailService.sendMail).toHaveBeenCalledWith({ - subject: emailOptions.subject, - to: application.email, - template: emailOptions.template, - context: emailOptions.context, - }); - - }); - - test("Should fail if trying to reject inexistent application", async () => { - const res = await test_agent - .post(`/applications/company/${new ObjectId()}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(HTTPStatus.NOT_FOUND); - }); - - test("Should fail if trying to reject already approved application", async () => { - await test_agent - .post(`/applications/company/${application._id}/approve`); - - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(HTTPStatus.CONFLICT); - }); - - test("Should fail if trying to reject already rejected application", async () => { - await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - const res = await test_agent - .post(`/applications/company/${application._id}/reject`) - .send({ rejectReason: "Some reason which is valid" }); - - expect(res.status).toBe(HTTPStatus.CONFLICT); - }); - }); - }); - }); - }); -}); diff --git a/test/account_schema.js b/test/schema/AccountSchema.js old mode 100644 new mode 100755 similarity index 98% rename from test/account_schema.js rename to test/schema/AccountSchema.js index d8ccb392..9fd97123 --- a/test/account_schema.js +++ b/test/schema/AccountSchema.js @@ -1,4 +1,4 @@ -import Account from "../src/models/Account.js"; +import Account from "../../src/models/Account.js"; describe("# Account schema tests", () => { describe("Testing required fields", () => { diff --git a/test/company_application_schema.js b/test/schema/CompanyApplicationSchema.js old mode 100644 new mode 100755 similarity index 90% rename from test/company_application_schema.js rename to test/schema/CompanyApplicationSchema.js index 2331eed7..0bf25736 --- a/test/company_application_schema.js +++ b/test/schema/CompanyApplicationSchema.js @@ -1,7 +1,7 @@ -import CompanyApplication from "../src/models/CompanyApplication"; -import SchemaTester from "./utils/SchemaTester"; -import ApplicationStatus from "../src/models/constants/ApplicationStatus"; -import CompanyApplicationConstants from "../src/models/constants/CompanyApplication"; +import CompanyApplication from "../../src/models/CompanyApplication"; +import SchemaTester from "../utils/SchemaTester"; +import ApplicationStatus from "../../src/models/constants/ApplicationStatus"; +import CompanyApplicationConstants from "../../src/models/constants/CompanyApplication"; const companyApplicationTester = SchemaTester(CompanyApplication); describe("# CompanyApplication schema tests", () => { diff --git a/test/company_schema.js b/test/schema/CompanySchema.js old mode 100644 new mode 100755 similarity index 91% rename from test/company_schema.js rename to test/schema/CompanySchema.js index adc80d99..730b0cbe --- a/test/company_schema.js +++ b/test/schema/CompanySchema.js @@ -1,8 +1,8 @@ -import Company from "../src/models/Company"; -import SchemaTester from "./utils/SchemaTester"; -import CompanyConstants from "../src/models/constants/Company"; -import { DAY_TO_MS } from "./utils/TimeConstants"; -import Offer from "../src/models/Offer"; +import Company from "../../src/models/Company"; +import SchemaTester from "../utils/SchemaTester"; +import CompanyConstants from "../../src/models/constants/Company"; +import { DAY_TO_MS } from "../utils/TimeConstants"; +import Offer from "../../src/models/Offer"; const CompanySchemaTester = SchemaTester(Company); diff --git a/test/offer_schema.js b/test/schema/OfferSchema.js old mode 100644 new mode 100755 similarity index 98% rename from test/offer_schema.js rename to test/schema/OfferSchema.js index 30436f88..fc8ca3a2 --- a/test/offer_schema.js +++ b/test/schema/OfferSchema.js @@ -1,8 +1,8 @@ -import Offer from "../src/models/Offer"; -import JobTypes from "../src/models/constants/JobTypes"; -import { MIN_FIELDS, MAX_FIELDS, FieldTypes } from "../src/models/constants/FieldTypes"; -import { MIN_TECHNOLOGIES, MAX_TECHNOLOGIES, TechnologyTypes } from "../src/models/constants/TechnologyTypes"; -import { OFFER_MAX_LIFETIME_MONTHS } from "../src/models/constants/TimeConstants"; +import Offer from "../../src/models/Offer"; +import JobTypes from "../../src/models/constants/JobTypes"; +import { MIN_FIELDS, MAX_FIELDS, FieldTypes } from "../../src/models/constants/FieldTypes"; +import { MIN_TECHNOLOGIES, MAX_TECHNOLOGIES, TechnologyTypes } from "../../src/models/constants/TechnologyTypes"; +import { OFFER_MAX_LIFETIME_MONTHS } from "../../src/models/constants/TimeConstants"; describe("# Offer Schema tests", () => { describe("Required and bound (between min and max elements) properties tests", () => { diff --git a/test/unit/EmailService.js b/test/unit/EmailService.js old mode 100644 new mode 100755 diff --git a/test/unit/auth.js b/test/unit/auth.js old mode 100644 new mode 100755 diff --git a/test/unit/token.js b/test/unit/token.js old mode 100644 new mode 100755 diff --git a/test/unit/utils.js b/test/unit/utils.js old mode 100644 new mode 100755 diff --git a/test/utils/GodToken.js b/test/utils/GodToken.js old mode 100644 new mode 100755 diff --git a/test/utils/SchemaTester.js b/test/utils/SchemaTester.js old mode 100644 new mode 100755 diff --git a/test/utils/TimeConstants.js b/test/utils/TimeConstants.js old mode 100644 new mode 100755 diff --git a/test/utils/ValidatorTester.js b/test/utils/ValidatorTester.js old mode 100644 new mode 100755 index f519c2a9..da875e47 --- a/test/utils/ValidatorTester.js +++ b/test/utils/ValidatorTester.js @@ -88,7 +88,7 @@ const ValidatorTester = (requestEndpoint) => (location) => (field_name) => ({ "location": location, "msg": ValidationReasons.DATE, "param": field_name, - "value": params[field_name], + "value": location === "query" ? params[field_name].toString() : params[field_name], }); }); }); @@ -172,10 +172,32 @@ const ValidatorTester = (requestEndpoint) => (location) => (field_name) => ({ }); }, + mustBeArray: () => { + test("should be array", async () => { + const params = { + [field_name]: "not_an_array", + }; + const res = await requestEndpoint(params); + + executeValidatorTestWithContext({ requestEndpoint, location, field_name }, () => { + checkCommonErrorResponse(res); + expect(res.body.errors).toContainEqual({ + "location": location, + "msg": ValidationReasons.ARRAY, + "param": field_name, + "value": params[field_name], + }); + }); + }); + }, + mustBeInArray: (array) => { test(`should be one of: [${array}]`, async () => { + + const value = "not_in_array"; + const params = { - [field_name]: "not_in_array", + [field_name]: value, }; const res = await requestEndpoint(params); @@ -183,7 +205,7 @@ const ValidatorTester = (requestEndpoint) => (location) => (field_name) => ({ checkCommonErrorResponse(res); expect(res.body.errors).toContainEqual({ "location": location, - "msg": ValidationReasons.IN_ARRAY(array), + "msg": ValidationReasons.IN_ARRAY(array, value), "param": field_name, "value": params[field_name], }); @@ -353,6 +375,26 @@ const ValidatorTester = (requestEndpoint) => (location) => (field_name) => ({ }); }, + mustBeLessThanOrEqualTo: (max) => { + test(`should be less than or equal to ${max}`, async () => { + const params = { + [field_name]: max + 1, + }; + + const res = await requestEndpoint(params); + + executeValidatorTestWithContext({ requestEndpoint, location, field_name }, () => { + checkCommonErrorResponse(res); + expect(res.body.errors).toContainEqual({ + "location": location, + "msg": ValidationReasons.MAX(max), + "param": field_name, + "value": params[field_name], + }); + }); + }); + }, + mustBeGreaterThanOrEqualToField: (field_name2) => { test(`should be greater than or equal to ${field_name2}`, async () => { const params = {