diff --git a/.eslintrc b/.eslintrc index 9a724d80..c5433d85 100644 --- a/.eslintrc +++ b/.eslintrc @@ -71,6 +71,7 @@ "ignoreRegExpLiterals": true, "ignorePattern": "^import.+|test" } - ] + ], + "new-cap": "off" } } diff --git a/.github/scripts/validate-audit-report.sh b/.github/scripts/validate-audit-report.sh index 27ebfbac..b961f5e0 100755 --- a/.github/scripts/validate-audit-report.sh +++ b/.github/scripts/validate-audit-report.sh @@ -6,8 +6,8 @@ echo '{"vulnerabilities": [' > audit-report.json awk 'NR > 1 {print ","} {print}' audit-report.txt >> audit-report.json echo ']}' >> audit-report.json -# Filter JSON array to remove lerna's transitive dependencies as these dependencies are not used at runtime -jq '.vulnerabilities |= map(select(.data.resolution.path | type == "string" and startswith("lerna") | not))' audit-report.json > audit-report-filtered.json +# Filter JSON array to remove jest and lerna's transitive dependencies as these dependencies are not used at runtime +jq '.vulnerabilities |= map(select(.data.resolution.path | type == "string" and (startswith("lerna") or startswith("jest") or startswith("@types/jest") or startswith("babel-jest")) | not))' audit-report.json > audit-report-filtered.json # Fail the build if filtered JSON array contains audit advisories if [ "$(jq '.vulnerabilities[] | select(.type == "auditAdvisory") | .type' audit-report-filtered.json | wc -l)" -gt 0 ]; then diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 00000000..a2c43d53 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,85 @@ +name: Publish Packages + +on: + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '18.x' + registry-url: 'https://registry.npmjs.org' + + - name: Authenticate to npm + run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc + + - name: Install dependencies + run: yarn install + + - name: Build packages + run: yarn run build + + - name: Run tests + run: yarn run test + + - name: Checkout sinch-sdk-mockserver repository + uses: actions/checkout@v3 + with: + repository: sinch/sinch-sdk-mockserver + token: ${{ secrets.PAT_CI }} + fetch-depth: 0 + path: sinch-sdk-mockserver + + - name: Install Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y docker-compose + + - name: Start mock servers with Docker Compose + run: | + cd sinch-sdk-mockserver + docker-compose up -d + + - name: Create target directories for feature files + run: | + mkdir -p ./packages/fax/tests/e2e/features + mkdir -p ./packages/numbers/tests/e2e/features + mkdir -p ./packages/conversation/tests/e2e/features + mkdir -p ./packages/elastic-sip-trunking/tests/e2e/features + mkdir -p ./packages/sms/tests/e2e/features + mkdir -p ./packages/verification/tests/e2e/features + mkdir -p ./packages/voice/tests/e2e/features + + - name: Copy feature files + run: | + cp sinch-sdk-mockserver/features/fax/*.feature ./packages/fax/tests/e2e/features/ + cp sinch-sdk-mockserver/features/numbers/*.feature ./packages/numbers/tests/e2e/features/ + cp sinch-sdk-mockserver/features/conversation/*.feature ./packages/conversation/tests/e2e/features/ + cp sinch-sdk-mockserver/features/elastic-sip-trunking/*.feature ./packages/elastic-sip-trunking/tests/e2e/features/ + cp sinch-sdk-mockserver/features/sms/*.feature ./packages/sms/tests/e2e/features/ + cp sinch-sdk-mockserver/features/verification/*.feature ./packages/verification/tests/e2e/features/ + cp sinch-sdk-mockserver/features/voice/*.feature ./packages/voice/tests/e2e/features/ + + - name: Run e2e tests + run: yarn run e2e + + - name: Publish packages + run: | + cd packages/sdk-client && npm publish + cd ../numbers && npm publish + cd ../sms && npm publish + cd ../verification && npm publish + cd ../voice && npm publish + cd ../conversation && npm publish + cd ../fax && npm publish + cd ../elastic-sip-trunking && npm publish + cd ../sdk-core && npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/run-ci.yaml b/.github/workflows/run-ci.yaml index 3f1d6f1e..acf12067 100644 --- a/.github/workflows/run-ci.yaml +++ b/.github/workflows/run-ci.yaml @@ -7,18 +7,66 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x, 20.x] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} + - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - - run: yarn install - - run: npx eslint "packages/**/src/**/*.ts" - - run: npx eslint "packages/**/tests/**/*.ts" - - run: yarn run build - - run: yarn run test + + - name: Install dependencies + run: yarn install + + - name: Run ESLint + run: npx eslint "packages/**/{src,tests}/**/*.ts" + + - name: Build project + run: yarn run build + + - name: Run unit tests + run: yarn run test + + - name: Checkout sinch-sdk-mockserver repository + uses: actions/checkout@v3 + with: + repository: sinch/sinch-sdk-mockserver + token: ${{ secrets.PAT_CI }} + fetch-depth: 0 + path: sinch-sdk-mockserver + + - name: Install Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y docker-compose + + - name: Start mock servers with Docker Compose + run: | + cd sinch-sdk-mockserver + docker-compose up -d + + - name: Create target directories for feature files + run: | + mkdir -p ./packages/fax/tests/e2e/features + mkdir -p ./packages/numbers/tests/e2e/features + mkdir -p ./packages/conversation/tests/e2e/features + mkdir -p ./packages/elastic-sip-trunking/tests/e2e/features + mkdir -p ./packages/sms/tests/e2e/features + mkdir -p ./packages/verification/tests/e2e/features + mkdir -p ./packages/voice/tests/e2e/features + + - name: Copy feature files + run: | + cp sinch-sdk-mockserver/features/fax/*.feature ./packages/fax/tests/e2e/features/ + cp sinch-sdk-mockserver/features/numbers/*.feature ./packages/numbers/tests/e2e/features/ + cp sinch-sdk-mockserver/features/conversation/*.feature ./packages/conversation/tests/e2e/features/ + cp sinch-sdk-mockserver/features/elastic-sip-trunking/*.feature ./packages/elastic-sip-trunking/tests/e2e/features/ + cp sinch-sdk-mockserver/features/sms/*.feature ./packages/sms/tests/e2e/features/ + cp sinch-sdk-mockserver/features/verification/*.feature ./packages/verification/tests/e2e/features/ + cp sinch-sdk-mockserver/features/voice/*.feature ./packages/voice/tests/e2e/features/ + + - name: Run e2e tests + run: yarn run e2e sonarcloud: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index f48cf608..8144aab8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tsconfig.build.tsbuildinfo tsconfig.tsbuildinfo packages/**/dist .env +*.feature diff --git a/examples/integrated-flows-examples/package.json b/examples/integrated-flows-examples/package.json index 4b626f61..20a364ba 100644 --- a/examples/integrated-flows-examples/package.json +++ b/examples/integrated-flows-examples/package.json @@ -13,7 +13,7 @@ "verification:app": "yarn compile && node dist/verification/app.js" }, "dependencies": { - "@sinch/sdk-core": "^1.1.0", + "@sinch/sdk-core": "^1.2.0", "@types/node": "^20.8.7", "dotenv": "^16.3.1", "inquirer": "^9.2.14", diff --git a/examples/integrated-flows-examples/src/numbers/app.ts b/examples/integrated-flows-examples/src/numbers/app.ts index 501f9321..9d505f11 100644 --- a/examples/integrated-flows-examples/src/numbers/app.ts +++ b/examples/integrated-flows-examples/src/numbers/app.ts @@ -68,7 +68,7 @@ dotenv.config(); try { // Send the HTTP request with the SDK method availableNumbersResponse - = await sinchClient.numbers.availableNumber.list(listAvailableNumbersRequestData); + = await sinchClient.numbers.searchForAvailableNumbers(listAvailableNumbersRequestData); } catch (error) { // Catch error if any, log it and stop the program console.error(`ERROR: An error occurred when trying to list the available numbers for the type ${type}`); @@ -100,7 +100,7 @@ dotenv.config(); let rentNumberResponse; try { // Send the HTTP request with the SDK method - rentNumberResponse = await sinchClient.numbers.availableNumber.rent(rentNumberRequestData); + rentNumberResponse = await sinchClient.numbers.rent(rentNumberRequestData); } catch (error) { // Catch error if any, log it and stop the program console.error(`ERROR: Impossible to rent the number ${phoneNumber1}`); @@ -129,7 +129,7 @@ dotenv.config(); let rentAnyNumberResponse; try { // Send the HTTP request with the SDK method - rentAnyNumberResponse = await sinchClient.numbers.availableNumber.rentAny(rentAnyNumberRequestData); + rentAnyNumberResponse = await sinchClient.numbers.rentAny(rentAnyNumberRequestData); } catch (error) { // Catch error if any, log it and stop the program console.error(`ERROR: Impossible to rent a number in the region ${regionCode} of type ${type}`); @@ -152,7 +152,7 @@ dotenv.config(); let getActiveNumberResponse; try { // Send the HTTP request with the SDK method - getActiveNumberResponse = await sinchClient.numbers.activeNumber.get(getActiveNumberRequestData); + getActiveNumberResponse = await sinchClient.numbers.get(getActiveNumberRequestData); } catch (error) { // Catch error if any, log it and stop the program console.error(`ERROR: Impossible to get details for the number ${phoneNumber1}`); @@ -172,7 +172,7 @@ dotenv.config(); // The ActiveNumbersResponse is paginated. Let's fetch all the pages using the iterator functionality const activeNumbersList: Numbers.ActiveNumber[] = []; - for await (const activeNumber of sinchClient.numbers.activeNumber.list(listActiveNumbersRequestData)) { + for await (const activeNumber of sinchClient.numbers.list(listActiveNumbersRequestData)) { activeNumbersList.push(activeNumber); } @@ -201,7 +201,7 @@ dotenv.config(); try { // Send the HTTP request with the SDK method updateActiveNumberResponse - = await sinchClient.numbers.activeNumber.update(updateActiveNumberRequestData); + = await sinchClient.numbers.update(updateActiveNumberRequestData); } catch (error) { // Catch error if any, log it and stop the program console.log(`ERROR: Impossible to update the number ${phoneNumber1}`); @@ -222,7 +222,7 @@ dotenv.config(); let releaseActiveNumberResponse; try { // Send the HTTP request with the SDK method - releaseActiveNumberResponse = await sinchClient.numbers.activeNumber.release(releaseActiveNumberRequestData); + releaseActiveNumberResponse = await sinchClient.numbers.release(releaseActiveNumberRequestData); } catch (error) { // Catch error if any, log it and stop the program console.error(`ERROR: Impossible to release the number ${phoneNumber1}`); @@ -241,7 +241,7 @@ dotenv.config(); try { // Send the HTTP request with the SDK method - releaseActiveNumberResponse = await sinchClient.numbers.activeNumber.release(releaseActiveNumberRequestData); + releaseActiveNumberResponse = await sinchClient.numbers.release(releaseActiveNumberRequestData); } catch (error) { // Catch error if any, log it and stop the program console.error(`ERROR: Impossible to release the number ${phoneNumber2}`); diff --git a/examples/integrated-flows-examples/src/verification/app.ts b/examples/integrated-flows-examples/src/verification/app.ts index 5c3cf202..1aadd449 100644 --- a/examples/integrated-flows-examples/src/verification/app.ts +++ b/examples/integrated-flows-examples/src/verification/app.ts @@ -18,9 +18,9 @@ dotenv.config(); enum VerificationMethods { SMS ='sms', - CALLOUT = 'callout', + PHONE_CALL = 'phone call', FLASH_CALL = 'flash call', - SEAMLESS = 'seamless', + DATA = 'data', } inquirer.prompt([ @@ -41,13 +41,13 @@ dotenv.config(); case VerificationMethods.SMS: startSmsVerificationFlow(answers.phoneNumber); break; - case VerificationMethods.CALLOUT: - startCalloutVerificationFlow(answers.phoneNumber); + case VerificationMethods.PHONE_CALL: + startPhoneCallVerificationFlow(answers.phoneNumber); break; case VerificationMethods.FLASH_CALL: startFlashCallVerificationFlow(answers.phoneNumber); break; - case VerificationMethods.SEAMLESS: + case VerificationMethods.DATA: startSeamlessVerificationFlow(answers.phoneNumber); break; } @@ -72,9 +72,9 @@ dotenv.config(); }; - const startCalloutVerificationFlow = async (phoneNumber: string) => { - const requestData = Verification.startVerificationHelper.buildCalloutRequest(phoneNumber); - const response = await sinch.verification.verifications.startCallout(requestData); + const startPhoneCallVerificationFlow = async (phoneNumber: string) => { + const requestData = Verification.startVerificationHelper.buildPhoneCallRequest(phoneNumber); + const response = await sinch.verification.verifications.startPhoneCall(requestData); console.log('Verification request sent! Please answer to the phone call ans listen to the OTP.'); const answers = await inquirer.prompt([ { @@ -83,9 +83,9 @@ dotenv.config(); message: 'Enter the verification code:', }, ]); - const reportRequestData = Verification.reportVerificationByIdHelper.buildCalloutRequest( + const reportRequestData = Verification.reportVerificationByIdHelper.buildPhoneCallRequest( response.id!, answers.code); - const reportResponse = await sinch.verification.verifications.reportCalloutById(reportRequestData); + const reportResponse = await sinch.verification.verifications.reportPhoneCallById(reportRequestData); console.log(`Verification status: ${reportResponse.status}${reportResponse.status === 'SUCCESSFUL'?'':' - Reason: ' + reportResponse.reason}`); }; @@ -109,10 +109,10 @@ dotenv.config(); }; const startSeamlessVerificationFlow = async (phoneNumber: string) => { - const requestData = Verification.startVerificationHelper.buildSeamlessRequest(phoneNumber); + const requestData = Verification.startVerificationHelper.buildDataRequest(phoneNumber); let response; try { - response = await sinch.verification.verifications.startSeamless(requestData); + response = await sinch.verification.verifications.startData(requestData); } catch (error: any) { console.log(`Impossible to process the seamless verification: ${error.data})`); return; diff --git a/examples/simple-examples/.gitignore b/examples/simple-examples/.gitignore index 2ccc0a30..e71e7c24 100644 --- a/examples/simple-examples/.gitignore +++ b/examples/simple-examples/.gitignore @@ -6,4 +6,5 @@ .DS_Store .env -fax-pdf/ +fax-pdf/* +!fax-pdf/you-faxed.pdf diff --git a/examples/simple-examples/README.md b/examples/simple-examples/README.md index d78fe15f..500f9f06 100644 --- a/examples/simple-examples/README.md +++ b/examples/simple-examples/README.md @@ -149,20 +149,21 @@ yarn run numbers:regions:list ### Verification -| Service | Sample application name and location | Required parameters | -|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| -| Verifications | [./src/verification/verifications/sms/start-sms.ts](./src/verification/verifications/sms/start-sms.ts) | `VERIFICATION_IDENTITY` | -| | [./src/verification/verifications/sms/report-with-id_sms.ts](./src/verification/verifications/sms/report-with-id_sms.ts) | `VERIFICATION_ID` + `VERIFICATION_CODE` | -| | [./src/verification/verifications/sms/report-with-identity_sms.ts](./src/verification/verifications/sms/report-with-identity_sms.ts) | `VERIFICATION_IDENTITY` + `VERIFICATION_CODE` | -| | [./src/verification/verifications/flashcall/start-flashcall.ts](./src/verification/verifications/flashcall/start-flashcall.ts) | `VERIFICATION_IDENTITY` | -| | [./src/verification/verifications/flashcall/report-with-id_flashcall.ts](./src/verification/verifications/flashcall/report-with-id_flashcall.ts) | `VERIFICATION_ID` + `VERIFICATION_CLI` | -| | [./src/verification/verifications/flashcall/report-with-identity_flashcall.ts](./src/verification/verifications/flashcall/report-with-identity_flashcall.ts) | `VERIFICATION_IDENTITY` + `VERIFICATION_CLI` | -| | [./src/verification/verifications/callout/start-callout.ts](./src/verification/verifications/callout/start-callout.ts) | `VERIFICATION_IDENTITY` | -| | [./src/verification/verifications/callout/report-with-id_callout.ts](./src/verification/verifications/callout/report-with-id_callout.ts) | `VERIFICATION_ID` + `VERIFICATION_CODE` | -| | [./src/verification/verifications/callout/report-with-identity_callout.ts](./src/verification/verifications/callout/report-with-identity_callout.ts) | `VERIFICATION_IDENTITY` + `VERIFICATION_CODE` | -| Verification-status | [./src/verification/verification-status/verification-by-id.ts](./src/verification/verification-status/verification-by-id.ts) | `VERIFICATION_ID` | -| | [./src/verification/verification-status/verification-by-identity.ts](./src/verification/verification-status/verification-by-identity.ts) | `VERIFICATION_IDENTITY` | -| | [./src/verification/verification-status/verification-by-reference.ts](./src/verification/verification-status/verification-by-reference.ts) | `VERIFICATION_REFERENCE` | +| Service | Sample application name and location | Required parameters | +|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| +| Start Verifications | [./src/verification/verifications/sms/start-sms.ts](./src/verification/verifications/sms/start-sms.ts) | `VERIFICATION_IDENTITY` | +| | [./src/verification/verifications/phonecall/start-phonecall.ts](./src/verification/verifications/phonecall/start-phonecall.ts) | `VERIFICATION_IDENTITY` | +| | [./src/verification/verifications/flashcall/start-flashcall.ts](./src/verification/verifications/flashcall/start-flashcall.ts) | `VERIFICATION_IDENTITY` | +| | [./src/verification/verifications/data/start-data.ts](./src/verification/verifications/data/start-data.ts) | `VERIFICATION_IDENTITY` | +| Verifications | [./src/verification/verifications/sms/report-with-id_sms.ts](./src/verification/verifications/sms/report-with-id_sms.ts) | `VERIFICATION_ID` + `VERIFICATION_CODE` | +| | [./src/verification/verifications/sms/report-with-identity_sms.ts](./src/verification/verifications/sms/report-with-identity_sms.ts) | `VERIFICATION_IDENTITY` + `VERIFICATION_CODE` | +| | [./src/verification/verifications/flashcall/report-with-id_flashcall.ts](./src/verification/verifications/flashcall/report-with-id_flashcall.ts) | `VERIFICATION_ID` + `VERIFICATION_CLI` | +| | [./src/verification/verifications/flashcall/report-with-identity_flashcall.ts](./src/verification/verifications/flashcall/report-with-identity_flashcall.ts) | `VERIFICATION_IDENTITY` + `VERIFICATION_CLI` | +| | [./src/verification/verifications/callout/report-with-id_callout.ts](./src/verification/verifications/phonecall/report-with-id_callout.ts) | `VERIFICATION_ID` + `VERIFICATION_CODE` | +| | [./src/verification/verifications/callout/report-with-identity_callout.ts](./src/verification/verifications/phonecall/report-with-identity_callout.ts) | `VERIFICATION_IDENTITY` + `VERIFICATION_CODE` | +| Verification-status | [./src/verification/verification-status/verification-by-id.ts](./src/verification/verification-status/verification-by-id.ts) | `VERIFICATION_ID` | +| | [./src/verification/verification-status/verification-by-identity.ts](./src/verification/verification-status/verification-by-identity.ts) | `VERIFICATION_IDENTITY` | +| | [./src/verification/verification-status/verification-by-reference.ts](./src/verification/verification-status/verification-by-reference.ts) | `VERIFICATION_REFERENCE` | ### Voice @@ -255,25 +256,27 @@ yarn run numbers:regions:list ### Fax -| Service | Sample application name and location | Required parameters | -|----------|----------------------------------------------------------------------------------------|-----------------------------------| -| Services | [./src/fax/services/create.ts](./src/fax/services/create.ts) | `PHONE_NUMBER` | -| | [./src/fax/services/get.ts](./src/fax/services/get.ts) | `FAX_SERVICE_ID` | -| | [./src/fax/services/list.ts](./src/fax/services/list.ts) | | -| | [./src/fax/services/listNumbers.ts](./src/fax/services/listNumbers.ts) | `FAX_SERVICE_ID` | -| | [./src/fax/services/listEmailsForNumber.ts](./src/fax/services/listEmailsForNumber.ts) | `PHONE_NUMBER` + `FAX_SERVICE_ID` | -| | [./src/fax/services/update.ts](./src/fax/services/update.ts) | `FAX_SERVICE_ID` | -| | [./src/fax/services/delete.ts](./src/fax/services/delete.ts) | `FAX_SERVICE_ID` | -| Faxes | [./src/fax/faxes/send.ts](./src/fax/faxes/send.ts) | `PHONE_NUMBER` | -| | [./src/fax/faxes/get.ts](./src/fax/faxes/get.ts) | `FAX_ID` | -| | [./src/fax/faxes/list.ts](./src/fax/faxes/list.ts) | | -| | [./src/fax/faxes/downloadContent.ts](./src/fax/faxes/downloadContent.ts) | `FAX_ID` | -| | [./src/fax/faxes/deleteContent.ts](./src/fax/faxes/deleteContent.ts) | `FAX_ID` | -| Emails | [./src/fax/emails/add.ts](./src/fax/emails/add.ts) | `FAX_EMAIL` + `PHONE_NUMBER` | -| | [./src/fax/emails/list.ts](./src/fax/emails/list.ts) | | -| | [./src/fax/emails/listNumbers.ts](./src/fax/emails/listNumbers.ts) | `FAX_EMAIL` | -| | [./src/fax/emails/update.ts](./src/fax/emails/update.ts) | `FAX_EMAIL` + `PHONE_NUMBER` | -| | [./src/fax/emails/delete.ts](./src/fax/emails/delete.ts) | `FAX_EMAIL` | +| Service | Sample application name and location | Required parameters | +|----------|------------------------------------------------------------------------------------------|-------------------------------------| +| Services | [./src/fax/services/create.ts](./src/fax/services/create.ts) | `PHONE_NUMBER` | +| | [./src/fax/services/get.ts](./src/fax/services/get.ts) | `FAX_SERVICE_ID` | +| | [./src/fax/services/list.ts](./src/fax/services/list.ts) | | +| | [./src/fax/services/listNumbers.ts](./src/fax/services/listNumbers.ts) | `FAX_SERVICE_ID` | +| | [./src/fax/services/listEmailsForNumber.ts](./src/fax/services/listEmailsForNumber.ts) | `PHONE_NUMBER` + `FAX_SERVICE_ID` | +| | [./src/fax/services/update.ts](./src/fax/services/update.ts) | `FAX_SERVICE_ID` | +| | [./src/fax/services/delete.ts](./src/fax/services/delete.ts) | `FAX_SERVICE_ID` | +| Faxes | [./src/fax/faxes/send-filePaths.ts](./src/fax/faxes/send-filePaths.ts) | `PHONE_NUMBER` + `FAX_CALLBACK_URL` | +| | [./src/fax/faxes/send-fileBase64.ts](./src/fax/faxes/send-fileBase64.ts) | `PHONE_NUMBER` + `FAX_CALLBACK_URL` | +| | [./src/fax/faxes/send-multipleRecipients.ts](./src/fax/faxes/send-multipleRecipients.ts) | `PHONE_NUMBER` + `FAX_CALLBACK_URL` | +| | [./src/fax/faxes/get.ts](./src/fax/faxes/get.ts) | `FAX_ID` | +| | [./src/fax/faxes/list.ts](./src/fax/faxes/list.ts) | | +| | [./src/fax/faxes/downloadContent.ts](./src/fax/faxes/downloadContent.ts) | `FAX_ID` | +| | [./src/fax/faxes/deleteContent.ts](./src/fax/faxes/deleteContent.ts) | `FAX_ID` | +| Emails | [./src/fax/emails/add.ts](./src/fax/emails/add.ts) | `FAX_EMAIL` + `PHONE_NUMBER` | +| | [./src/fax/emails/list.ts](./src/fax/emails/list.ts) | | +| | [./src/fax/emails/listNumbers.ts](./src/fax/emails/listNumbers.ts) | `FAX_EMAIL` | +| | [./src/fax/emails/update.ts](./src/fax/emails/update.ts) | `FAX_EMAIL` + `PHONE_NUMBER` | +| | [./src/fax/emails/delete.ts](./src/fax/emails/delete.ts) | `FAX_EMAIL` | ### Elastic SIP Trunk diff --git a/examples/simple-examples/fax-pdf/you-faxed.pdf b/examples/simple-examples/fax-pdf/you-faxed.pdf new file mode 100644 index 00000000..a1564289 Binary files /dev/null and b/examples/simple-examples/fax-pdf/you-faxed.pdf differ diff --git a/examples/simple-examples/package.json b/examples/simple-examples/package.json index c23c1932..936e28e8 100644 --- a/examples/simple-examples/package.json +++ b/examples/simple-examples/package.json @@ -106,7 +106,9 @@ "fax:services:listEmailsForNumber": "ts-node src/fax/services/listEmailsForNumber.ts", "fax:services:update": "ts-node src/fax/services/update.ts", "fax:services:delete": "ts-node src/fax/services/delete.ts", - "fax:faxes:send": "ts-node src/fax/faxes/send.ts", + "fax:faxes:send-filePaths": "ts-node src/fax/faxes/send-filePaths.ts", + "fax:faxes:send-fileBase64": "ts-node src/fax/faxes/send-fileBase64.ts", + "fax:faxes:send-multipleRecipients": "ts-node src/fax/faxes/send-multipleRecipients.ts", "fax:faxes:get": "ts-node src/fax/faxes/get.ts", "fax:faxes:list": "ts-node src/fax/faxes/list.ts", "fax:faxes:download": "ts-node src/fax/faxes/downloadContent.ts", @@ -151,14 +153,14 @@ "sms:inbounds:list": "ts-node src/sms/inbounds/list.ts", "sms:inbounds:get": "ts-node src/sms/inbounds/get.ts", "verification:verifications:start-sms": "ts-node src/verification/verifications/sms/start-sms.ts", - "verification:verifications:start-callout": "ts-node src/verification/verifications/callout/start-callout.ts", + "verification:verifications:start-phonecall": "ts-node src/verification/verifications/phonecall/start-phonecall.ts", "verification:verifications:start-flashcall": "ts-node src/verification/verifications/flashcall/start-flashcall.ts", - "verification:verifications:start-seamless": "ts-node src/verification/verifications/seamless/start-seamless.ts", + "verification:verifications:start-data": "ts-node src/verification/verifications/data/start-data.ts", "verification:verifications:report-with-id:sms": "ts-node src/verification/verifications/sms/report-with-id_sms.ts", - "verification:verifications:report-with-id:callout": "ts-node src/verification/verifications/callout/report-with-id_callout.ts", + "verification:verifications:report-with-id:callout": "ts-node src/verification/verifications/phonecall/report-with-id_callout.ts", "verification:verifications:report-with-id:flashcall": "ts-node src/verification/verifications/flashcall/report-with-id_flashcall.ts", "verification:verifications:report-with-identity:sms": "ts-node src/verification/verifications/sms/report-with-identity_sms.ts", - "verification:verifications:report-with-identity:callout": "ts-node src/verification/verifications/callout/report-with-identity_callout.ts", + "verification:verifications:report-with-identity:callout": "ts-node src/verification/verifications/phonecall/report-with-identity_callout.ts", "verification:verifications:report-with-identity:flashcall": "ts-node src/verification/verifications/flashcall/report-with-identity_flashcall.ts", "verification:verification-status:verification-by-id": "ts-node src/verification/verification-status/verification-by-id.ts", "verification:verification-status:verification-by-identity": "ts-node src/verification/verification-status/verification-by-identity.ts", @@ -181,7 +183,7 @@ "voice:conferences:kickAll": "ts-node src/voice/conferences/kickAll.ts" }, "dependencies": { - "@sinch/sdk-core": "^1.1.0", + "@sinch/sdk-core": "^1.2.0", "dotenv": "^16.3.1" }, "devDependencies": { diff --git a/examples/simple-examples/src/elastic-sip-trunking/calls-history/find.ts b/examples/simple-examples/src/elastic-sip-trunking/calls-history/find.ts index 10dcddfe..18e21eef 100644 --- a/examples/simple-examples/src/elastic-sip-trunking/calls-history/find.ts +++ b/examples/simple-examples/src/elastic-sip-trunking/calls-history/find.ts @@ -27,7 +27,7 @@ const populateCallsList = ( const requestData: ElasticSipTrunking.FindCallsRequestData = { trunkId, callResult: 'COMPLETED', - direction: 'INBOUND', + direction: 'inbound', }; const elasticSipTrunkingService = initElasticSipTrunkingService(); diff --git a/examples/simple-examples/src/fax/faxes/get.ts b/examples/simple-examples/src/fax/faxes/get.ts index b582737a..93bf2a1d 100644 --- a/examples/simple-examples/src/fax/faxes/get.ts +++ b/examples/simple-examples/src/fax/faxes/get.ts @@ -20,7 +20,7 @@ import { getFaxIdFromConfig, getPrintFormat, initFaxService, printFullResponse } if (printFormat === 'pretty') { console.log(`Fax found: it has been created at '${response.createTime}' and the status is '${response.status}'`); if (response.status === 'FAILURE') { - console.log(`Error type: ${response.errorType} (${response.errorId}): ${response.errorCode}`); + console.log(`Error type: ${response.errorType} (${response.errorCode}): ${response.errorMessage}`); } } else { printFullResponse(response); diff --git a/examples/simple-examples/src/fax/faxes/list.ts b/examples/simple-examples/src/fax/faxes/list.ts index cba310e8..c9cbb365 100644 --- a/examples/simple-examples/src/fax/faxes/list.ts +++ b/examples/simple-examples/src/fax/faxes/list.ts @@ -17,8 +17,42 @@ const populateFaxesList = ( console.log('* getFaxes *'); console.log('************'); + const today = new Date(); + const previousMonth = (today.getUTCMonth() - 1) % 12; + const lastMonth = new Date().setMonth(previousMonth); + + // Example of createTime filter + // - fetch last month's faxes (with Date objects) + // => Output = ?createTime>=2024-04-24T11:33:44Z&createTime<=2024-05-24T11:33:44Z + // createTimeFilter: { + // from: new Date(lastMonth), + // to: new Date(), + // } + // - fetch last month's faxes (with DateFormat objets) + // => Output = ?createTime>=2024-04-24&createTime<=2024-05-24 + // createTimeFilter: { + // from: { + // date: new Date(lastMonth), + // unit: 'day', + // }, + // to: { + // date: new Date(), + // unit: 'day', + // }, + // } + const requestData: Fax.ListFaxesRequestData = { - pageSize: 2, + pageSize: 10, + createTimeRange: { + from: { + date: new Date(lastMonth), + unit: 'day', + }, + to: { + date: new Date(), + unit: 'day', + }, + }, }; const faxService = initFaxService(); diff --git a/examples/simple-examples/src/fax/faxes/send-fileBase64.ts b/examples/simple-examples/src/fax/faxes/send-fileBase64.ts new file mode 100644 index 00000000..4e69d11a --- /dev/null +++ b/examples/simple-examples/src/fax/faxes/send-fileBase64.ts @@ -0,0 +1,60 @@ +import { Fax } from '@sinch/sdk-core'; +import { + getFaxCallbackUrlFromConfig, + getPhoneNumberFromConfig, + getPrintFormat, + initFaxService, + printFullResponse, +} from '../../config'; +import * as fs from 'fs'; + +const getBase64 = (filePath: string): Fax.FaxBase64File => { + const fileExtension = filePath.split('.').pop()?.toUpperCase(); + try { + const fileBuffer = fs.readFileSync(filePath); + return { + file: fileBuffer.toString('base64'), + fileType: Fax.convertToSupportedFileType(fileExtension), + }; + } catch (error) { + console.error('Error reading or converting the file:', error); + throw error; + } +}; + +(async () => { + console.log('*************************'); + console.log('* sendFax - base64 file *'); + console.log('*************************'); + + const originPhoneNumber = getPhoneNumberFromConfig(); + const destinationPhoneNumber = getPhoneNumberFromConfig(); + const faxCallbackUrl = getFaxCallbackUrlFromConfig(); + + const requestData: Fax.SendSingleFaxRequestData = { + sendFaxRequestBody: { + to: destinationPhoneNumber, + from: originPhoneNumber, + contentUrl: 'https://developers.sinch.com/', + files: [getBase64('./fax-pdf/you-faxed.pdf')], + callbackUrl: faxCallbackUrl, + callbackUrlContentType: 'application/json', + imageConversionMethod: 'MONOCHROME', + headerTimeZone: 'Europe/Paris', + headerText: ' - Sent with the Node.js SDK', + maxRetries: 2, + }, + }; + + const faxService = initFaxService(); + const response = await faxService.faxes.send(requestData); + + const printFormat = getPrintFormat(process.argv); + + if (printFormat === 'pretty') { + const fax = response[0]; + console.log(`Fax successfully created at '${fax.createTime?.toISOString()}'. Status = '${fax.status}'`); + } else { + printFullResponse(response); + } +})(); diff --git a/examples/simple-examples/src/fax/faxes/send-filePaths.ts b/examples/simple-examples/src/fax/faxes/send-filePaths.ts new file mode 100644 index 00000000..a7180c0e --- /dev/null +++ b/examples/simple-examples/src/fax/faxes/send-filePaths.ts @@ -0,0 +1,45 @@ +import { Fax } from '@sinch/sdk-core'; +import { + getFaxCallbackUrlFromConfig, + getPhoneNumberFromConfig, + getPrintFormat, + initFaxService, + printFullResponse, +} from '../../config'; + +(async () => { + console.log('**********************'); + console.log('* sendFax - filePath *'); + console.log('**********************'); + + const originPhoneNumber = getPhoneNumberFromConfig(); + const destinationPhoneNumber = getPhoneNumberFromConfig(); + const faxCallbackUrl = getFaxCallbackUrlFromConfig(); + + const requestData: Fax.SendSingleFaxRequestData = { + sendFaxRequestBody: { + to: destinationPhoneNumber, + from: originPhoneNumber, + contentUrl: 'https://developers.sinch.com/', + filePaths: ['./fax-pdf/you-faxed.pdf', './fax-pdf/you-faxed.pdf'], + callbackUrl: faxCallbackUrl, + callbackUrlContentType: 'application/json', + imageConversionMethod: 'MONOCHROME', + headerTimeZone: 'Europe/Paris', + headerText: ' - Sent with the Node.js SDK', + maxRetries: 2, + }, + }; + + const faxService = initFaxService(); + const response = await faxService.faxes.send(requestData); + + const printFormat = getPrintFormat(process.argv); + + if (printFormat === 'pretty') { + const fax = response[0]; + console.log(`Fax successfully created at '${fax.createTime?.toISOString()}'. Status = '${fax.status}'`); + } else { + printFullResponse(response); + } +})(); diff --git a/examples/simple-examples/src/fax/faxes/send.ts b/examples/simple-examples/src/fax/faxes/send-multipleRecipients.ts similarity index 54% rename from examples/simple-examples/src/fax/faxes/send.ts rename to examples/simple-examples/src/fax/faxes/send-multipleRecipients.ts index 221c9d92..0e94bf95 100644 --- a/examples/simple-examples/src/fax/faxes/send.ts +++ b/examples/simple-examples/src/fax/faxes/send-multipleRecipients.ts @@ -8,21 +8,25 @@ import { } from '../../config'; (async () => { - console.log('***********'); - console.log('* sendFax *'); - console.log('***********'); + console.log('*********************************'); + console.log('* sendFax - multiple recipients *'); + console.log('*********************************'); const originPhoneNumber = getPhoneNumberFromConfig(); const destinationPhoneNumber = getPhoneNumberFromConfig(); const faxCallbackUrl = getFaxCallbackUrlFromConfig(); - const requestData: Fax.SendFaxRequestData = { + const requestData: Fax.SendMultipleFaxRequestData = { sendFaxRequestBody: { - to: destinationPhoneNumber, + to: [destinationPhoneNumber, destinationPhoneNumber], from: originPhoneNumber, contentUrl: 'https://developers.sinch.com/fax/fax.pdf', callbackUrl: faxCallbackUrl, - callbackContentType: 'application/json', + callbackUrlContentType: 'application/json', + imageConversionMethod: 'MONOCHROME', + headerTimeZone: 'Europe/Paris', + headerText: ' - Sent with the Node.js SDK', + maxRetries: 2, }, }; @@ -32,7 +36,7 @@ import { const printFormat = getPrintFormat(process.argv); if (printFormat === 'pretty') { - console.log(`Fax successfully created at '${response.createTime?.toISOString()}'. Status = '${response.status}'`); + console.log(`${response.length} fax(es) successfully created.\n${response.map((fax) => ' - ' + fax.id + ' created at ' + fax.createTime?.toISOString()).join('\n')}`); } else { printFullResponse(response); } diff --git a/examples/simple-examples/src/numbers/active/get.ts b/examples/simple-examples/src/numbers/active/get.ts index d3324cd6..40119992 100644 --- a/examples/simple-examples/src/numbers/active/get.ts +++ b/examples/simple-examples/src/numbers/active/get.ts @@ -20,7 +20,7 @@ import { Numbers } from '@sinch/sdk-core'; const numbersService = initNumbersService(); let response; try { - response = await numbersService.activeNumber.get(requestData); + response = await numbersService.get(requestData); } catch (error) { console.error(`ERROR: The phone number ${requestData.phoneNumber} is not active`); throw error; diff --git a/examples/simple-examples/src/numbers/active/list.ts b/examples/simple-examples/src/numbers/active/list.ts index 4291d7b2..8dc38296 100644 --- a/examples/simple-examples/src/numbers/active/list.ts +++ b/examples/simple-examples/src/numbers/active/list.ts @@ -30,7 +30,7 @@ const populateActiveNumbersList = ( // ---------------------------------------------- // Method 1: Fetch the data page by page manually // ---------------------------------------------- - let response = await numbersService.activeNumber.list(requestData); + let response = await numbersService.list(requestData); const activeNumbersList: Numbers.ActiveNumber[] = []; const phoneNumbersList: (string | undefined)[] = []; @@ -59,7 +59,7 @@ const populateActiveNumbersList = ( // --------------------------------------------------------------------- // Method 2: Use the iterator and fetch data on more pages automatically // --------------------------------------------------------------------- - for await (const activeNumber of numbersService.activeNumber.list(requestData)) { + for await (const activeNumber of numbersService.list(requestData)) { if (printFormat === 'pretty') { console.log(`${activeNumber.phoneNumber} - Voice Configuration: ${activeNumber.voiceConfiguration?.type}`); } else { diff --git a/examples/simple-examples/src/numbers/active/release.ts b/examples/simple-examples/src/numbers/active/release.ts index 372b86d3..0f9c29f6 100644 --- a/examples/simple-examples/src/numbers/active/release.ts +++ b/examples/simple-examples/src/numbers/active/release.ts @@ -18,7 +18,7 @@ import { Numbers } from '@sinch/sdk-core'; }; const numbersService = initNumbersService(); - const response = await numbersService.activeNumber.release(requestData); + const response = await numbersService.release(requestData); const printFormat = getPrintFormat(process.argv); diff --git a/examples/simple-examples/src/numbers/active/update.ts b/examples/simple-examples/src/numbers/active/update.ts index d3a8c26c..026a7cd3 100644 --- a/examples/simple-examples/src/numbers/active/update.ts +++ b/examples/simple-examples/src/numbers/active/update.ts @@ -1,4 +1,5 @@ import { + getFaxServiceIdFromConfig, getNumberCallbackUrlFromConfig, getPhoneNumberFromConfig, getPrintFormat, @@ -16,6 +17,7 @@ import { Numbers } from '@sinch/sdk-core'; const phoneNumber = getPhoneNumberFromConfig(); const servicePlanId = getServicePlanIdFromConfig(); const callbackUrl = getNumberCallbackUrlFromConfig(); + const faxServiceId = getFaxServiceIdFromConfig(); const requestData: Numbers.UpdateActiveNumberRequestData= { phoneNumber, @@ -24,12 +26,16 @@ import { Numbers } from '@sinch/sdk-core'; smsConfiguration: { servicePlanId, }, + voiceConfiguration: { + type: 'FAX', + serviceId: faxServiceId, + }, callbackUrl, }, }; const numbersService = initNumbersService(); - const response = await numbersService.activeNumber.update(requestData); + const response = await numbersService.update(requestData); const printFormat = getPrintFormat(process.argv); diff --git a/examples/simple-examples/src/numbers/available/checkAvailability.ts b/examples/simple-examples/src/numbers/available/checkAvailability.ts index 43f1cdab..801553fe 100644 --- a/examples/simple-examples/src/numbers/available/checkAvailability.ts +++ b/examples/simple-examples/src/numbers/available/checkAvailability.ts @@ -21,7 +21,7 @@ import { Numbers } from '@sinch/sdk-core'; const numbersService = initNumbersService(); let response; try { - response = await numbersService.availableNumber.checkAvailability(requestData); + response = await numbersService.checkAvailability(requestData); } catch (error) { console.error(`ERROR: the phone number ${requestData.phoneNumber} is not available`); } diff --git a/examples/simple-examples/src/numbers/available/list.ts b/examples/simple-examples/src/numbers/available/list.ts index e375c918..c8e21954 100644 --- a/examples/simple-examples/src/numbers/available/list.ts +++ b/examples/simple-examples/src/numbers/available/list.ts @@ -13,7 +13,7 @@ import { Numbers } from '@sinch/sdk-core'; }; const numbersService = initNumbersService(); - const response = await numbersService.availableNumber.list(requestData); + const response = await numbersService.searchForAvailableNumbers(requestData); const printFormat = getPrintFormat(process.argv); diff --git a/examples/simple-examples/src/numbers/available/rent.ts b/examples/simple-examples/src/numbers/available/rent.ts index c3b3cc92..0144fce2 100644 --- a/examples/simple-examples/src/numbers/available/rent.ts +++ b/examples/simple-examples/src/numbers/available/rent.ts @@ -38,7 +38,7 @@ import { Numbers } from '@sinch/sdk-core'; }; const numbersService = initNumbersService(); - const response = await numbersService.availableNumber.rent(requestData); + const response = await numbersService.rent(requestData); const printFormat = getPrintFormat(process.argv); diff --git a/examples/simple-examples/src/numbers/available/rentAny.ts b/examples/simple-examples/src/numbers/available/rentAny.ts index 8cf8678c..962a774f 100644 --- a/examples/simple-examples/src/numbers/available/rentAny.ts +++ b/examples/simple-examples/src/numbers/available/rentAny.ts @@ -45,7 +45,7 @@ import { Numbers } from '@sinch/sdk-core'; }; const numbersService = initNumbersService(); - const response = await numbersService.availableNumber.rentAny(requestData); + const response = await numbersService.rentAny(requestData); const printFormat = getPrintFormat(process.argv); diff --git a/examples/simple-examples/src/sms/delivery-reports/getForNumber.ts b/examples/simple-examples/src/sms/delivery-reports/getForNumber.ts index e8b170aa..4a57f9c6 100644 --- a/examples/simple-examples/src/sms/delivery-reports/getForNumber.ts +++ b/examples/simple-examples/src/sms/delivery-reports/getForNumber.ts @@ -17,7 +17,7 @@ import { Sms } from '@sinch/sdk-core'; const requestData: Sms.GetDeliveryReportByPhoneNumberRequestData = { batch_id: batchId, - recipient_msisdn: recipientPhoneNumber, + phone_number: recipientPhoneNumber, }; const smsService = initSmsServiceWithServicePlanId(); @@ -25,7 +25,7 @@ import { Sms } from '@sinch/sdk-core'; try { response = await smsService.deliveryReports.getForNumber(requestData); } catch (error) { - console.error(`ERROR: Impossible to retrieve the delivery report by batch ID ${requestData.batch_id} for the recipient ${requestData.recipient_msisdn}`); + console.error(`ERROR: Impossible to retrieve the delivery report by batch ID ${requestData.batch_id} for the recipient ${requestData.phone_number}`); throw error; } diff --git a/examples/simple-examples/src/sms/groups/list/list.ts b/examples/simple-examples/src/sms/groups/list/list.ts index da28f28d..0d635738 100644 --- a/examples/simple-examples/src/sms/groups/list/list.ts +++ b/examples/simple-examples/src/sms/groups/list/list.ts @@ -6,14 +6,14 @@ import { import { getPrintFormat, printFullResponse } from '../../../config'; const populateGroupsList = ( - groupsPage: PageResult, - fullGroupsList: Sms.CreateGroupResponse[], + groupsPage: PageResult, + fullGroupsList: Sms.Group[], groupsList: string[], ) => { // Populate the data structure that holds the response content fullGroupsList.push(...groupsPage.data); // Populate the data structure that holds the response content for pretty print - groupsPage.data.map((group: Sms.CreateGroupResponse) => { + groupsPage.data.map((group: Sms.Group) => { groupsList.push(`Group ID: ${group.id} - Group name: ${group.name}`); }); }; @@ -39,7 +39,7 @@ export const list = async(smsService: SmsService) => { } // Init data structure to hold the response content - const fullGroupsList: Sms.CreateGroupResponse[] = []; + const fullGroupsList: Sms.Group[] = []; // Init data structure to hold the response content for pretty print const groupsList: string[] = []; diff --git a/examples/simple-examples/src/verification/verifications/data/start-data.ts b/examples/simple-examples/src/verification/verifications/data/start-data.ts new file mode 100644 index 00000000..3c0f2f7b --- /dev/null +++ b/examples/simple-examples/src/verification/verifications/data/start-data.ts @@ -0,0 +1,32 @@ +import { Verification } from '@sinch/sdk-core'; +import { + getPrintFormat, + getVerificationIdentityFromConfig, + initVerificationService, + printFullResponse, +} from '../../../config'; + +(async () => { + console.log('****************************'); + console.log('* StartVerification - data *'); + console.log('****************************'); + + const verificationIdentity = getVerificationIdentityFromConfig(); + + const requestData = Verification.startVerificationHelper.buildDataRequest( + verificationIdentity, + `test-reference-for-seamless-verification_${verificationIdentity}`, + ); + + const verificationService = initVerificationService(); + const response = await verificationService.verifications.startData(requestData); + + const printFormat = getPrintFormat(process.argv); + + if (printFormat === 'pretty') { + console.log(`Verification ID = ${response.id}`); + console.log(`Data verification specific field: targetUri = ${response.seamless?.targetUri}`); + } else { + printFullResponse(response); + } +})(); diff --git a/examples/simple-examples/src/verification/verifications/seamless/start-seamless.ts b/examples/simple-examples/src/verification/verifications/data/start-seamless.ts similarity index 86% rename from examples/simple-examples/src/verification/verifications/seamless/start-seamless.ts rename to examples/simple-examples/src/verification/verifications/data/start-seamless.ts index c72e5555..e0202ba9 100644 --- a/examples/simple-examples/src/verification/verifications/seamless/start-seamless.ts +++ b/examples/simple-examples/src/verification/verifications/data/start-seamless.ts @@ -6,6 +6,7 @@ import { printFullResponse, } from '../../../config'; +/** @deprecated see ./start-data.ts instead */ (async () => { console.log('********************************'); console.log('* StartVerification - seamless *'); @@ -25,7 +26,7 @@ import { if (printFormat === 'pretty') { console.log(`Verification ID = ${response.id}`); - console.log(`Seamless verification specific field: template = ${response.seamless?.targetUri}`); + console.log(`Seamless verification specific field: targetUri = ${response.seamless?.targetUri}`); } else { printFullResponse(response); } diff --git a/examples/simple-examples/src/verification/verifications/callout/report-with-id_callout.ts b/examples/simple-examples/src/verification/verifications/phonecall/report-with-id_callout.ts similarity index 94% rename from examples/simple-examples/src/verification/verifications/callout/report-with-id_callout.ts rename to examples/simple-examples/src/verification/verifications/phonecall/report-with-id_callout.ts index 60575953..1bdaefa8 100644 --- a/examples/simple-examples/src/verification/verifications/callout/report-with-id_callout.ts +++ b/examples/simple-examples/src/verification/verifications/phonecall/report-with-id_callout.ts @@ -7,6 +7,7 @@ import { printFullResponse, } from '../../../config'; +/** @deprecated see ./report-with-id_phonecall.ts instead */ (async () => { console.log('************************************'); console.log('* ReportVerificationById - callout *'); diff --git a/examples/simple-examples/src/verification/verifications/phonecall/report-with-id_phonecall.ts b/examples/simple-examples/src/verification/verifications/phonecall/report-with-id_phonecall.ts new file mode 100644 index 00000000..b4c6ec67 --- /dev/null +++ b/examples/simple-examples/src/verification/verifications/phonecall/report-with-id_phonecall.ts @@ -0,0 +1,32 @@ +import { Verification } from '@sinch/sdk-core'; +import { + getPrintFormat, + getVerificationCodeFromConfig, + getVerificationIdFromConfig, + initVerificationService, + printFullResponse, +} from '../../../config'; + +(async () => { + console.log('**************************************'); + console.log('* ReportVerificationById - phoneCall *'); + console.log('**************************************'); + + const verificationId = getVerificationIdFromConfig(); + const verificationCode = getVerificationCodeFromConfig(); + + const requestData = Verification.reportVerificationByIdHelper.buildPhoneCallRequest( + verificationId, verificationCode); + + const verificationService = initVerificationService(); + const response = await verificationService.verifications.reportPhoneCallById(requestData); + + const printFormat = getPrintFormat(process.argv); + + if (printFormat === 'pretty') { + console.log(`Phone call verification status: ${response.status}${response.status === 'SUCCESSFUL'?'':' - Reason: ' + response.reason}`); + } else { + printFullResponse(response); + } + +})(); diff --git a/examples/simple-examples/src/verification/verifications/callout/report-with-identity_callout.ts b/examples/simple-examples/src/verification/verifications/phonecall/report-with-identity_callout.ts similarity index 94% rename from examples/simple-examples/src/verification/verifications/callout/report-with-identity_callout.ts rename to examples/simple-examples/src/verification/verifications/phonecall/report-with-identity_callout.ts index 23662c77..0ae33e59 100644 --- a/examples/simple-examples/src/verification/verifications/callout/report-with-identity_callout.ts +++ b/examples/simple-examples/src/verification/verifications/phonecall/report-with-identity_callout.ts @@ -7,6 +7,7 @@ import { printFullResponse, } from '../../../config'; +/** @deprecated see ./report-with-identity_phonecall.ts instead */ (async () => { console.log('******************************************'); console.log('* ReportVerificationByIdentity - callout *'); diff --git a/examples/simple-examples/src/verification/verifications/phonecall/report-with-identity_phonecall.ts b/examples/simple-examples/src/verification/verifications/phonecall/report-with-identity_phonecall.ts new file mode 100644 index 00000000..b677c8d0 --- /dev/null +++ b/examples/simple-examples/src/verification/verifications/phonecall/report-with-identity_phonecall.ts @@ -0,0 +1,32 @@ +import { Verification } from '@sinch/sdk-core'; +import { + getPrintFormat, + getVerificationCodeFromConfig, + getVerificationIdentityFromConfig, + initVerificationService, + printFullResponse, +} from '../../../config'; + +(async () => { + console.log('********************************************'); + console.log('* ReportVerificationByIdentity - phoneCall *'); + console.log('********************************************'); + + const verificationIdentity = getVerificationIdentityFromConfig(); + const verificationCode = getVerificationCodeFromConfig(); + + const requestData = Verification.reportVerificationByIdentityHelper.buildPhoneCallRequest( + verificationIdentity, verificationCode); + + const verificationService = initVerificationService(); + const response = await verificationService.verifications.reportPhoneCallByIdentity(requestData); + + const printFormat = getPrintFormat(process.argv); + + if (printFormat === 'pretty') { + console.log(`Phone call verification status: ${response.status}${response.status === 'SUCCESSFUL'?'':' - Reason: ' + response.reason}`); + } else { + printFullResponse(response); + } + +})(); diff --git a/examples/simple-examples/src/verification/verifications/callout/start-callout.ts b/examples/simple-examples/src/verification/verifications/phonecall/start-callout.ts similarity index 94% rename from examples/simple-examples/src/verification/verifications/callout/start-callout.ts rename to examples/simple-examples/src/verification/verifications/phonecall/start-callout.ts index 1a0a6ddb..d5a052a5 100644 --- a/examples/simple-examples/src/verification/verifications/callout/start-callout.ts +++ b/examples/simple-examples/src/verification/verifications/phonecall/start-callout.ts @@ -6,6 +6,7 @@ import { printFullResponse, } from '../../../config'; +/** @deprecated see ./start-phonecall.ts instead */ (async () => { console.log('*******************************'); console.log('* StartVerification - callout *'); diff --git a/examples/simple-examples/src/verification/verifications/phonecall/start-phonecall.ts b/examples/simple-examples/src/verification/verifications/phonecall/start-phonecall.ts new file mode 100644 index 00000000..27f5c7b7 --- /dev/null +++ b/examples/simple-examples/src/verification/verifications/phonecall/start-phonecall.ts @@ -0,0 +1,31 @@ +import { Verification } from '@sinch/sdk-core'; +import { + getPrintFormat, + getVerificationIdentityFromConfig, + initVerificationService, + printFullResponse, +} from '../../../config'; + +(async () => { + console.log('**********************************'); + console.log('* StartVerification - phoneCall *'); + console.log('**********************************'); + + const verificationIdentity = getVerificationIdentityFromConfig(); + + const requestData = Verification.startVerificationHelper.buildPhoneCallRequest( + verificationIdentity, + `test-reference-for-callout-verification_${verificationIdentity}`, + ); + + const verificationService = initVerificationService(); + const response = await verificationService.verifications.startPhoneCall(requestData); + + const printFormat = getPrintFormat(process.argv); + + if (printFormat === 'pretty') { + console.log(`Verification ID = ${response.id}`); + } else { + printFullResponse(response); + } +})(); diff --git a/examples/simple-examples/src/verification/verifications/sms/start-sms.ts b/examples/simple-examples/src/verification/verifications/sms/start-sms.ts index 846413a7..75abfea2 100644 --- a/examples/simple-examples/src/verification/verifications/sms/start-sms.ts +++ b/examples/simple-examples/src/verification/verifications/sms/start-sms.ts @@ -16,6 +16,9 @@ import { const requestData = Verification.startVerificationHelper.buildSmsRequest( verificationIdentity, `test-reference-for-sms-verification_${verificationIdentity}`, + { + locale: 'sv-SE', + }, ); const verificationService = initVerificationService(); diff --git a/examples/webhooks/package.json b/examples/webhooks/package.json index 1e85d838..252af88e 100644 --- a/examples/webhooks/package.json +++ b/examples/webhooks/package.json @@ -12,10 +12,10 @@ "start:prod": "node dist/main" }, "dependencies": { - "@nestjs/common": "^10.3.7", - "@nestjs/core": "^10.3.7", - "@nestjs/platform-express": "^10.3.7", - "@sinch/sdk-core": "^1.1.0", + "@nestjs/common": "^10.4.4", + "@nestjs/core": "^10.4.4", + "@nestjs/platform-express": "^10.4.4", + "@sinch/sdk-core": "^1.2.0", "dotenv": "^16.3.1", "raw-body": "^2.5.2", "reflect-metadata": "^0.1.13", diff --git a/examples/webhooks/src/services/fax-event.service.ts b/examples/webhooks/src/services/fax-event.service.ts index c5858a6e..54160c91 100644 --- a/examples/webhooks/src/services/fax-event.service.ts +++ b/examples/webhooks/src/services/fax-event.service.ts @@ -15,9 +15,7 @@ export class FaxEventService { } if (event.event === 'FAX_COMPLETED') { const faxCompletedEvent = event as Fax.FaxCompletedEventJson; - for (const fileBase64 of faxCompletedEvent.files!) { - this.saveBase64File(fileBase64, event.fax!.id!); - } + this.saveBase64File(faxCompletedEvent, event.fax!.id!); } } else if (contentType?.includes('multipart/form-data')) { console.log(`** multipart/form-data\n${event.event}: ${event.fax!.id} - ${event.eventTime}`); @@ -28,10 +26,10 @@ export class FaxEventService { } } - private saveBase64File(fileBase64: Fax.FaxBase64File, faxId: string) { + private saveBase64File(event: Fax.IncomingFaxEventJson | Fax.FaxCompletedEventJson, faxId: string) { console.log('Saving file...'); - const filePath = path.join('./fax-upload', faxId + '.' + fileBase64.fileType!.toLowerCase()); - const buffer = Buffer.from(fileBase64.file!, 'base64'); + const filePath = path.join('./fax-upload', event.event + '-' + faxId + '.' + event.fileType!.toLowerCase()); + const buffer = Buffer.from(event.file!, 'base64'); fs.writeFileSync(filePath, buffer); console.log('File saved! ' + filePath); } diff --git a/examples/webhooks/src/services/sms-event.service.ts b/examples/webhooks/src/services/sms-event.service.ts index 53d6686e..0e318582 100644 --- a/examples/webhooks/src/services/sms-event.service.ts +++ b/examples/webhooks/src/services/sms-event.service.ts @@ -8,6 +8,8 @@ export class SmsEventService { console.log(`:: INCOMING EVENT :: ${event.type}`); if (event.type === 'delivery_report_sms' || event.type === 'delivery_report_mms') { return this.handleDeliveryReportEvent(event as Sms.DeliveryReport); + } else if (event.type === 'recipient_delivery_report_sms' || event.type === 'recipient_delivery_report_mms') { + return this.handleRecipientDeliveryReportEvent(event as Sms.RecipientDeliveryReport); } else if (event.type === 'mo_text') { return this.handleSmsEvent(event as Sms.MOText); } else if (event.type === 'mo_binary') { @@ -18,7 +20,11 @@ export class SmsEventService { } private handleDeliveryReportEvent(event: Sms.DeliveryReport): void { - console.log(`The batch ${event.batch_id} has the following statuses:\n${event.statuses.map((status) => ' - \'' + status.status + '\' for the recipients: ' + status.recipients.join(', ')).join('\n')} `); + console.log(`The batch ${event.batch_id} has the following statuses:\n${event.statuses.map((status) => ' - \'' + status.status + '\' for the recipients: ' + status.recipients?.join(', ')).join('\n')} `); + } + + private handleRecipientDeliveryReportEvent(event: Sms.RecipientDeliveryReport): void { + console.log(`The batch ${event.batch_id} has the status "${event.status}" (code: ${event.code}) for the recipient ${event.recipient}`); } private handleSmsEvent(event: Sms.MOText): void { diff --git a/examples/webhooks/src/services/verification-event.service.ts b/examples/webhooks/src/services/verification-event.service.ts index ca8ce9d7..73697f73 100644 --- a/examples/webhooks/src/services/verification-event.service.ts +++ b/examples/webhooks/src/services/verification-event.service.ts @@ -1,14 +1,11 @@ import { Injectable } from '@nestjs/common'; import { Response } from 'express'; -import { - Verification, - VerificationCallback, -} from '@sinch/sdk-core'; +import { Verification } from '@sinch/sdk-core'; @Injectable() export class VerificationEventService { - handleEvent(event: VerificationCallback, res: Response) { + handleEvent(event: Verification.VerificationCallbackEvent, res: Response) { console.log(`:: INCOMING EVENT :: ${event.event}`); switch (event.event) { case 'VerificationRequestEvent': @@ -23,7 +20,7 @@ export class VerificationEventService { private handleVerificationRequestEvent(event: Verification.VerificationRequestEvent, res: Response) { switch (event.method) { case 'sms': - const smsRequestEventResponse: Verification.SMSRequestEventResponse = { + const smsRequestEventResponse: Verification.SmsRequestEventResponse = { action: 'allow', sms: { code: '123456' @@ -32,7 +29,7 @@ export class VerificationEventService { res.status(200).json(smsRequestEventResponse); break; case 'callout': - const calloutRequestEventResponse: Verification.CalloutRequestEventResponse = { + const calloutRequestEventResponse: Verification.PhoneCallRequestEventResponse = { action: 'allow', callout: { code: '123456', diff --git a/examples/webhooks/src/services/voice-event.service.ts b/examples/webhooks/src/services/voice-event.service.ts index ba85be68..7c71600e 100644 --- a/examples/webhooks/src/services/voice-event.service.ts +++ b/examples/webhooks/src/services/voice-event.service.ts @@ -1,14 +1,11 @@ import { Injectable } from '@nestjs/common'; import { Response } from 'express'; -import { - VoiceCallback, - Voice, -} from '@sinch/sdk-core'; +import { Voice } from '@sinch/sdk-core'; @Injectable() export class VoiceEventService { - handleEvent(event: VoiceCallback, res: Response) { + handleEvent(event: Voice.VoiceCallbackEvent, res: Response) { console.log(`:: INCOMING EVENT :: ${event.event}`); switch (event.event) { case 'ice': diff --git a/package.json b/package.json index 680f7ac0..13870acf 100644 --- a/package.json +++ b/package.json @@ -14,14 +14,16 @@ "lerna": "lerna", "bootstrap": "npx lerna bootstrap", "build": "lerna run build", - "test": "jest" + "test": "jest", + "e2e": "lerna run test:e2e" }, "keywords": [], "devDependencies": { - "@babel/core": "^7.23.3", - "@babel/preset-env": "^7.23.3", - "@babel/preset-typescript": "^7.23.3", - "@types/jest": "^29.5.1", + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.4", + "@babel/preset-typescript": "^7.24.7", + "@cucumber/cucumber": "^10.3.1", + "@types/jest": "^29.5.13", "@typescript-eslint/eslint-plugin": "^6.9.0", "@typescript-eslint/parser": "^6.9.0", "babel-jest": "^29.7.0", @@ -33,8 +35,8 @@ "eslint-plugin-jest-extended": "^2.0.0", "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-prettier": "^5.0.1", - "jest": "^29.5.0", - "lerna": "^7.4.1" + "jest": "^29.7.0", + "lerna": "^7.4.2" }, "engines": { "node": ">=18.0.0" diff --git a/packages/conversation/CHANGELOG.md b/packages/conversation/CHANGELOG.md index 4b098f80..68ccf2df 100644 --- a/packages/conversation/CHANGELOG.md +++ b/packages/conversation/CHANGELOG.md @@ -1,3 +1,15 @@ +## Version 1.2.0 +- [Tech] Update dependency `@sinch/sdk-client` to `1.2.0`. +- [Bugfix] Fix issue with pagination to iterate over multiple pages. +- [Bugfix] `ListConversationsRequestData`: the property `only_active` becomes optional. +- [Bugfix] Template V2: add an optional `id` property to the `V2Template` interface. +- [Bugfix] `conversations.listRecent()`: Add a page_size value by default. Without it the API returns an empty list. +- [Bugfix][Breaking] `InjectConversationEvent` interface: only `AppEvent` is allowed (`ContactEvent` and `ContactMessageEvent` are no longer allowed). +- [Bugfix][Breaking] + - Template V2: For "create" and "update" operations, the request bodies interface no longer accept read-only properties. + - Webhooks: For "create" and "update" operations, the request bodies interface no longer accept read-only properties. +- [E2E] Add Cucumber steps implementation. + ## Version 1.1.0 - [Tech] Update dependency `@sinch/sdk-client` to `1.1.0` diff --git a/packages/conversation/cucumber.js b/packages/conversation/cucumber.js new file mode 100644 index 00000000..691a9809 --- /dev/null +++ b/packages/conversation/cucumber.js @@ -0,0 +1,8 @@ +module.exports = { + default: [ + 'tests/e2e/features/**/*.feature', + '--require-module ts-node/register', + '--require tests/rest/v1/**/*.steps.ts', + `--format-options '{"snippetInterface": "synchronous"}'`, + ].join(' '), +}; diff --git a/packages/conversation/package.json b/packages/conversation/package.json index a02dc56c..4a71052a 100644 --- a/packages/conversation/package.json +++ b/packages/conversation/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/conversation", - "version": "1.1.0", + "version": "1.2.0", "description": "Sinch Conversation API", "homepage": "", "repository": { @@ -25,13 +25,15 @@ "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", - "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo" + "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo", + "test:e2e": "cucumber-js" }, "dependencies": { - "@sinch/sdk-client": "^1.1.0" + "@sinch/sdk-client": "^1.2.0" }, "devDependencies": {}, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/conversation/src/models/v1/conversation/conversation.ts b/packages/conversation/src/models/v1/conversation/conversation.ts index 5a8fa064..49e607c8 100644 --- a/packages/conversation/src/models/v1/conversation/conversation.ts +++ b/packages/conversation/src/models/v1/conversation/conversation.ts @@ -20,8 +20,7 @@ export interface Conversation { /** * Arbitrary data set by the Conversation API clients. Up to 1024 characters long. * NOTE: This field has been deprecated due to changes in the system architecture or functionality. - * It is no longer actively maintained and may be removed in future versions. Please avoid relying on this field in new code. - * @deprecated + * @deprecated It is no longer actively maintained and may be removed in future versions. Please avoid relying on this field in new code. */ metadata?: string; /** Arbitrary data set by the Conversation API clients and/or provided in the `conversation_metadata` field of a SendMessageRequest. A valid JSON object. */ diff --git a/packages/conversation/src/models/v1/inject-conversation-event-request/inject-conversation-event-request.ts b/packages/conversation/src/models/v1/inject-conversation-event-request/inject-conversation-event-request.ts index 4f837a97..fcab6b5d 100644 --- a/packages/conversation/src/models/v1/inject-conversation-event-request/inject-conversation-event-request.ts +++ b/packages/conversation/src/models/v1/inject-conversation-event-request/inject-conversation-event-request.ts @@ -1,18 +1,13 @@ import { ChannelIdentity } from '../channel-identity'; import { AppEvent } from '../app-event'; -import { ContactEvent } from '../contact-event'; import { ProcessingMode } from '../enums'; -import { ContactMessageEvent } from '../contact-message-event'; /** * Inject Event request */ -export type InjectConversationEventRequest = - InjectConversationAppEvent - | InjectConversationContactEvent - | InjectConversationContactMessageEvent; - -interface InjectConversationEventBase { +export interface InjectConversationEventRequest { + /** @see AppEvent */ + app_event: AppEvent; /** Optional. The ID of the event\'s conversation. Will not be present for apps in Dispatch Mode. */ conversation_id?: string; /** Optional. The ID of the contact. Will not be present for apps in Dispatch Mode. */ @@ -24,27 +19,3 @@ interface InjectConversationEventBase { /** Whether or not Conversation API should store contacts and conversations for the app. For more information, see [Processing Modes](../../../../../conversation/processing-modes/). */ processing_mode?: ProcessingMode; } - -interface InjectConversationAppEvent extends InjectConversationEventBase { - /** @see AppEvent */ - app_event: AppEvent; - // Exclude other event types - contact_event?: never; - contact_message_event?: never; -} - -interface InjectConversationContactEvent extends InjectConversationEventBase { - /** @see AppEvent */ - contact_event: ContactEvent; - // Exclude other event types - app_event?: never; - contact_message_event?: never; -} - -interface InjectConversationContactMessageEvent extends InjectConversationEventBase { - /** @see ContactMessageEvent */ - contact_message_event: ContactMessageEvent; - // Exclude other event types - app_event?: never; - contact_event?: never; -} diff --git a/packages/conversation/src/models/v1/requests/conversation/conversation-request-data.ts b/packages/conversation/src/models/v1/requests/conversation/conversation-request-data.ts index c2be28fc..42e7c30d 100644 --- a/packages/conversation/src/models/v1/requests/conversation/conversation-request-data.ts +++ b/packages/conversation/src/models/v1/requests/conversation/conversation-request-data.ts @@ -30,8 +30,8 @@ export interface InjectMessageRequestData { 'injectMessageRequestBody': InjectMessageRequest; } export interface ListConversationsRequestData { - /** Required. True if only active conversations should be listed. */ - 'only_active': boolean; + /** True if only active conversations should be listed. */ + 'only_active'?: boolean; /** The ID of the app involved in the conversations. Note that either `app_id` or `contact_id` is required in order for the operation to function correctly. */ 'app_id'?: string; /** Resource name (ID) of the contact. Note that either `app_id` or `contact_id` is required in order for the operation to function correctly. */ diff --git a/packages/conversation/src/models/v1/requests/templates-v2/templates-v2-request-data.ts b/packages/conversation/src/models/v1/requests/templates-v2/templates-v2-request-data.ts index a1ec21e5..fbbe9b47 100644 --- a/packages/conversation/src/models/v1/requests/templates-v2/templates-v2-request-data.ts +++ b/packages/conversation/src/models/v1/requests/templates-v2/templates-v2-request-data.ts @@ -2,7 +2,7 @@ import { V2Template } from '../../v2-template'; export interface V2CreateTemplateRequestData { /** Required. The template to create. */ - 'createTemplateRequestBody': V2Template; + 'createTemplateRequestBody': Omit; } export interface V2DeleteTemplateRequestData { /** Required. The ID of the template to delete. */ @@ -26,5 +26,5 @@ export interface V2UpdateTemplateRequestData { /** The id of the template to be updated. Specified or automatically generated during template creation. Unique per project. */ 'template_id': string; /** Required. The updated template. */ - 'updateTemplateRequestBody': V2Template; + 'updateTemplateRequestBody': Omit; } diff --git a/packages/conversation/src/models/v1/requests/webhooks/webhooks-request-data.ts b/packages/conversation/src/models/v1/requests/webhooks/webhooks-request-data.ts index 4212795a..53ab9944 100644 --- a/packages/conversation/src/models/v1/requests/webhooks/webhooks-request-data.ts +++ b/packages/conversation/src/models/v1/requests/webhooks/webhooks-request-data.ts @@ -1,8 +1,8 @@ -import { Webhook } from '../../webhook'; +import { CreateWebhookRequestBody, UpdateWebhookRequestBody } from '../../webhook'; export interface CreateWebhookRequestData { /** Required. The Webhook to create */ - 'webhookCreateRequestBody': Webhook; + 'webhookCreateRequestBody': CreateWebhookRequestBody; } export interface DeleteWebhookRequestData { /** The unique ID of the webhook. */ @@ -20,7 +20,7 @@ export interface UpdateWebhookRequestData { /** The unique ID of the webhook. */ 'webhook_id': string; /** Required. The Webhook to update */ - 'webhookUpdateRequestBody': Webhook; + 'webhookUpdateRequestBody': UpdateWebhookRequestBody; /** The set of field mask paths. */ 'update_mask'?: Array; } diff --git a/packages/conversation/src/models/v1/v2-template/v2-template.ts b/packages/conversation/src/models/v1/v2-template/v2-template.ts index 223257c5..369b3421 100644 --- a/packages/conversation/src/models/v1/v2-template/v2-template.ts +++ b/packages/conversation/src/models/v1/v2-template/v2-template.ts @@ -1,7 +1,8 @@ import { V2TemplateTranslation } from '../v2-template-translation'; export interface V2Template { - + /** The id of the template. Specify this yourself during creation. Otherwise, we will generate an ID for you. This must be unique for a given project. */ + id?: string; /** The description of the template. */ description?: string; /** The version of the template. While creating a template, this will be defaulted to 1. When updating a template, you must supply the latest version of the template in order for the update to be successful. */ diff --git a/packages/conversation/src/models/v1/webhook/index.ts b/packages/conversation/src/models/v1/webhook/index.ts index 54c42503..5aeb5bd4 100644 --- a/packages/conversation/src/models/v1/webhook/index.ts +++ b/packages/conversation/src/models/v1/webhook/index.ts @@ -1 +1 @@ -export type { Webhook } from './webhook'; +export type { Webhook, CreateWebhookRequestBody, UpdateWebhookRequestBody } from './webhook'; diff --git a/packages/conversation/src/models/v1/webhook/webhook.ts b/packages/conversation/src/models/v1/webhook/webhook.ts index af60e63d..ebb60d6e 100644 --- a/packages/conversation/src/models/v1/webhook/webhook.ts +++ b/packages/conversation/src/models/v1/webhook/webhook.ts @@ -6,7 +6,6 @@ import { WebhookTargetType } from '../enums'; * Represents a destination for receiving callbacks from the Conversation API. */ export interface Webhook { - /** The app that this webhook belongs to. */ app_id: string; /** @see ClientCredentials */ @@ -22,3 +21,11 @@ export interface Webhook { /** An array of triggers that should trigger the webhook and result in an event being sent to the target url. Refer to the list of [Webhook Triggers](/docs/conversation/callbacks#webhook-triggers) for a complete list. */ triggers: WebhookTrigger[]; } + +export interface UpdateWebhookRequestBody extends + Pick, + Partial> {} + +export interface CreateWebhookRequestBody extends + Pick, + Partial> {} diff --git a/packages/conversation/src/rest/v1/contact/contact-api.ts b/packages/conversation/src/rest/v1/contact/contact-api.ts index 2d3d3f21..6e904651 100644 --- a/packages/conversation/src/rest/v1/contact/contact-api.ts +++ b/packages/conversation/src/rest/v1/contact/contact-api.ts @@ -168,7 +168,7 @@ export class ContactApi extends ConversationDomainApi { const requestOptionsPromise = this.client.prepareOptions(basePathUrl, 'GET', getParams, headers, body || undefined); const operationProperties: PaginatedApiProperties = { - pagination: PaginationEnum.TOKEN, + pagination: PaginationEnum.TOKEN2, apiName: this.apiName, operationId: 'ListContacts', dataKey: 'contacts', diff --git a/packages/conversation/src/rest/v1/conversation/conversation-api.ts b/packages/conversation/src/rest/v1/conversation/conversation-api.ts index 5fc11c5a..b330af74 100644 --- a/packages/conversation/src/rest/v1/conversation/conversation-api.ts +++ b/packages/conversation/src/rest/v1/conversation/conversation-api.ts @@ -201,7 +201,7 @@ export class ConversationApi extends ConversationDomainApi { const requestOptionsPromise = this.client.prepareOptions(basePathUrl, 'GET', getParams, headers, body || undefined); const operationProperties: PaginatedApiProperties = { - pagination: PaginationEnum.TOKEN, + pagination: PaginationEnum.TOKEN2, apiName: this.apiName, operationId: 'ListConversations', dataKey: 'conversations', @@ -238,6 +238,8 @@ export class ConversationApi extends ConversationDomainApi { 'page_token', 'order', ]); + // Manually set the default page size to 10 otherwise the API returns an empty list + getParams.page_size = getParams.page_size !== undefined ? getParams.page_size : '10'; const headers: { [key: string]: string | undefined } = { 'Content-Type': 'application/json', 'Accept': 'application/json', @@ -249,7 +251,7 @@ export class ConversationApi extends ConversationDomainApi { const requestOptionsPromise = this.client.prepareOptions(basePathUrl, 'GET', getParams, headers, body || undefined); const operationProperties: PaginatedApiProperties = { - pagination: PaginationEnum.TOKEN, + pagination: PaginationEnum.TOKEN2, apiName: this.apiName, operationId: 'ListRecentConversations', dataKey: 'conversations', diff --git a/packages/conversation/src/rest/v1/events/events-api.ts b/packages/conversation/src/rest/v1/events/events-api.ts index 87afe850..453e4cb3 100644 --- a/packages/conversation/src/rest/v1/events/events-api.ts +++ b/packages/conversation/src/rest/v1/events/events-api.ts @@ -116,7 +116,7 @@ export class EventsApi extends ConversationDomainApi { const requestOptionsPromise = this.client.prepareOptions(basePathUrl, 'GET', getParams, headers, body || undefined); const operationProperties: PaginatedApiProperties = { - pagination: PaginationEnum.TOKEN, + pagination: PaginationEnum.TOKEN2, apiName: this.apiName, operationId: 'ListEvents', dataKey: 'events', diff --git a/packages/conversation/src/rest/v1/messages/messages-api.ts b/packages/conversation/src/rest/v1/messages/messages-api.ts index ec19b487..11489ab2 100644 --- a/packages/conversation/src/rest/v1/messages/messages-api.ts +++ b/packages/conversation/src/rest/v1/messages/messages-api.ts @@ -131,7 +131,7 @@ export class MessagesApi extends ConversationDomainApi { const requestOptionsPromise = this.client.prepareOptions(basePathUrl, 'GET', getParams, headers, body || undefined); const operationProperties: PaginatedApiProperties = { - pagination: PaginationEnum.TOKEN, + pagination: PaginationEnum.TOKEN2, apiName: this.apiName, operationId: 'ListMessages', dataKey: 'messages', diff --git a/packages/conversation/tests/rest/v1/app/app.steps.ts b/packages/conversation/tests/rest/v1/app/app.steps.ts new file mode 100644 index 00000000..0fba87cd --- /dev/null +++ b/packages/conversation/tests/rest/v1/app/app.steps.ts @@ -0,0 +1,124 @@ +import { AppApi, Conversation, ConversationService, SupportedConversationRegion } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let appsApi: AppApi; +let appResponse: Conversation.AppResponse; +let listResponse: Conversation.ListAppsResponse; +let deleteAppResponse: void; + +Given('the Conversation service "Apps" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationHostname: 'http://localhost:3014', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + appsApi = conversationService.app; +}); + +When('I send a request to create an app', async () => { + appResponse = await appsApi.create({ + appCreateRequestBody: { + display_name: 'E2E Conversation App', + channel_credentials: [ + { + channel: 'SMS', + static_bearer: { + claimed_identity: 'SpaceMonkeySquadron', + token: '00112233445566778899aabbccddeeff', + }, + }, + ], + }, + }); +}); + +Then('the conversation app is created', () => { + assert.equal(appResponse.id, '01W4FFL35P4NC4K35CONVAPP001'); + assert.equal(appResponse.channel_credentials?.length, 1); + assert.equal(appResponse.channel_credentials?.[0].state?.status, 'PENDING'); + assert.equal(appResponse.conversation_metadata_report_view, 'NONE'); + assert.equal(appResponse.display_name, 'E2E Conversation App'); + assert.equal(appResponse.processing_mode, 'CONVERSATION'); +}); + +When('I send a request to list all the apps', async () => { + listResponse = await appsApi.list({}); +}); + +Then('the apps list contains 2 apps', () => { + assert.equal(listResponse.apps?.length, 2); + const app1 = listResponse.apps![0]; + assert.equal(app1.id, '01W4FFL35P4NC4K35CONVAPP001'); + assert.equal(app1.channel_credentials?.[0].state?.status, 'ACTIVE'); + assert.equal(app1.conversation_metadata_report_view, 'NONE'); + const app2 = listResponse.apps![1]; + assert.equal(app2.id, '01W4FFL35P4NC4K35CONVAPP002'); + assert.equal(app2.channel_credentials?.length, 2); + assert.equal(app2.channel_credentials?.[0].state?.status, 'FAILING'); + assert.equal(app2.conversation_metadata_report_view, 'FULL'); +}); + +When('I send a request to retrieve an app', async () => { + appResponse = await appsApi.get({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + }); +}); + +Then('the response contains the app details', () => { + assert.equal(appResponse.id, '01W4FFL35P4NC4K35CONVAPP001'); + assert.equal(appResponse.conversation_metadata_report_view, 'NONE'); + assert.equal(appResponse.display_name, 'E2E Conversation App'); + assert.equal(appResponse.processing_mode, 'CONVERSATION'); + assert.equal(appResponse.rate_limits?.outbound, 20); + assert.equal(appResponse.rate_limits?.inbound, 100); + assert.equal(appResponse.rate_limits?.webhooks, 100); + assert.equal(appResponse.retention_policy?.retention_type, 'MESSAGE_EXPIRE_POLICY'); + assert.equal(appResponse.retention_policy?.ttl_days, 180); + assert.equal(appResponse.dispatch_retention_policy?.retention_type, 'MESSAGE_EXPIRE_POLICY'); + assert.equal(appResponse.dispatch_retention_policy?.ttl_days, 0); + assert.equal(appResponse.smart_conversation?.enabled, false); + assert.equal(appResponse.queue_stats?.outbound_size, 0); + assert.equal(appResponse.queue_stats?.outbound_limit, 500000); + assert.equal(appResponse.persist_message_status?.enabled, false); + assert.equal(appResponse.message_search?.enabled, false); + assert.equal(appResponse.callback_settings?.secret_for_overridden_callback_urls, ''); + assert.equal(appResponse.delivery_report_based_fallback?.enabled, false); + assert.equal(appResponse.delivery_report_based_fallback?.delivery_report_waiting_time, 0); + assert.equal(appResponse.message_retry_settings?.retry_duration, 3600); + const channelCredentials = appResponse.channel_credentials![0] as Conversation.ChannelCredentialsSmsResponse; + assert.equal(channelCredentials.channel, 'SMS'); + assert.equal(channelCredentials.static_bearer.claimed_identity, 'SpaceMonkeySquadron'); + assert.equal(channelCredentials.static_bearer.token, '00112233445566778899aabbccddeeff'); + assert.equal(channelCredentials.callback_secret, ''); + assert.equal(channelCredentials.state?.status, 'ACTIVE'); + assert.equal(channelCredentials.state?.description, ''); + assert.equal(channelCredentials.channel_known_id, ''); +}); + +When('I send a request to update an app', async () => { + appResponse = await appsApi.update({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + appUpdateRequestBody: { + display_name: 'Updated name', + }, + }); +}); + +Then('the response contains the app details with updated properties', () => { + assert.equal(appResponse.id, '01W4FFL35P4NC4K35CONVAPP001'); + assert.equal(appResponse.display_name, 'Updated name'); +}); + +When('I send a request to delete an app', async () => { + deleteAppResponse = await appsApi.delete({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + }); +}); + +Then('the delete app response contains no data', () => { + assert.deepEqual(deleteAppResponse, {} ); +}); diff --git a/packages/conversation/tests/rest/v1/capability/capability.steps.ts b/packages/conversation/tests/rest/v1/capability/capability.steps.ts new file mode 100644 index 00000000..f738d568 --- /dev/null +++ b/packages/conversation/tests/rest/v1/capability/capability.steps.ts @@ -0,0 +1,35 @@ +import { Conversation, ConversationService, SupportedConversationRegion, CapabilityApi } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let capabilityApi: CapabilityApi; +let lookupCapabilityResponse: Conversation.LookupCapabilityResponse; + +Given('the Conversation service "Capability" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationHostname: 'http://localhost:3014', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + capabilityApi = conversationService.capability; +}); + +When('I send a request to query a capability lookup', async () => { + lookupCapabilityResponse = await capabilityApi.lookup({ + lookupCapabilityRequestBody: { + app_id: '01W4FFL35P4NC4K35CONVAPP001', + recipient: { + contact_id: '01W4FFL35P4NC4K35CONTACT001', + }, + }, + }); +}); + +Then('the response contains the id of the capability lookup query', () => { + assert.equal(lookupCapabilityResponse.app_id, '01W4FFL35P4NC4K35CONVAPP001'); + assert.equal(lookupCapabilityResponse.recipient?.contact_id, '01W4FFL35P4NC4K35CONTACT001'); + assert.equal(lookupCapabilityResponse.request_id, '01W4FFL35P4NC4K35CAPABILITY'); +}); diff --git a/packages/conversation/tests/rest/v1/contact/contacts.steps.ts b/packages/conversation/tests/rest/v1/contact/contacts.steps.ts new file mode 100644 index 00000000..ad7f0969 --- /dev/null +++ b/packages/conversation/tests/rest/v1/contact/contacts.steps.ts @@ -0,0 +1,180 @@ +import { Conversation, ContactApi, ConversationService, SupportedConversationRegion } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let contactsApi: ContactApi; +let contact: Conversation.Contact; +let listResponse: PageResult; +let contactsList: Conversation.Contact[]; +let pagesIteration: number; +let deleteContactResponse: void; +let channelProfile: Conversation.GetChannelProfileResponse; + +Given('the Conversation service "Contacts" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationHostname: 'http://localhost:3014', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + contactsApi = conversationService.contact; +}); + +When('I send a request to create a contact', async () => { + contact = await contactsApi.create({ + contactCreateRequestBody: { + channel_identities: [ + { + channel: 'SMS', + identity: '+12015555555', + }, + ], + language: 'EN_US', + display_name: 'Marty McFly', + email: 'time.traveler@delorean.com', + }, + }); +}); + +Then('the contact is created', () => { + assert.equal(contact.id, '01W4FFL35P4NC4K35CONTACT001'); +}); + +When('I send a request to list the existing contacts', async () => { + listResponse = await contactsApi.list({ + page_size: 2, + }); +}); + +Then('the response contains {string} contacts', (expectedAnswer: string) => { + const expectedContactsCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedContactsCount); +}); + +When('I send a request to list all the contacts', async () => { + contactsList = []; + for await (const contact of contactsApi.list({ page_size: 2 })) { + contactsList.push(contact); + } +}); + +When('I iterate manually over the contacts pages', async () => { + contactsList = []; + listResponse = await contactsApi.list({ + page_size: 2, + }); + contactsList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + contactsList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the contacts list contains {string} contacts', (expectedAnswer: string) => { + const expectedServices = parseInt(expectedAnswer, 10); + assert.equal(contactsList.length, expectedServices); +}); + +Then('the contacts iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve a contact', async () => { + contact = await contactsApi.get({ + contact_id: '01W4FFL35P4NC4K35CONTACT001', + }); +}); + +Then('the response contains the contact details', () => { + assert.equal(contact.id, '01W4FFL35P4NC4K35CONTACT001'); + assert.equal(contact.channel_identities?.length, 1); + const channelIdentity = contact.channel_identities![0]; + assert.equal(channelIdentity.channel, 'SMS'); + assert.equal(channelIdentity.identity, '12015555555'); + assert.equal(contact.channel_priority?.length, 0); + assert.equal(contact.display_name, 'Marty McFly'); + assert.equal(contact.email, 'time.traveler@delorean.com'); + assert.equal(contact.language, 'EN_US'); +}); + +When('I send a request to update a contact', async () => { + contact = await contactsApi.update({ + contact_id: '01W4FFL35P4NC4K35CONTACT001', + updateContactRequestBody: { + channel_identities: [ + { + channel: 'MESSENGER', + identity: '7968425018576406', + app_id: '01W4FFL35P4NC4K35CONVAPP001', + }, + { + channel: 'SMS', + identity: '12015555555', + }, + ], + channel_priority: ['MESSENGER'], + }, + }); +}); + +Then('the response contains the contact details with updated data', () => { + assert.equal(contact.id, '01W4FFL35P4NC4K35CONTACT001'); + assert.equal(contact.channel_identities?.length, 2); +}); + +When('I send a request to delete a contact', async () => { + deleteContactResponse = await contactsApi.delete({ + contact_id: '01W4FFL35P4NC4K35CONTACT001', + }); +}); + +Then('the delete contact response contains no data', () => { + assert.deepEqual(deleteContactResponse, {} ); +}); + +When('I send a request to merge a source contact to a destination contact', async () => { + contact = await contactsApi.mergeContact({ + destination_id: '01W4FFL35P4NC4K35CONTACT002', + mergeContactRequestBody: { + source_id: '01W4FFL35P4NC4K35CONTACT001', + }, + }); +}); + +Then('the response contains data from the destination contact and from the source contact', () => { + const channelIdentities = contact.channel_identities!; + assert.equal(channelIdentities.length, 3); + assert.equal(channelIdentities[0].channel, 'MESSENGER'); + assert.equal(channelIdentities[1].channel, 'MMS'); + assert.equal(channelIdentities[2].channel, 'SMS'); + assert.deepEqual(contact.channel_priority, ['MMS', 'MESSENGER']); + assert.equal(contact.display_name, 'Pika pika'); + assert.equal(contact.email, 'pikachu@poke.mon'); +}); + +When('I send a request to get the channel profile of a contact ID', async () => { + channelProfile = await contactsApi.getChannelProfile({ + getChannelProfileRequestBody: { + app_id: '01W4FFL35P4NC4K35CONVAPP001', + channel: 'MESSENGER', + recipient: { + contact_id: '01W4FFL35P4NC4K35CONTACT001', + }, + }, + }); +}); + +Then('the response contains the profile of the contact on the requested channel', () => { + assert.equal(channelProfile.profile_name, 'Marty McFly FB' ); +}); diff --git a/packages/conversation/tests/rest/v1/conversation/conversations.steps.ts b/packages/conversation/tests/rest/v1/conversation/conversations.steps.ts new file mode 100644 index 00000000..50b281f9 --- /dev/null +++ b/packages/conversation/tests/rest/v1/conversation/conversations.steps.ts @@ -0,0 +1,257 @@ +import { Conversation, ConversationApi, ConversationService, SupportedConversationRegion } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let conversationsApi: ConversationApi; +let conversation: Conversation.Conversation; +let listResponse: PageResult; +let conversationsList: Conversation.Conversation[]; +let listRecentConversationsResponse: PageResult; +let recentConversationsList: Conversation.ConversationRecentMessage[]; +let pagesIteration: number; +let deleteConversationResponse: void; +let injectEventResponse: Conversation.InjectEventResponse; +let injectMessageResponse: void; +let stopConversationResponse: void; + +Given('the Conversation service "Conversations" is available', () => { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationHostname: 'http://localhost:3014', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + conversationsApi = conversationService.conversation; +}); + +When('I send a request to create a conversation', async () => { + conversation = await conversationsApi.create({ + createConversationRequestBody: { + app_id: '01W4FFL35P4NC4K35CONVAPP001', + contact_id: '01W4FFL35P4NC4K35CONTACT001', + active: true, + active_channel: 'MESSENGER', + metadata: 'e2e tests', + metadata_json: { + prop1: 'value1', + prop2: 'value2', + }, + }, + }); +}); + +Then('the conversation is created', () => { + assert.equal(conversation.id, '01W4FFL35P4NC4K35CONVERS001'); +}); + +When('I send a request to list the existing conversations', async () => { + listResponse = await conversationsApi.list({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + page_size: 2, + }); +}); + +Then('the response contains {string} conversations', (expectedAnswer: string) => { + const expectedConversationsCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedConversationsCount); +}); + +When('I send a request to list all the conversations', async () => { + conversationsList = []; + for await (const conversation of conversationsApi.list({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + page_size: 2, + })) { + conversationsList.push(conversation); + } +}); + +When('I iterate manually over the conversations pages', async () => { + conversationsList = []; + listResponse = await conversationsApi.list({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + page_size: 2, + }); + conversationsList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + conversationsList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the conversations list contains {string} conversations', (expectedAnswer: string) => { + const expectedConversationsCount = parseInt(expectedAnswer, 10); + assert.equal(conversationsList.length, expectedConversationsCount); +}); + +Then('the conversations iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +// //////////////////// + +When('I send a request to list the recent conversations', async () => { + listRecentConversationsResponse = await conversationsApi.listRecent({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + page_size: 2, + }); +}); + +Then('the response contains {string} recent conversations', (expectedAnswer: string) => { + const expectedRecentConversationsCount = parseInt(expectedAnswer, 10); + assert.equal(listRecentConversationsResponse.data.length, expectedRecentConversationsCount); +}); + +When('I send a request to list all the recent conversations', async () => { + recentConversationsList = []; + for await (const recentConversation of conversationsApi.listRecent({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + page_size: 2, + })) { + recentConversationsList.push(recentConversation); + } +}); + +When('I iterate manually over the recent conversations pages', async () => { + recentConversationsList = []; + listRecentConversationsResponse = await conversationsApi.listRecent({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + page_size: 2, + }); + recentConversationsList.push(...listRecentConversationsResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listRecentConversationsResponse.hasNextPage) { + listRecentConversationsResponse = await listRecentConversationsResponse.nextPage(); + recentConversationsList.push(...listRecentConversationsResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the recent conversations list contains {string} recent conversations', (expectedAnswer: string) => { + const expectedRecentConversationsCount = parseInt(expectedAnswer, 10); + assert.equal(conversationsList.length, expectedRecentConversationsCount); +}); + +Then('the recent conversations iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve a conversation', async () => { + conversation = await conversationsApi.get({ + conversation_id: '01W4FFL35P4NC4K35CONVERS001', + }); +}); + +Then('the response contains the conversation details', () => { + assert.equal(conversation.id, '01W4FFL35P4NC4K35CONVERS001'); + assert.equal(conversation.app_id, '01W4FFL35P4NC4K35CONVAPP001'); + assert.equal(conversation.contact_id, '01W4FFL35P4NC4K35CONTACT002'); + assert.deepEqual(conversation.last_received, new Date('2024-06-06T14:42:42Z')); + assert.equal(conversation.active_channel, 'MESSENGER'); + assert.equal(conversation.active, true); + assert.equal(conversation.metadata, 'e2e tests'); + assert.deepEqual(conversation.metadata_json, { + prop1: 'value1', + prop2: 'value2', + }); + assert.equal(conversation.correlation_id, ''); +}); + +When('I send a request to update a conversation', async () => { + conversation = await conversationsApi.update({ + conversation_id: '01W4FFL35P4NC4K35CONVERS001', + updateConversationRequestBody: { + active: false, + app_id: '01W4FFL35P4NC4K35CONVAPP002', + metadata: 'Transferred conversation', + correlation_id: 'my-correlator', + }, + }); +}); + +Then('the response contains the conversation details with updated data', () => { + assert.equal(conversation.id, '01W4FFL35P4NC4K35CONVERS001'); + assert.equal(conversation.app_id, '01W4FFL35P4NC4K35CONVAPP002'); + assert.equal(conversation.active, false); + assert.equal(conversation.metadata, 'Transferred conversation'); + assert.equal(conversation.correlation_id, 'my-correlator'); +}); + +When('I send a request to delete a conversation', async () => { + deleteConversationResponse = await conversationsApi.delete({ + conversation_id: '01W4FFL35P4NC4K35CONVERS001', + }); +}); + +Then('the delete conversation response contains no data', () => { + assert.deepEqual(deleteConversationResponse, {} ); +}); + +When('I send a request to inject an event into a conversation', async () => { + injectEventResponse = await conversationsApi.injectEvent({ + conversation_id: '01W4FFL35P4NC4K35CONVERS001', + injectConversationEventRequestBody: { + app_event: { + composing_event: {}, + }, + accept_time: new Date('2024-06-06T15:15:15Z'), + }, + }); +}); + +Then('the event is created and injected in the conversation', () => { + assert.equal(injectEventResponse.event_id, '01W4FFL35P4NC4K35CONVEVENT1'); + assert.deepEqual(injectEventResponse.accepted_time, new Date('2024-06-06T15:15:15Z')); +}); + +When('I send a request to inject a message into a conversation', async () => { + injectMessageResponse = await conversationsApi.injectMessage({ + conversation_id: '01W4FFL35P4NC4K35CONVERS001', + injectMessageRequestBody: { + app_message: { + text_message: { + text: 'Injected text message', + }, + }, + accept_time: new Date('2024-06-06T14:42:42Z'), + direction: 'TO_CONTACT', + contact_id: '01W4FFL35P4NC4K35CONTACT002', + channel_identity: { + channel: 'MESSENGER', + identity: '7968425018576406', + app_id: '01W4FFL35P4NC4K35CONVAPP001', + }, + }, + }); +}); + +Then('the message is created and injected in the conversation', () => { + assert.deepEqual(injectMessageResponse, {}); +}); + +When('I send a request to stop a conversation', async () => { + stopConversationResponse = await conversationsApi.stopActive({ + conversation_id: '01W4FFL35P4NC4K35CONVERS001', + }); +}); + +Then('the stop conversation response contains no data', () => { + assert.deepEqual(stopConversationResponse, {} ); +}); diff --git a/packages/conversation/tests/rest/v1/events/events.steps.ts b/packages/conversation/tests/rest/v1/events/events.steps.ts new file mode 100644 index 00000000..5dc7f883 --- /dev/null +++ b/packages/conversation/tests/rest/v1/events/events.steps.ts @@ -0,0 +1,124 @@ +import { Conversation, ConversationService, EventsApi, SupportedConversationRegion } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let eventsApi: EventsApi; +let eventResponse: Conversation.SendEventResponse; +let listResponse: PageResult; +let eventsList: Conversation.ConversationEvent[]; +let pagesIteration: number; +let event: Conversation.ConversationEvent; +let deleteEventResponse: void; + +Given('the Conversation service "Events" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationHostname: 'http://localhost:3014', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + eventsApi = conversationService.events; +}); + +When('I send a request to send a conversation event to a contact', async () => { + eventResponse = await eventsApi.send({ + sendEventRequestBody: { + event: { + composing_event: {}, + }, + app_id: '01W4FFL35P4NC4K35CONVAPP001', + recipient: { + contact_id: '01W4FFL35P4NC4K35CONTACT001', + }, + }, + }); +}); + +Then('the response contains the id of the conversation event', () => { + assert.equal(eventResponse.event_id, '01W4FFL35P4NC4K35CONVEVENT1'); +}); + +When('I send a request to list the existing conversation events', async () => { + listResponse = await eventsApi.list({ + page_size: 2, + }); +}); + +Then('the response contains {string} conversation events', (expectedAnswer: string) => { + const expectedEventsCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedEventsCount); +}); + +When('I send a request to list all the conversation events', async () => { + eventsList = []; + for await (const message of eventsApi.list({ page_size: 2 })) { + eventsList.push(message); + } +}); + +When('I iterate manually over the conversation events pages', async () => { + eventsList = []; + listResponse = await eventsApi.list({ + page_size: 2, + }); + eventsList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + eventsList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the conversation events list contains {string} conversation events', (expectedAnswer: string) => { + const expectedServices = parseInt(expectedAnswer, 10); + assert.equal(eventsList.length, expectedServices); +}); + +Then('the conversation events iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve a conversation event', async () => { + event = await eventsApi.get({ + event_id: '01W4FFL35P4NC4K35CONVEVENT1', + }); +}); + +Then('the response contains the conversation event details', () => { + assert.equal(event.id, '01W4FFL35P4NC4K35CONVEVENT1'); + assert.equal(event.direction, 'TO_CONTACT'); + assert.equal(event.conversation_id, '01W4FFL35P4NC4K35CONVERSATI'); + assert.equal(event.contact_id, '01W4FFL35P4NC4K35CONTACT001'); + assert.deepEqual(event.accept_time, new Date('2024-06-06T12:42:42Z')); + assert.equal(event.processing_mode, 'CONVERSATION'); + const channelIdentity: Conversation.ChannelIdentity = { + channel: 'MESSENGER', + identity: '7968425018576406', + app_id: '01W4FFL35P4NC4K35CONVAPP001', + }; + assert.deepEqual(event.channel_identity, channelIdentity); + const composingEvent: Conversation.ComposingEvent = { + composing_event: {}, + }; + assert.deepEqual(event.app_event, composingEvent); +}); + +When('I send a request to delete a conversation event', async () => { + deleteEventResponse = await eventsApi.delete({ + event_id: '01W4FFL35P4NC4K35CONVEVENT1', + }); +}); + +Then('the delete conversation event response contains no data', () => { + assert.deepEqual(deleteEventResponse, {} ); +}); diff --git a/packages/conversation/tests/rest/v1/messages/messages.steps.ts b/packages/conversation/tests/rest/v1/messages/messages.steps.ts new file mode 100644 index 00000000..e5438864 --- /dev/null +++ b/packages/conversation/tests/rest/v1/messages/messages.steps.ts @@ -0,0 +1,138 @@ +import { Conversation, ConversationService, MessagesApi, SupportedConversationRegion } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let messagesApi: MessagesApi; +let messageResponse: Conversation.SendMessageResponse; +let listResponse: PageResult; +let messagesList: Conversation.ConversationMessage[]; +let pagesIteration: number; +let message: Conversation.ConversationMessage; +let deleteMessageResponse: void; + +Given('the Conversation service "Messages" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationHostname: 'http://localhost:3014', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + messagesApi = conversationService.messages; +}); + +When('I send a request to send a message to a contact', async () => { + messageResponse = await messagesApi.send({ + sendMessageRequestBody: { + message: { + text_message: { + text: 'Hello', + }, + }, + app_id: '01W4FFL35P4NC4K35CONVAPP001', + recipient: { + contact_id: '01W4FFL35P4NC4K35CONTACT001', + }, + }, + }); +}); + +Then('the response contains the id of the message', () => { + assert.equal(messageResponse.message_id, '01W4FFL35P4NC4K35MESSAGE001'); +}); + +When('I send a request to list the existing messages', async () => { + listResponse = await messagesApi.list({ + page_size: 2, + }); +}); + +Then('the response contains {string} messages', (expectedAnswer: string) => { + const expectedMessagesCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedMessagesCount); +}); + +When('I send a request to list all the messages', async () => { + messagesList = []; + for await (const message of messagesApi.list({ page_size: 2 })) { + messagesList.push(message); + } +}); + +When('I iterate manually over the messages pages', async () => { + messagesList = []; + listResponse = await messagesApi.list({ + page_size: 2, + }); + messagesList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + messagesList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the messages list contains {string} messages', (expectedAnswer: string) => { + const expectedServices = parseInt(expectedAnswer, 10); + assert.equal(messagesList.length, expectedServices); +}); + +Then('the result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve a message', async () => { + message = await messagesApi.get({ + message_id: '01W4FFL35P4NC4K35MESSAGE001', + }); +}); + +Then('the response contains the message details', () => { + assert.equal(message.id, '01W4FFL35P4NC4K35MESSAGE001'); + assert.equal(message.direction, 'TO_CONTACT'); + assert.equal(message.conversation_id, '01W4FFL35P4NC4K35CONVERSATI'); + assert.equal(message.contact_id, '01W4FFL35P4NC4K35CONTACT001'); + assert.equal(message.metadata, ''); + assert.deepEqual(message.accept_time, new Date('2024-06-06T12:42:42Z')); + assert.equal(message.processing_mode, 'CONVERSATION'); + assert.equal(message.injected, false); + const channelIdentity: Conversation.ChannelIdentity = { + channel: 'SMS', + identity: '12015555555', + app_id: '', + }; + assert.deepEqual(message.channel_identity, channelIdentity); +}); + +When('I send a request to update a message', async () => { + message = await messagesApi.update({ + message_id: '01W4FFL35P4NC4K35MESSAGE001', + updateMessageRequestBody: { + metadata: 'Updated metadata', + }, + }); +}); + +Then('the response contains the message details with updated metadata', () => { + assert.equal(message.id, '01W4FFL35P4NC4K35MESSAGE001'); + assert.equal(message.metadata, 'Updated metadata'); +}); + +When('I send a request to delete a message', async () => { + deleteMessageResponse = await messagesApi.delete({ + message_id: '01W4FFL35P4NC4K35MESSAGE001', + }); +}); + +Then('the delete message response contains no data', () => { + assert.deepEqual(deleteMessageResponse, {} ); +}); diff --git a/packages/conversation/tests/rest/v1/templates-v1/templates-v1.steps.ts b/packages/conversation/tests/rest/v1/templates-v1/templates-v1.steps.ts new file mode 100644 index 00000000..b8c312ac --- /dev/null +++ b/packages/conversation/tests/rest/v1/templates-v1/templates-v1.steps.ts @@ -0,0 +1,170 @@ +import { Conversation, ConversationService, SupportedConversationRegion, TemplatesV1Api } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let templatesV1Api: TemplatesV1Api; +let template: Conversation.V1Template; +let templatesList: Conversation.V1ListTemplatesResponse; +let deleteTemplateResponse: void; + +const enUSTranslation: Conversation.V1TemplateTranslation = { + language_code: 'en-US', + variables: [ + { + key: 'name', + preview_value: 'Professor Jones', + }, + ], + content: '{"text_message":{"text":"Hello ${name}. Text message template created with V1 API"}}', +}; + +const frFRTranslation: Conversation.V1TemplateTranslation = { + language_code: 'fr-FR', + variables: [ + { + key: 'name', + preview_value: 'Professeur Jones', + }, + ], + content: '{"text_message":{"text":"Bonjour ${name}. Ce message texte provient d\'un template V1"}}', +}; + +Given('the Conversation service "TemplatesV1" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationTemplatesHostname: 'http://localhost:3015', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + templatesV1Api = conversationService.templatesV1; +}); + +When('I send a request to create a conversation template with the V1 API', async () => { + template = await templatesV1Api.create({ + createTemplateRequestBody: { + default_translation: 'en-US', + channel: 'MESSENGER', + description: 'Text template V1', + translations: [ + { + ...enUSTranslation, + version: '1', + }, + ], + }, + }); +}); + +Then('the conversation template V1 is created', () => { + assert.equal(template.id, '01W4FFL35P4NC4K35TEMPLATE01'); +}); + +When('I send a request to list the conversation templates with the V1 API', async () => { + templatesList = await templatesV1Api.list({}); +}); + +Then('the response contains the list of conversation templates with the V1 structure', () => { + assert.equal(templatesList.templates?.length, 2); +}); + +When('I send a request to retrieve a conversation template with the V1 API', async () => { + template = await templatesV1Api.get({ + template_id: '01W4FFL35P4NC4K35TEMPLATE01', + }); +}); + +Then('the response contains the conversation template details with the V1 structure', () => { + assert.equal(template.id, '01W4FFL35P4NC4K35TEMPLATE01'); + assert.equal(template.description, 'Text template V1'); + assert.equal(template.translations?.length, 1); + const expectedTranslation: Conversation.V1TemplateTranslation = { + language_code: 'en-US', + content: '{"text_message":{"text":"Hello ${name}. Text message template created with V1 API"}}', + version: '1', + create_time: new Date('2024-06-06T14:42:42Z'), + update_time: new Date('2024-06-06T14:42:42Z'), + variables: [ + { + key: 'name', + preview_value: 'Professor Jones', + }, + ], + }; + assert.deepEqual(template.translations?.[0], expectedTranslation); + assert.equal(template.default_translation, 'en-US'); + assert.deepEqual(template.create_time, new Date('2024-06-06T14:42:42Z')); + assert.deepEqual(template.update_time, new Date('2024-06-06T14:42:42Z')); + assert.equal(template.channel, 'UNSPECIFIED'); +}); + +When('I send a request to update a conversation template with the V1 API', async () => { + template = await templatesV1Api.update({ + template_id: '01W4FFL35P4NC4K35TEMPLATE01', + updateTemplateRequestBody: { + description: 'Updated text template V1', + channel: 'SMS', + default_translation: 'fr-FR', + translations: [ + { + ...enUSTranslation, + content: '{"text_message":{"text":"Hello ${name}. This text message template has been created with V1 API"}}', + version: '2', + }, + { + ...frFRTranslation, + version: '1', + }, + ], + }, + }); +}); + +Then('the response contains the conversation template details with updated data with the V1 structure', () => { + assert.equal(template.id, '01W4FFL35P4NC4K35TEMPLATE01'); + assert.equal(template.description, 'Updated text template V1'); + assert.equal(template.translations?.length, 2); + const expectedenUSTranslation: Conversation.V1TemplateTranslation = { + language_code: 'en-US', + content: '{"text_message":{"text":"Hello ${name}. This text message template has been created with V1 API"}}', + version: '2', + create_time: new Date('2024-06-06T14:45:45Z'), + update_time: new Date('2024-06-06T14:45:45Z'), + variables: [ + { + key: 'name', + preview_value: 'Professor Jones', + }, + ], + }; + assert.deepEqual(template.translations?.[1], expectedenUSTranslation); + const expectedfrFRTranslation: Conversation.V1TemplateTranslation = { + language_code: 'fr-FR', + content: '{"text_message":{"text":"Bonjour ${name}. Ce message texte provient d\'un template V1"}}', + version: '1', + create_time: new Date('2024-06-06T14:45:45Z'), + update_time: new Date('2024-06-06T14:45:45Z'), + variables: [ + { + key: 'name', + preview_value: 'Professeur Jones', + }, + ], + }; + assert.deepEqual(template.translations?.[0], expectedfrFRTranslation); + assert.equal(template.default_translation, 'fr-FR'); + assert.deepEqual(template.create_time, new Date('2024-06-06T14:42:42Z')); + assert.deepEqual(template.update_time, new Date('2024-06-06T14:45:45Z')); + assert.equal(template.channel, 'UNSPECIFIED'); +}); + +When('I send a request to delete a conversation template with the V1 API', async () => { + deleteTemplateResponse = await templatesV1Api.delete({ + template_id: '01W4FFL35P4NC4K35TEMPLATE01', + }); +}); + +Then('the delete conversation template response V1 contains no data', () => { + assert.deepEqual(deleteTemplateResponse, {} ); +}); diff --git a/packages/conversation/tests/rest/v1/templates-v2/templates-v2.steps.ts b/packages/conversation/tests/rest/v1/templates-v2/templates-v2.steps.ts new file mode 100644 index 00000000..85a6d307 --- /dev/null +++ b/packages/conversation/tests/rest/v1/templates-v2/templates-v2.steps.ts @@ -0,0 +1,193 @@ +import { Conversation, ConversationService, SupportedConversationRegion, TemplatesV2Api } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let templatesV2Api: TemplatesV2Api; +let template: Conversation.V2Template; +let templatesList: Conversation.V2ListTemplatesResponse; +let translationsList: Conversation.V2ListTranslationsResponse; +let deleteTemplateResponse: void; +let currentVersion: number; + +Given('the Conversation service "TemplatesV2" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationTemplatesHostname: 'http://localhost:3015', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + templatesV2Api = conversationService.templatesV2; +}); + +When('I send a request to create a conversation template with the V2 API', async () => { + template = await templatesV2Api.create({ + createTemplateRequestBody: { + id: '01HVN010MG3B9N6X323JAFN59P', + default_translation: 'en-US', + description: 'Text template V2', + translations: [ + { + language_code: 'en-US', + version: '3', + text_message: { + text: 'Hello ${name}. Text message template created with V2 API', + }, + variables: [ + { + key: 'name', + preview_value: 'Professor Jones', + }, + ], + }, + ], + }, + }); +}); + +Then('the conversation template V2 is created', () => { + assert.equal(template.id, '01HVN010MG3B9N6X323JAFN59P'); + assert.equal(template.version, 1); + assert.equal(template.translations?.length, 1); + assert.equal(template.translations?.[0].version, '3'); +}); + +When('I send a request to list the conversation templates with the V2 API', async () => { + templatesList = await templatesV2Api.list({}); +}); + +Then('the response contains the list of conversation templates with the V2 structure', () => { + assert.ok(templatesList.templates); + assert.equal(templatesList.templates.length, 2); +}); + +Then('for each templateV2 in the templateV2 list response, it defines a translation with version "latest" on top of each current translation version', () => { + for(const templateV2 of templatesList.templates!) { + assert.ok(templateV2.version); + let latestVersionCount = 0; + let otherVersionCount = 0; + const translations = templateV2.translations!; + for(const translation of translations) { + translation.version === 'latest' ? latestVersionCount++ : otherVersionCount++; + } + assert.equal(latestVersionCount, otherVersionCount); + } +}); + +When('I send a request to list the translations for a template with the V2 API', async () => { + translationsList = await templatesV2Api.listTranslations({ + template_id: '01W4FFL35P4NC4K35TEMPLATEV2', + }); +}); + +Then('the response contains the list of translations for a template with the V2 structure', () => { + assert.ok(translationsList.translations); + assert.equal(translationsList.translations.length, 2); + assert.equal(translationsList.translations.find((translation) => translation.version === 'latest'), undefined); +}); + +When('I send a request to retrieve a conversation template with the V2 API', async () => { + template = await templatesV2Api.get({ + template_id: '01HVN010MG3B9N6X323JAFN59P', + }); +}); + +Then('the response contains the conversation template details with the V2 structure', () => { + assert.equal(template.id, '01HVN010MG3B9N6X323JAFN59P'); + assert.equal(template.description, 'Text template V2'); + assert.equal(template.version, 1); + assert.equal(template.translations?.length, 2); + const translation = template.translations!.find((translation) => translation.version !== 'latest'); + assert.ok(translation); + assert.equal(translation.language_code, 'en-US'); + assert.equal(translation.version, '3'); + assert.deepEqual(translation.create_time, new Date('2024-06-06T14:42:42Z')); + assert.deepEqual(translation.update_time, new Date('2024-06-06T14:42:42Z')); + assert.equal(translation.variables?.length, 1); + assert.ok(translation.text_message?.text); + assert.deepEqual(translation.channel_template_overrides, {}); + assert.equal(template.default_translation, 'en-US'); + assert.deepEqual(template.create_time, new Date('2024-06-06T14:42:42Z')); + assert.deepEqual(template.update_time, new Date('2024-06-06T14:42:42Z')); +}); + +When('I send a request to update a conversation template with the V2 API', async () => { + currentVersion = 1; + template = await templatesV2Api.update({ + template_id: '01HVN010MG3B9N6X323JAFN59P', + updateTemplateRequestBody: { + description: 'Updated description v2', + version: currentVersion, + default_translation: 'en-US', + translations: [ + { + language_code: 'en-US', + version: '1', + list_message: { + title: 'Choose your icecream flavor', + description: 'The best icecream in town!', + sections: [ + { + title: 'Fruit flavors', + items: [ + { + choice: { + title: 'Strawberry', + postback_data: 'Strawberry postback', + }, + }, + { + choice: { + title: 'Blueberry', + postback_data: 'Blueberry postback', + }, + }, + ], + }, + { + title: 'Other flavors', + items: [ + { + choice: { + title: 'Chocolate', + postback_data: 'Chocolate postback', + }, + }, + { + choice: { + title: 'Vanilla', + postback_data: 'Vanilla postback', + }, + }, + ], + }, + ], + }, + }, + ], + }, + }); +}); + +Then('the response contains the conversation template details with updated data with the V2 structure', () => { + assert.equal(template.id, '01HVN010MG3B9N6X323JAFN59P'); + assert.equal(template.description, 'Updated description v2'); + assert.equal(template.version, currentVersion + 1); + assert.equal(template.translations?.length, 1); + assert.equal(template.default_translation, 'en-US'); + assert.deepEqual(template.create_time, new Date('2024-06-06T14:42:42Z')); + assert.deepEqual(template.update_time, new Date('2024-06-06T15:45:45Z')); + const translation = template.translations?.[0]; + assert.ok(translation?.list_message); +}); + +When('I send a request to delete a conversation template with the V2 API', async () => { + deleteTemplateResponse = await templatesV2Api.delete({ + template_id: '01W4FFL35P4NC4K35TEMPLATEV2', + }); +}); + +Then('the delete conversation template response V2 contains no data', () => { + assert.deepEqual(deleteTemplateResponse, {} ); +}); diff --git a/packages/conversation/tests/rest/v1/transcoding/transcoding.steps.ts b/packages/conversation/tests/rest/v1/transcoding/transcoding.steps.ts new file mode 100644 index 00000000..d0eb967d --- /dev/null +++ b/packages/conversation/tests/rest/v1/transcoding/transcoding.steps.ts @@ -0,0 +1,79 @@ +import { Conversation, ConversationService, SupportedConversationRegion, TranscodingApi } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let transcodingApi: TranscodingApi; +let transcodeMessageResponse: Conversation.TranscodeMessageResponse; + +Given('the Conversation service "Transcoding" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationHostname: 'http://localhost:3014', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + transcodingApi = conversationService.transcoding; +}); + +When('I send a request to transcode a location message', async () => { + transcodeMessageResponse = await transcodingApi.transcodeMessage({ + transcodeMessageRequestBody: { + app_id: '01W4FFL35P4NC4K35CONVAPP001', + app_message: { + location_message: { + title: 'Phare d\'Eckmühl', + label: 'Pointe de Penmarch', + coordinates: { + latitude: 47.7981899, + longitude: -4.3727685, + }, + }, + }, + channels: [ + 'APPLEBC', + 'INSTAGRAM', + 'KAKAOTALK', + 'KAKAOTALKCHAT', + 'LINE', + 'MESSENGER', + 'RCS', + 'SMS', + 'TELEGRAM', + 'VIBER', + 'WECHAT', + 'WHATSAPP', + ], + }, + }); +}); + +Then('the location message is transcoded for all the channels', () => { + assert.ok(transcodeMessageResponse.transcoded_message?.APPLEBC); + assert.ok(transcodeMessageResponse.transcoded_message?.INSTAGRAM); + assert.ok(transcodeMessageResponse.transcoded_message?.KAKAOTALK); + assert.ok(transcodeMessageResponse.transcoded_message?.KAKAOTALKCHAT); + assert.ok(transcodeMessageResponse.transcoded_message?.LINE); + assert.ok(transcodeMessageResponse.transcoded_message?.MESSENGER); + assert.ok(transcodeMessageResponse.transcoded_message?.RCS); + assert.ok(transcodeMessageResponse.transcoded_message?.SMS); + assert.ok(transcodeMessageResponse.transcoded_message?.TELEGRAM); + assert.ok(transcodeMessageResponse.transcoded_message?.VIBER); + assert.ok(transcodeMessageResponse.transcoded_message?.WECHAT); + assert.ok(transcodeMessageResponse.transcoded_message?.WHATSAPP); + const expectedWhatsAppMessage = { + to: '{{to}}', + type: 'location', + recipient_type: 'individual', + location: { + name: 'Phare d\'Eckmühl', + address: 'Pointe de Penmarch', + longitude: '-4.3727684', + latitude: '47.79819', + }, + messaging_product: 'whatsapp', + biz_opaque_callback_data: null, + }; + assert.deepEqual(JSON.parse(transcodeMessageResponse.transcoded_message?.WHATSAPP), expectedWhatsAppMessage); +}); diff --git a/packages/conversation/tests/rest/v1/webhooks-events/webhooks-events.steps.ts b/packages/conversation/tests/rest/v1/webhooks-events/webhooks-events.steps.ts new file mode 100644 index 00000000..01b8334b --- /dev/null +++ b/packages/conversation/tests/rest/v1/webhooks-events/webhooks-events.steps.ts @@ -0,0 +1,279 @@ +import { Given, Then, When } from '@cucumber/cucumber'; +import { ConversationCallbackWebhooks, Conversation } from '../../../../src'; +import assert from 'assert'; +import { IncomingHttpHeaders } from 'http'; + +const APP_SECRET = 'CactusKnight_SurfsWaves'; +let conversationCallbackWebhook: ConversationCallbackWebhooks; +let rawEvent: any; +let event: Conversation.ConversationWebhookEvent; +let formattedHeaders: IncomingHttpHeaders; + +const processEvent = async (response: Response) => { + formattedHeaders = {}; + response.headers.forEach((value, name) => { + formattedHeaders[name.toLowerCase()] = value; + }); + rawEvent = await response.text(); + event = conversationCallbackWebhook.parseEvent(JSON.parse(rawEvent)); +}; + +Given('the Conversation Webhooks handler is available', () => { + conversationCallbackWebhook = new ConversationCallbackWebhooks(APP_SECRET); +}); + +When('I send a request to trigger a "CAPABILITY" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/capability-lookup'); + await processEvent(response); +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +Then('the header of the Conversation event {string} contains a valid signature', (_event) => { + assert.ok(conversationCallbackWebhook.validateAuthenticationHeader(formattedHeaders, rawEvent)); +}); + +Then('the Conversation event describes a "CAPABILITY" event type', () => { + const capabilityEvent = event as Conversation.CapabilityEvent; + assert.ok(capabilityEvent.capability_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'CAPABILITY'; + assert.equal(capabilityEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "CONTACT_CREATE" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/contact-create'); + await processEvent(response); +}); + +Then('the Conversation event describes a "CONTACT_CREATE" event type', () => { + const contactCreateEvent = event as Conversation.ContactCreateEvent; + assert.ok(contactCreateEvent.contact_create_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'CONTACT_CREATE'; + assert.equal(contactCreateEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "CONTACT_DELETE" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/contact-delete'); + await processEvent(response); +}); + +Then('the Conversation event describes a "CONTACT_DELETE" event type', () => { + const contactDeleteEvent = event as Conversation.ContactDeleteEvent; + assert.ok(contactDeleteEvent.contact_delete_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'CONTACT_DELETE'; + assert.equal(contactDeleteEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "CONTACT_MERGE" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/contact-merge'); + await processEvent(response); +}); + +Then('the Conversation event describes a "CONTACT_MERGE" event type', () => { + const contactMergeEvent = event as Conversation.ContactMergeEvent; + assert.ok(contactMergeEvent.contact_merge_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'CONTACT_MERGE'; + assert.equal(contactMergeEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "CONTACT_UPDATE" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/contact-update'); + await processEvent(response); +}); + +Then('the Conversation event describes a "CONTACT_UPDATE" event type', () => { + const contactUpdateEvent = event as Conversation.ContactUpdateEvent; + assert.ok(contactUpdateEvent.contact_update_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'CONTACT_UPDATE'; + assert.equal(contactUpdateEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "CONVERSATION_DELETE" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/conversation-delete'); + await processEvent(response); +}); + +Then('the Conversation event describes a "CONVERSATION_DELETE" event type', () => { + const conversationDeleteEvent = event as Conversation.ConversationDeleteEvent; + assert.ok(conversationDeleteEvent.conversation_delete_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'CONVERSATION_DELETE'; + assert.equal(conversationDeleteEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "CONVERSATION_START" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/conversation-start'); + await processEvent(response); +}); + +Then('the Conversation event describes a "CONVERSATION_START" event type', () => { + const conversationStartEvent = event as Conversation.ConversationStartEvent; + assert.ok(conversationStartEvent.conversation_start_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'CONVERSATION_START'; + assert.equal(conversationStartEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "CONVERSATION_STOP" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/conversation-stop'); + await processEvent(response); +}); + +Then('the Conversation event describes a "CONVERSATION_STOP" event type', () => { + const conversationStopEvent = event as Conversation.ConversationStopEvent; + assert.ok(conversationStopEvent.conversation_stop_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'CONVERSATION_STOP'; + assert.equal(conversationStopEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "EVENT_DELIVERY" event with a "FAILED" status', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/event-delivery-report/failed'); + await processEvent(response); +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +Then('the header of the Conversation event {} with a {} status contains a valid signature', (_event, _status) => { + assert.ok(conversationCallbackWebhook.validateAuthenticationHeader(formattedHeaders, rawEvent)); +}); + +Then('the Conversation event describes a "EVENT_DELIVERY" event type', () => { + const eventDeliveryEvent = event as Conversation.EventDelivery; + assert.ok(eventDeliveryEvent.event_delivery_report); + const expectedTrigger: Conversation.WebhookTrigger = 'EVENT_DELIVERY'; + assert.equal(eventDeliveryEvent.trigger, expectedTrigger); +}); + +Then('the Conversation event describes a FAILED event delivery status and its reason', () => { + const eventDeliveryReport = (event as Conversation.EventDelivery).event_delivery_report!; + const expectedStatus: Conversation.DeliveryStatus = 'FAILED'; + assert.equal(eventDeliveryReport.status, expectedStatus); + assert.ok(eventDeliveryReport.reason); + const expectedReasonCode: Conversation.ReasonCode = 'BAD_REQUEST'; + assert.equal(eventDeliveryReport.reason.code, expectedReasonCode); +}); + +When('I send a request to trigger a "EVENT_DELIVERY" event with a "DELIVERED" status', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/event-delivery-report/succeeded'); + await processEvent(response); +}); + +When('I send a request to trigger a "EVENT_INBOUND" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/event-inbound'); + await processEvent(response); +}); + +Then('the Conversation event describes a "EVENT_INBOUND" event type', () => { + const eventInbound = event as Conversation.EventInbound; + assert.ok(eventInbound.event); + const expectedTrigger: Conversation.WebhookTrigger = 'EVENT_INBOUND'; + assert.equal(eventInbound.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "MESSAGE_DELIVERY" event with a "FAILED" status', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/message-delivery-report/failed'); + await processEvent(response); +}); + +When('I send a request to trigger a "MESSAGE_DELIVERY" event with a "QUEUED_ON_CHANNEL" status', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/message-delivery-report/succeeded'); + await processEvent(response); +}); + +Then('the Conversation event describes a "MESSAGE_DELIVERY" event type', () => { + const messageDeliveryReceiptEvent = event as Conversation.MessageDeliveryReceiptEvent; + assert.ok(messageDeliveryReceiptEvent.message_delivery_report); + const expectedTrigger: Conversation.WebhookTrigger = 'MESSAGE_DELIVERY'; + assert.equal(messageDeliveryReceiptEvent.trigger, expectedTrigger); +}); + +Then('the Conversation event describes a FAILED message delivery status and its reason', () => { + const messageDeliveryReport = (event as Conversation.MessageDeliveryReceiptEvent).message_delivery_report!; + const expectedStatus: Conversation.DeliveryStatus = 'FAILED'; + assert.equal(messageDeliveryReport.status, expectedStatus); + assert.ok(messageDeliveryReport.reason); + const expectedReasonCode: Conversation.ReasonCode = 'RECIPIENT_NOT_REACHABLE'; + assert.equal(messageDeliveryReport.reason.code, expectedReasonCode); +}); + +When('I send a request to trigger a "MESSAGE_INBOUND" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/message-inbound'); + await processEvent(response); +}); + +Then('the Conversation event describes a "MESSAGE_INBOUND" event type', () => { + const messageInboundEvent = event as Conversation.MessageInboundEvent; + assert.ok(messageInboundEvent.message); + const expectedTrigger: Conversation.WebhookTrigger = 'MESSAGE_INBOUND'; + assert.equal(messageInboundEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "MESSAGE_INBOUND_SMART_CONVERSATION_REDACTION" event', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/message-inbound/smart-conversation-redaction'); + await processEvent(response); +}); + +Then('the Conversation event describes a "MESSAGE_INBOUND_SMART_CONVERSATION_REDACTION" event type', () => { + const messageInboundSmartConversationRedactionEvent + = event as Conversation.MessageInboundSmartConversationRedactionEvent; + assert.ok(messageInboundSmartConversationRedactionEvent.message_redaction); + const expectedTrigger: Conversation.WebhookTrigger = 'MESSAGE_INBOUND_SMART_CONVERSATION_REDACTION'; + assert.equal(messageInboundSmartConversationRedactionEvent.trigger, expectedTrigger); +}); + +When('I send a request to trigger a "MESSAGE_SUBMIT" event for a "media" message', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/message-submit/media'); + await processEvent(response); +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +Then('the header of the Conversation event {} for a {} message contains a valid signature', (_event, _messageType) => { + assert.ok(conversationCallbackWebhook.validateAuthenticationHeader(formattedHeaders, rawEvent)); +}); + +Then('the Conversation event describes a "MESSAGE_SUBMIT" event type for a "media" message', () => { + const messageSubmitEvent = event as Conversation.MessageSubmitEvent; + assert.ok(messageSubmitEvent.message_submit_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'MESSAGE_SUBMIT'; + assert.equal(messageSubmitEvent.trigger, expectedTrigger); + assert.ok(messageSubmitEvent.message_submit_notification.submitted_message?.media_message); +}); + +When('I send a request to trigger a "MESSAGE_SUBMIT" event for a "text" message', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/message-submit/text'); + await processEvent(response); +}); + +Then('the Conversation event describes a "MESSAGE_SUBMIT" event type for a "text" message', () => { + const messageSubmitEvent = event as Conversation.MessageSubmitEvent; + assert.ok(messageSubmitEvent.message_submit_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'MESSAGE_SUBMIT'; + assert.equal(messageSubmitEvent.trigger, expectedTrigger); + assert.ok(messageSubmitEvent.message_submit_notification.submitted_message?.text_message); +}); + +When('I send a request to trigger a "SMART_CONVERSATIONS" event for a "media" message', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/smart-conversations/media'); + await processEvent(response); +}); + +Then('the Conversation event describes a "SMART_CONVERSATIONS" event type for a "media" message', () => { + const smartConversationsEvent = event as Conversation.SmartConversationsEvent; + assert.ok(smartConversationsEvent.smart_conversation_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'SMART_CONVERSATIONS'; + assert.equal(smartConversationsEvent.trigger, expectedTrigger); + assert.ok(smartConversationsEvent.smart_conversation_notification.analysis_results?.ml_image_recognition_result); + assert.ok(smartConversationsEvent.smart_conversation_notification.analysis_results?.ml_offensive_analysis_result); +}); + +When('I send a request to trigger a "SMART_CONVERSATIONS" event for a "text" message', async () => { + const response = await fetch('http://localhost:3014/webhooks/conversation/smart-conversations/text'); + await processEvent(response); +}); + +Then('the Conversation event describes a "SMART_CONVERSATIONS" event type for a "text" message', () => { + const smartConversationsEvent = event as Conversation.SmartConversationsEvent; + assert.ok(smartConversationsEvent.smart_conversation_notification); + const expectedTrigger: Conversation.WebhookTrigger = 'SMART_CONVERSATIONS'; + assert.equal(smartConversationsEvent.trigger, expectedTrigger); + assert.ok(smartConversationsEvent.smart_conversation_notification.analysis_results?.ml_sentiment_result); + assert.ok(smartConversationsEvent.smart_conversation_notification.analysis_results?.ml_nlu_result); + assert.ok(smartConversationsEvent.smart_conversation_notification.analysis_results?.ml_pii_result); + assert.ok(smartConversationsEvent.smart_conversation_notification.analysis_results?.ml_offensive_analysis_result); +}); diff --git a/packages/conversation/tests/rest/v1/webhooks/webhooks.steps.ts b/packages/conversation/tests/rest/v1/webhooks/webhooks.steps.ts new file mode 100644 index 00000000..070015f4 --- /dev/null +++ b/packages/conversation/tests/rest/v1/webhooks/webhooks.steps.ts @@ -0,0 +1,119 @@ +import { Conversation, ConversationService, WebhooksApi, SupportedConversationRegion } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let webhooksApi: WebhooksApi; +let webhooksList: Conversation.ListWebhooksResponse; +let webhook: Conversation.Webhook; +let deleteWebhookResponse: void; + +Given('the Conversation service "Webhooks" is available', function () { + const conversationService = new ConversationService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + conversationHostname: 'http://localhost:3014', + conversationRegion: SupportedConversationRegion.UNITED_STATES, + }); + webhooksApi = conversationService.webhooks; +}); + +When('I send a request to create a conversation webhook', async () => { + webhook = await webhooksApi.create({ + webhookCreateRequestBody: { + app_id: '01W4FFL35P4NC4K35CONVAPP001', + target: 'https://my-callback-server.com/capability', + triggers: [ 'CAPABILITY' ], + secret: 'CactusKnight_SurfsWaves', + target_type: 'HTTP', + }, + }); +}); + +Then('the conversation webhook is created', () => { + assert.equal(webhook.id, '01W4FFL35P4NC4K35WEBHOOK004'); + assert.deepEqual(webhook.triggers, ['CAPABILITY']); + assert.equal(webhook.secret, 'CactusKnight_SurfsWaves'); +}); + +When('I send a request to list the conversation webhooks for an app', async () => { + webhooksList = await webhooksApi.list({ + app_id: '01W4FFL35P4NC4K35CONVAPP001', + }); +}); + +Then('the response contains the list of conversation webhooks', () => { + assert.equal(webhooksList.webhooks?.length, 4); + const webhook = webhooksList.webhooks![1]; + const triggersList: Conversation.WebhookTrigger[] = [ + 'CONTACT_CREATE', + 'CONTACT_DELETE', + 'CONTACT_IDENTITIES_DUPLICATION', + 'CONTACT_MERGE', + 'CONTACT_UPDATE', + ]; + assert.deepEqual(webhook.triggers, triggersList); + assert.equal(webhook.id, '01W4FFL35P4NC4K35WEBHOOK002'); + assert.equal(webhook.target_type, 'HTTP'); + assert.equal(webhook.secret, 'DiscoDragon_BuildsLego'); + assert.equal(webhook.client_credentials, null); +}); + +When('I send a request to retrieve a conversation webhook', async () => { + webhook = await webhooksApi.get({ + webhook_id: '01W4FFL35P4NC4K35WEBHOOK001', + }); +}); + +Then('the response contains the conversation webhook details', () => { + assert.equal(webhook.id, '01W4FFL35P4NC4K35WEBHOOK001'); + assert.equal(webhook.app_id, '01W4FFL35P4NC4K35CONVAPP001'); + assert.equal(webhook.target, 'https://my-callback-server.com/unsupported'); + assert.equal(webhook.target_type, 'HTTP'); + assert.equal(webhook.secret, 'VeganVampire_SipsTea'); + const triggersList: Conversation.WebhookTrigger[] = ['UNSUPPORTED']; + assert.deepEqual(webhook.triggers, triggersList); + const credentials: Conversation.ClientCredentials = { + endpoint: 'https://my-auth-server.com/oauth2/token', + client_id: 'webhook-username', + client_secret: 'webhook-password', + }; + assert.deepEqual(webhook.client_credentials, credentials); +}); + +When('I send a request to update a conversation webhook', async () => { + webhook = await webhooksApi.update({ + webhook_id: '01W4FFL35P4NC4K35WEBHOOK004', + webhookUpdateRequestBody: { + app_id: '01W4FFL35P4NC4K35CONVAPP002', + target: 'https://my-callback-server.com/capability-optin-optout', + triggers: [ + 'CAPABILITY', + 'OPT_IN', + 'OPT_OUT', + ], + secret: 'SpacePanda_RidesUnicycle', + }, + }); +}); + +Then('the response contains the conversation webhook details with updated data', () => { + assert.equal(webhook.id, '01W4FFL35P4NC4K35WEBHOOK004'); + assert.equal(webhook.app_id, '01W4FFL35P4NC4K35CONVAPP002'); + assert.equal(webhook.target, 'https://my-callback-server.com/capability-optin-optout'); + assert.deepEqual(webhook.triggers, ['CAPABILITY', 'OPT_IN', 'OPT_OUT']); + assert.equal(webhook.target_type, 'HTTP'); + assert.equal(webhook.secret, 'SpacePanda_RidesUnicycle'); + assert.equal(webhook.client_credentials, null); +}); + +When('I send a request to delete a conversation webhook', async () => { + deleteWebhookResponse = await webhooksApi.delete({ + webhook_id: '01W4FFL35P4NC4K35WEBHOOK004', + }); +}); + +Then('the delete conversation webhook response contains no data', () => { + assert.deepEqual(deleteWebhookResponse, {} ); +}); diff --git a/packages/elastic-sip-trunking/CHANGELOG.md b/packages/elastic-sip-trunking/CHANGELOG.md index 729aa932..da1dad71 100644 --- a/packages/elastic-sip-trunking/CHANGELOG.md +++ b/packages/elastic-sip-trunking/CHANGELOG.md @@ -1,3 +1,16 @@ +## Version 1.2.0 +- [Tech] Update dependency `@sinch/sdk-client` to `1.2.0`. +- [Feature] Add the method `accessControlList.get()`. +- Calls History: + - [Bugfix][Breaking] The `DirectionEnum` values are in lower case. E.g: INBOUND -> inbound. + - [Bugfix][Breaking] The price `amount` is now a `number` instead of a `string`. + - [Feature] Support date range filter for listing calls. +- [Bugfix][Breaking] + - ACLs: For "create", "update" and "addIPRange" operations, the request bodies interface no longer accept read-only properties. + - SIP endpoints: For "create" and "update" operations, the request bodies interface no longer accept read-only properties. + - SIP Trunks: For "create" and "update" operations, the request bodies interface no longer accept read-only properties. +- [E2E] Add Cucumber steps implementation. + # Version 1.1.0 - Initial version. Support for: - SIP Trunks diff --git a/packages/elastic-sip-trunking/cucumber.js b/packages/elastic-sip-trunking/cucumber.js new file mode 100644 index 00000000..691a9809 --- /dev/null +++ b/packages/elastic-sip-trunking/cucumber.js @@ -0,0 +1,8 @@ +module.exports = { + default: [ + 'tests/e2e/features/**/*.feature', + '--require-module ts-node/register', + '--require tests/rest/v1/**/*.steps.ts', + `--format-options '{"snippetInterface": "synchronous"}'`, + ].join(' '), +}; diff --git a/packages/elastic-sip-trunking/package.json b/packages/elastic-sip-trunking/package.json index e80637ef..019438f2 100644 --- a/packages/elastic-sip-trunking/package.json +++ b/packages/elastic-sip-trunking/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/elastic-sip-trunking", - "version": "1.1.0", + "version": "1.2.0", "description": "Sinch Elastic SIP Trunking API", "homepage": "", "repository": { @@ -24,13 +24,15 @@ "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", - "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo" + "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo", + "test:e2e": "cucumber-js" }, "dependencies": { - "@sinch/sdk-client": "^1.0.0" + "@sinch/sdk-client": "^1.2.0" }, "devDependencies": {}, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/elastic-sip-trunking/src/models/v1/create-access-control-list-request/create-access-control-list-request.ts b/packages/elastic-sip-trunking/src/models/v1/create-access-control-list-request/create-access-control-list-request.ts new file mode 100644 index 00000000..875d0144 --- /dev/null +++ b/packages/elastic-sip-trunking/src/models/v1/create-access-control-list-request/create-access-control-list-request.ts @@ -0,0 +1,10 @@ +import { IpRangeRequest } from '../ip-range'; + +export interface CreateAccessControlListRequest { + /** Your name for the access control list entry. */ + name: string; + /** Whether the access control list entry is enabled. You can use this to disable a list temporarily without deleting it. */ + enabled?: boolean; + /** An array of all the IP ranges to create. */ + ipRanges: IpRangeRequest[]; +} diff --git a/packages/elastic-sip-trunking/src/models/v1/create-access-control-list-request/index.ts b/packages/elastic-sip-trunking/src/models/v1/create-access-control-list-request/index.ts new file mode 100644 index 00000000..55455980 --- /dev/null +++ b/packages/elastic-sip-trunking/src/models/v1/create-access-control-list-request/index.ts @@ -0,0 +1 @@ +export type { CreateAccessControlListRequest } from './create-access-control-list-request'; diff --git a/packages/elastic-sip-trunking/src/models/v1/enum.ts b/packages/elastic-sip-trunking/src/models/v1/enum.ts index 312f600f..e9f9a547 100644 --- a/packages/elastic-sip-trunking/src/models/v1/enum.ts +++ b/packages/elastic-sip-trunking/src/models/v1/enum.ts @@ -1,3 +1,3 @@ -export type DirectionEnum = 'INBOUND' | 'OUTBOUND'; +export type DirectionEnum = 'inbound' | 'outbound'; export type CallResult = 'COMPLETED' | 'NO_ANSWER' | 'CANCEL' | 'BUSY' | 'FAILED'; diff --git a/packages/elastic-sip-trunking/src/models/v1/index.ts b/packages/elastic-sip-trunking/src/models/v1/index.ts index 4f57738f..6df44669 100644 --- a/packages/elastic-sip-trunking/src/models/v1/index.ts +++ b/packages/elastic-sip-trunking/src/models/v1/index.ts @@ -2,6 +2,7 @@ export * from './access-control-list'; export * from './add-access-control-list-to-trunk'; export * from './call'; export * from './country-permission'; +export * from './create-access-control-list-request'; export * from './ip-range'; export * from './list-country-permissions-response'; export * from './money'; diff --git a/packages/elastic-sip-trunking/src/models/v1/ip-range/index.ts b/packages/elastic-sip-trunking/src/models/v1/ip-range/index.ts index 8693404c..62f1fc29 100644 --- a/packages/elastic-sip-trunking/src/models/v1/ip-range/index.ts +++ b/packages/elastic-sip-trunking/src/models/v1/ip-range/index.ts @@ -1 +1 @@ -export type { IpRange } from './ip-range'; +export type { IpRange, IpRangeRequest } from './ip-range'; diff --git a/packages/elastic-sip-trunking/src/models/v1/ip-range/ip-range.ts b/packages/elastic-sip-trunking/src/models/v1/ip-range/ip-range.ts index a014b342..7043afb0 100644 --- a/packages/elastic-sip-trunking/src/models/v1/ip-range/ip-range.ts +++ b/packages/elastic-sip-trunking/src/models/v1/ip-range/ip-range.ts @@ -19,3 +19,5 @@ export interface IpRange { /** The ID of the access control list. */ accessControlListId?: string; } + +export type IpRangeRequest = Omit; diff --git a/packages/elastic-sip-trunking/src/models/v1/money/money.ts b/packages/elastic-sip-trunking/src/models/v1/money/money.ts index bc45fbfb..6b555751 100644 --- a/packages/elastic-sip-trunking/src/models/v1/money/money.ts +++ b/packages/elastic-sip-trunking/src/models/v1/money/money.ts @@ -5,5 +5,5 @@ export interface Money { /** The 3-letter currency code defined in ISO 4217. */ currencyCode?: string; /** The amount with 4 decimals and decimal delimiter `.`. */ - amount?: string; + amount?: number; } diff --git a/packages/elastic-sip-trunking/src/models/v1/requests/access-control-list/access-control-list-request-data.ts b/packages/elastic-sip-trunking/src/models/v1/requests/access-control-list/access-control-list-request-data.ts index db991ced..bf8c9300 100644 --- a/packages/elastic-sip-trunking/src/models/v1/requests/access-control-list/access-control-list-request-data.ts +++ b/packages/elastic-sip-trunking/src/models/v1/requests/access-control-list/access-control-list-request-data.ts @@ -1,16 +1,16 @@ -import { AccessControlList } from '../../access-control-list'; -import { IpRange } from '../../ip-range'; +import { IpRangeRequest } from '../../ip-range'; import { UpdateAccessControlListRequest } from '../../update-access-control-list-request'; +import { CreateAccessControlListRequest } from '../../create-access-control-list-request'; export interface AddIpRangeToAccessControlListRequestData { /** The ID of the access control list entry. that you want to work with */ 'accessControlListId': string; /** */ - 'addIpRangeRequestBody': IpRange; + 'addIpRangeRequestBody': IpRangeRequest; } export interface CreateAccessControlListRequestData { /** The Access Control List details used to create an Access Control List */ - 'createAccessControlListBody': AccessControlList; + 'createAccessControlListBody': CreateAccessControlListRequest; } export interface DeleteAccessControlListRequestData { /** The ID of the access control list entry. */ @@ -22,6 +22,10 @@ export interface DeleteIpRangeFromAccessControlListRequestData { /** The ID of the IP range that you want to update. */ 'ipRangeId': string; } +export interface GetAccessControlListRequestData { + /** The ID of the access control list entry that you want to retrieve. */ + 'id': string; +} export interface ListAccessControlListRequestData { } export interface ListIpRangesForAccessControlListRequestData { @@ -40,5 +44,5 @@ export interface UpdateIpRangeFromAccessControlListRequestData { /** The ID of the IP range that you want to update. */ 'ipRangeId': string; /** The IP range details used to update the IP range property from an Access Control List */ - 'updateIpRangeRequestBody': IpRange; + 'updateIpRangeRequestBody': IpRangeRequest; } diff --git a/packages/elastic-sip-trunking/src/models/v1/requests/calls-history/calls-history-request-data.ts b/packages/elastic-sip-trunking/src/models/v1/requests/calls-history/calls-history-request-data.ts index f22dc84a..c0faad56 100644 --- a/packages/elastic-sip-trunking/src/models/v1/requests/calls-history/calls-history-request-data.ts +++ b/packages/elastic-sip-trunking/src/models/v1/requests/calls-history/calls-history-request-data.ts @@ -1,4 +1,5 @@ import { CallResult, DirectionEnum } from '../../enum'; +import { DateFormat } from '@sinch/sdk-client'; export interface FindCallsRequestData { /** A phone number that you want to use to filter results. You can pass a partial number to get all calls sent to numbers that start with the number you passed. */ @@ -7,8 +8,10 @@ export interface FindCallsRequestData { 'to'?: string; /** Only include calls made from this trunk. */ 'trunkId'?: string; - /** Filter calls based on `createTime`. You make the query more precise, fewer results will be returned. For example, 2021-02-01 will return all calls from the first of February 2021, and 2021-02-01T14:00:00Z will return all calls after 14:00 on the first of February. This field also supports <= and >= to search for calls in a range ?createTime>=2021-10-01&createTime<=2021-10-30 to get a list if calls for october 2021 It is also possible to submit partial dates for example createTime=2021-02 will return all calls for February ***Defaults to 24 hours*** ***Internal notes*** If a customer submits = and not <> we should add min and max for the date range psueodo sql ``` createTime = 2021-02-01 select * from calls where createTime >= 2021-02-01 and createTime <= 2021-02-01T23:59:59Z createTime = 2021-02-01T08 select * from calls where createTime >= 2021-02-01T08:00:00 and createTime <= 2021-02-01T08:59:59Z ``` but if they submit < or > we should just use the value they submitted and parse it a complete date */ + /** Filter calls based on `createTime`. You make the query more precise, fewer results will be returned. For example, 2021-02-01 will return all calls from the first of February 2021, and 2021-02-01T14:00:00Z will return all calls after 14:00 on the first of February. */ 'createTime'?: string; + /** Filter calls based on `createTime`. It will filter the calls on a range of dates. */ + 'createTimeRange'?: DateRangeFilter; /** only include calls by on the callResult(s), example callResult=COMPLETED will return all calls which have completed normally. */ 'callResult'?: CallResult; /** only include calls by on the direction(s), example direction=INBOUND,OUTBOUND will return all calls that are inbound or outbound. */ @@ -18,3 +21,20 @@ export interface FindCallsRequestData { /** The maximum number of items to return per request. The default is 100 and the maximum is 1000. If you need to export larger amounts and pagination is not suitable for you can use the Export function in the dashboard. */ 'pageSize'?: number; } + +/** + * Filter calls based on `createTime`. If not value is submitted, the default value is the prior week. + * - `from: '2024-02-15'` will return all calls from February 2024, 15th + * - `from: '2024-02-01T14:00:00Z'` will return all calls after 14:00:00 on the first of February 2024. + * - `from: '2024-02-01T14:00:00Z'` + `to: '2024-02-01T15:00:00Z'` will return all calls between 14:00:00 and 15:00:00 (inclusive) on the first of February 2024. + * - `from: '2024-02-01'` + `to: '2024-02-29'` will return all calls for all of February 2024. + * + * Note: It is also possible to submit partial dates. + * - `from: '2024-02'` will return all calls for February 2024 + */ +export interface DateRangeFilter { + /** */ + from?: string | Date | DateFormat; + /** */ + to?: string | Date | DateFormat; +} diff --git a/packages/elastic-sip-trunking/src/models/v1/requests/sip-endpoints/sip-endpoints-request-data.ts b/packages/elastic-sip-trunking/src/models/v1/requests/sip-endpoints/sip-endpoints-request-data.ts index 04d74ba7..78d5f17f 100644 --- a/packages/elastic-sip-trunking/src/models/v1/requests/sip-endpoints/sip-endpoints-request-data.ts +++ b/packages/elastic-sip-trunking/src/models/v1/requests/sip-endpoints/sip-endpoints-request-data.ts @@ -4,7 +4,7 @@ export interface CreateSipEndpointRequestData { /** The ID of the SIP trunk. */ 'sipTrunkId': string; /** The body containing the SIP Endpoint to create for the SIP trunk */ - 'createSipEndpointRequestBody': SipEndpoint; + 'createSipEndpointRequestBody': Omit; } export interface DeleteSipEndpointRequestData { /** The ID of the SIP trunk. */ @@ -32,5 +32,5 @@ export interface UpdateSipEndpointRequestData { /** The ID of the SIP endpoint. */ 'sipEndpointId': string; /** The body containing the SIP Endpoint details to update */ - 'updateSipEndpointRequestBody': SipEndpoint; + 'updateSipEndpointRequestBody': Omit; } diff --git a/packages/elastic-sip-trunking/src/models/v1/requests/sip-trunks/sip-trunks-request-data.ts b/packages/elastic-sip-trunking/src/models/v1/requests/sip-trunks/sip-trunks-request-data.ts index 24197c43..da0f3f6c 100644 --- a/packages/elastic-sip-trunking/src/models/v1/requests/sip-trunks/sip-trunks-request-data.ts +++ b/packages/elastic-sip-trunking/src/models/v1/requests/sip-trunks/sip-trunks-request-data.ts @@ -9,7 +9,7 @@ export interface AddAccessControlListToTrunkRequestData { } export interface CreateSipTrunkRequestData { /** The SIP trunk details to be used to create a SIP trunk */ - 'createSipTrunkRequestBody': SipTrunk; + 'createSipTrunkRequestBody': Pick; } export interface DeleteAccessControlListFromTrunkRequestData { /** The ID of the trunk that you want to work with */ @@ -41,5 +41,5 @@ export interface UpdateSipTrunkRequestData { /** The ID of the SIP trunk. */ 'sipTrunkId': string; /** The SIP trunk details to be used to update the SIP trunk */ - 'updateSipTrunkRequestBody': SipTrunk; + 'updateSipTrunkRequestBody': Partial>; } diff --git a/packages/elastic-sip-trunking/src/models/v1/update-access-control-list-request/update-access-control-list-request.ts b/packages/elastic-sip-trunking/src/models/v1/update-access-control-list-request/update-access-control-list-request.ts index cfdef295..50a23a6e 100644 --- a/packages/elastic-sip-trunking/src/models/v1/update-access-control-list-request/update-access-control-list-request.ts +++ b/packages/elastic-sip-trunking/src/models/v1/update-access-control-list-request/update-access-control-list-request.ts @@ -1,4 +1,4 @@ -import { IpRange } from '../ip-range'; +import { IpRangeRequest } from '../ip-range'; export interface UpdateAccessControlListRequest { /** Your name for the access control list entry. */ @@ -6,5 +6,5 @@ export interface UpdateAccessControlListRequest { /** Whether the access control list entry is enabled. You can use this to disable a list temporarily without deleting it. */ enabled?: boolean; /** An array of all the IP ranges to update. */ - ipRanges?: IpRange[]; + ipRanges?: IpRangeRequest[]; } diff --git a/packages/elastic-sip-trunking/src/rest/v1/access-control-list/access-control-list-api.jest.fixture.ts b/packages/elastic-sip-trunking/src/rest/v1/access-control-list/access-control-list-api.jest.fixture.ts index eddf7b4b..29ee6db5 100644 --- a/packages/elastic-sip-trunking/src/rest/v1/access-control-list/access-control-list-api.jest.fixture.ts +++ b/packages/elastic-sip-trunking/src/rest/v1/access-control-list/access-control-list-api.jest.fixture.ts @@ -14,6 +14,7 @@ import { UpdateAccessControlListRequestData, UpdateIpRangeFromAccessControlListRequestData, AddAccessControlListToTrunk, + GetAccessControlListRequestData, } from '../../../models'; import { ApiListPromise } from '@sinch/sdk-client'; @@ -44,6 +45,11 @@ export class AccessControlListApiFixture implements Partial, [CreateAccessControlListRequestData]> = jest.fn(); + /** + * Fixture associated to function get + */ + public get: jest.Mock< + Promise, [GetAccessControlListRequestData]> = jest.fn(); /** * Fixture associated to function delete */ diff --git a/packages/elastic-sip-trunking/src/rest/v1/access-control-list/access-control-list-api.ts b/packages/elastic-sip-trunking/src/rest/v1/access-control-list/access-control-list-api.ts index 9a8e699f..fa954054 100644 --- a/packages/elastic-sip-trunking/src/rest/v1/access-control-list/access-control-list-api.ts +++ b/packages/elastic-sip-trunking/src/rest/v1/access-control-list/access-control-list-api.ts @@ -13,6 +13,7 @@ import { ListAccessControlListsForTrunkRequestData, AddAccessControlListToTrunk, IpRange, + GetAccessControlListRequestData, } from '../../../models'; import { RequestBody, @@ -183,6 +184,33 @@ export class AccessControlListApi extends ElasticSipTrunkingDomainApi { }); } + /** + * Get Access Control List + * Search for an Access Control List by ID. + * @param { GetAccessControlListRequestData } data - The data to provide to the API call. + */ + public async get(data: GetAccessControlListRequestData): Promise { + this.client = this.getSinchClient(); + const getParams = this.client.extractQueryParams(data, [] as never[]); + const headers: { [key: string]: string | undefined } = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; + + const body: RequestBody = ''; + const basePathUrl = `${this.client.apiClientOptions.hostname}/v1/projects/${this.client.apiClientOptions.projectId}/accessControlLists/${data['id']}`; + + const requestOptions = await this.client.prepareOptions(basePathUrl, 'GET', getParams, headers, body || undefined); + const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); + + return this.client.processCall({ + url, + requestOptions, + apiName: this.apiName, + operationId: 'GetAccessControlListById', + }); + } + /** * List ACLs * Fetches the list of Access Control List entries. @@ -205,7 +233,7 @@ export class AccessControlListApi extends ElasticSipTrunkingDomainApi { const operationProperties: PaginatedApiProperties = { pagination: PaginationEnum.PAGE2, apiName: this.apiName, - operationId: 'GetAccessControlList', + operationId: 'GetAccessControlLists', dataKey: 'accessControlLists', }; diff --git a/packages/elastic-sip-trunking/src/rest/v1/calls-history/calls-history-api.ts b/packages/elastic-sip-trunking/src/rest/v1/calls-history/calls-history-api.ts index 6c42951b..81a58167 100644 --- a/packages/elastic-sip-trunking/src/rest/v1/calls-history/calls-history-api.ts +++ b/packages/elastic-sip-trunking/src/rest/v1/calls-history/calls-history-api.ts @@ -7,6 +7,8 @@ import { PaginationEnum, buildPageResultPromise, createIteratorMethodsForPagination, + formatCreateTimeFilter, + formatCreateTimeRangeFilter, } from '@sinch/sdk-client'; import { ElasticSipTrunkingDomainApi } from '../elastic-sip-trunking-domain-api'; @@ -22,16 +24,18 @@ export class CallsHistoryApi extends ElasticSipTrunkingDomainApi { } /** - * Find calls * Find calls by query parameters. * @param { FindCallsRequestData } data - The data to provide to the API call. * @return { ApiListPromise } */ public find(data: FindCallsRequestData): ApiListPromise { this.client = this.getSinchClient(); - data['createTime'] = data['createTime'] !== undefined ? data['createTime'] : 'now-24h'; const getParams = this.client.extractQueryParams(data, [ 'from', 'to', 'trunkId', 'createTime', 'callResult', 'direction', 'page', 'pageSize']); + (getParams as any).createTime = JSON.stringify(formatCreateTimeFilter(data.createTime)); + (getParams as any)['createTime>'] = JSON.stringify(formatCreateTimeRangeFilter(data.createTimeRange?.from)); + (getParams as any)['createTime<'] = JSON.stringify(formatCreateTimeRangeFilter(data.createTimeRange?.to)); + const headers: { [key: string]: string | undefined } = { 'Content-Type': 'application/json', 'Accept': 'application/json', diff --git a/packages/elastic-sip-trunking/src/rest/v1/sip-endpoints/sip-endpoints-api.ts b/packages/elastic-sip-trunking/src/rest/v1/sip-endpoints/sip-endpoints-api.ts index 854a1b22..50a1aa88 100644 --- a/packages/elastic-sip-trunking/src/rest/v1/sip-endpoints/sip-endpoints-api.ts +++ b/packages/elastic-sip-trunking/src/rest/v1/sip-endpoints/sip-endpoints-api.ts @@ -95,8 +95,6 @@ export class SipEndpointsApi extends ElasticSipTrunkingDomainApi { */ public list(data: ListSipEndpointsRequestData): ApiListPromise { this.client = this.getSinchClient(); - data['page'] = data['page'] !== undefined ? data['page'] : 1; - data['pageSize'] = data['pageSize'] !== undefined ? data['pageSize'] : 1000; const getParams = this.client.extractQueryParams(data, ['page', 'pageSize']); const headers: { [key: string]: string | undefined } = { 'Content-Type': 'application/json', @@ -111,7 +109,7 @@ export class SipEndpointsApi extends ElasticSipTrunkingDomainApi { const operationProperties: PaginatedApiProperties = { pagination: PaginationEnum.PAGE2, apiName: this.apiName, - operationId: 'GetSipEndpoint', + operationId: 'GetSipEndpoints', dataKey: 'endpoints', }; diff --git a/packages/elastic-sip-trunking/src/rest/v1/sip-trunks/sip-trunks-api.ts b/packages/elastic-sip-trunking/src/rest/v1/sip-trunks/sip-trunks-api.ts index 22c32b60..06fd9ec4 100644 --- a/packages/elastic-sip-trunking/src/rest/v1/sip-trunks/sip-trunks-api.ts +++ b/packages/elastic-sip-trunking/src/rest/v1/sip-trunks/sip-trunks-api.ts @@ -226,8 +226,6 @@ export class SipTrunksApi extends ElasticSipTrunkingDomainApi { */ public list(data: ListSipTrunksRequestData): ApiListPromise { this.client = this.getSinchClient(); - data['page'] = data['page'] !== undefined ? data['page'] : 1; - data['pageSize'] = data['pageSize'] !== undefined ? data['pageSize'] : 1000; const getParams = this.client.extractQueryParams(data, ['page', 'pageSize', 'domain']); const headers: { [key: string]: string | undefined } = { 'Content-Type': 'application/json', diff --git a/packages/elastic-sip-trunking/tests/rest/v1/access-control-list/access-control-list-api.test.ts b/packages/elastic-sip-trunking/tests/rest/v1/access-control-list/access-control-list-api.test.ts index c14d4111..93a6bb11 100644 --- a/packages/elastic-sip-trunking/tests/rest/v1/access-control-list/access-control-list-api.test.ts +++ b/packages/elastic-sip-trunking/tests/rest/v1/access-control-list/access-control-list-api.test.ts @@ -131,6 +131,45 @@ describe('AccessControlListApi', () => { }); }); + describe ('getAccessControlListById', () => { + // eslint-disable-next-line max-len + it('should make a GET request to retrieve an access control list', async () => { + // Given + const requestData: ElasticSipTrunking.GetAccessControlListRequestData = { + id: '01HA9BRJW4J3QE4WBKVC337V4E', + }; + const expectedResponse: ElasticSipTrunking.AccessControlList = { + name: 'My new ACL', + projectId: '3acb7ae1-cf3d-4112-ba5e-3a9d8c71cd47', + enabled: true, + id: '01HA9BRJW4J3QE4WBKVC337V4E', + createTime: new Date('2023-09-14T07:39:19Z'), + updateTime: null, + ipRanges: [ + { + description: 'Location 1', + ipAddress: '15.15.15.15', + range: 20, + projectId: '3acb7ae1-cf3d-4112-ba5e-3a9d8c71cd47', + accessControlListId: '01HA9BRJW4J3QE4WBKVC337V4E', + id: '01HA9BRJYR9Q7ZBDYMXHVWT8S8', + createTime: new Date('2023-09-14T07:39:19Z'), + updateTime: null, + }, + ], + }; + + // When + fixture.get.mockResolvedValue(expectedResponse); + accessControlListApi.get = fixture.get; + const response = await accessControlListApi.get(requestData); + + // Then + expect(response).toEqual(expectedResponse); + expect(fixture.get).toHaveBeenCalledWith(requestData); + }); + }); + describe ('deleteAccessControlList', () => { it('should make a DELETE request to delete an access control list entry', async () => { // Given @@ -187,7 +226,7 @@ describe('AccessControlListApi', () => { }); }); - describe ('getAccessControlList', () => { + describe ('getAccessControlLists', () => { it('should make a GET request to fetch the list of Access Control List entries', async () => { // Given const requestData: ElasticSipTrunking.ListAccessControlListRequestData = {}; diff --git a/packages/elastic-sip-trunking/tests/rest/v1/access-control-list/access-control-lists.steps.ts b/packages/elastic-sip-trunking/tests/rest/v1/access-control-list/access-control-lists.steps.ts new file mode 100644 index 00000000..b83bf778 --- /dev/null +++ b/packages/elastic-sip-trunking/tests/rest/v1/access-control-list/access-control-lists.steps.ts @@ -0,0 +1,326 @@ +import { ElasticSipTrunking, ElasticSipTrunkingService, AccessControlListApi } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let accessControlListsApi: AccessControlListApi; +let accessControlList: ElasticSipTrunking.AccessControlList; +let listResponse: PageResult; +let aclsList: ElasticSipTrunking.AccessControlList[]; +let pagesIteration: number; +let deleteAclResponse: void; +let ipRange: ElasticSipTrunking.IpRange; +let listIpRangesResponse: PageResult; +let ipRangesList: ElasticSipTrunking.IpRange[]; +let deleteIpRangeResponse: void; +let addedAclsList: ElasticSipTrunking.AddAccessControlListToTrunk; +let listAclIdsResponse: PageResult; +let aclIdsList: string[]; +let deleteAclFromTrunkResponse: void; + +Given('the Elastic SIP Trunking service "Access Control Lists" is available', function () { + const elasticSipTrunkingService = new ElasticSipTrunkingService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + elasticSipTrunkingHostname: 'http://localhost:3016', + }); + accessControlListsApi = elasticSipTrunkingService.accessControlList; +}); + +When('I send a request to create an Access Control List', async () => { + accessControlList = await accessControlListsApi.create({ + createAccessControlListBody: { + name: 'My Access Control List', + ipRanges: [ + { + description: 'Location 1', + ipAddress: '15.15.15.15', + }, + ], + }, + }); +}); + +Then('the Access Control List is created', () => { + assert.equal(accessControlList.id, '01W4FFL35P4NC4K35SIPACL001'); + assert.equal(accessControlList.name, 'My Access Control List'); + assert.equal(accessControlList.enabled, true); + assert.equal(accessControlList.projectId, 'tinyfrog-jump-high-over-lilypadbasin'); + assert.deepEqual(accessControlList.createTime, new Date('2024-06-06T14:42:42.892741384Z')); + assert.equal(accessControlList.updateTime, null); + assert.ok(accessControlList.ipRanges); + const ipRange = accessControlList.ipRanges[0]; + assert.equal(ipRange.id, '01W4FFL35P4NC4K35IPRANGE01'); + assert.equal(ipRange.accessControlListId, '01W4FFL35P4NC4K35SIPACL001'); + assert.equal(ipRange.description, 'Location 1'); + assert.equal(ipRange.ipAddress, '15.15.15.15'); + assert.equal(ipRange.range, 32); + assert.equal(ipRange.projectId, 'tinyfrog-jump-high-over-lilypadbasin'); + assert.deepEqual(ipRange.createTime, new Date('2024-06-06T14:42:42.896695235Z')); + assert.equal(ipRange.updateTime, null); +}); + +When('I send a request to list the existing Access Control Lists', async () => { + listResponse = await accessControlListsApi.list({}); +}); + +Then('the response contains {string} Access Control Lists', (expectedAnswer: string) => { + const expectedAclsCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedAclsCount); + accessControlList = listResponse.data[0]; + assert.equal(accessControlList.id, '01W4FFL35P4NC4K35SIPACL001'); + assert.deepEqual(accessControlList.createTime, new Date('2024-06-06T14:42:42Z')); + const ipRange = accessControlList.ipRanges[0]; + assert.deepEqual(ipRange.createTime, new Date('2024-06-06T14:42:42Z')); +}); + +When('I send a request to list all the Access Control Lists', async () => { + aclsList = []; + for await (const acl of accessControlListsApi.list({})) { + aclsList.push(acl); + } +}); + +When('I iterate manually over the Access Control Lists pages', async () => { + aclsList = []; + listResponse = await accessControlListsApi.list({}); + aclsList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + aclsList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the Access Control Lists list contains {string} Access Control Lists', (expectedAnswer: string) => { + const expectedAclsCount = parseInt(expectedAnswer, 10); + assert.equal(aclsList.length, expectedAclsCount); +}); + +Then('the Access Control Lists iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve an Access Control List', async () => { + accessControlList = await accessControlListsApi.get({ + id: '01W4FFL35P4NC4K35SIPACL001', + }); +}); + +Then('the response contains the Access Control List details', () => { + assert.equal(accessControlList.id, '01W4FFL35P4NC4K35SIPACL001'); + assert.equal(accessControlList.name, 'My Access Control List'); + assert.deepEqual(accessControlList.createTime, new Date('2024-06-06T14:42:42Z')); + assert.ok(accessControlList.ipRanges); +}); + +When('I send a request to update an Access Control List', async () => { + accessControlList = await accessControlListsApi.update({ + id: '01W4FFL35P4NC4K35SIPACL003', + updateAccessControlListRequestBody: { + name: 'My Access Control List 3', + }, + }); +}); + +Then('the response contains the Access Control List details with updated data', () => { + assert.equal(accessControlList.id, '01W4FFL35P4NC4K35SIPACL003'); + assert.equal(accessControlList.name, 'My Access Control List 3'); + assert.deepEqual(accessControlList.createTime, new Date('2024-06-06T15:52:22Z')); + assert.deepEqual(accessControlList.updateTime, new Date('2024-06-06T15:52:52.554735034Z')); +}); + +When('I send a request to delete an Access Control List', async () => { + deleteAclResponse = await accessControlListsApi.delete({ + id: '01W4FFL35P4NC4K35SIPACL001', + }); +}); + +Then('the delete Access Control List response contains no data', () => { + assert.deepEqual(deleteAclResponse, {} ); +}); + +When('I send a request to add an IP Range to an Access Control List', async () => { + ipRange = await accessControlListsApi.addIpRange({ + accessControlListId: '01W4FFL35P4NC4K35SIPACL001', + addIpRangeRequestBody: { + description: 'West wing', + ipAddress: '10.0.1.1', + range: 24, + }, + }); +}); + +Then('the response contains the created IP range associated to the Access Control List', () => { + assert.equal(ipRange.id, '01W4FFL35P4NC4K35IPRANGE06'); + assert.equal(ipRange.accessControlListId, '01W4FFL35P4NC4K35SIPACL003'); + assert.equal(ipRange.description, 'West wing'); + assert.equal(ipRange.ipAddress, '10.0.1.1'); + assert.equal(ipRange.range, 24); + assert.equal(ipRange.projectId, 'tinyfrog-jump-high-over-lilypadbasin'); + assert.deepEqual(ipRange.createTime, new Date('2024-06-06T15:56:26.70848666Z')); + assert.equal(ipRange.updateTime, null); +}); + +When('I send a request to list the existing IP Ranges', async () => { + listIpRangesResponse = await accessControlListsApi.listIpRanges({ + accessControlListId: '01W4FFL35P4NC4K35SIPACL002', + }); +}); + +Then('the response contains {string} IP Ranges', (expectedAnswer: string) => { + const expectedIpRangesCount = parseInt(expectedAnswer, 10); + assert.equal(listIpRangesResponse.data.length, expectedIpRangesCount); +}); + +When('I send a request to list all the IP Ranges', async () => { + ipRangesList = []; + for await (const ipRange of accessControlListsApi.listIpRanges({ + accessControlListId: '01W4FFL35P4NC4K35SIPACL002', + })) { + ipRangesList.push(ipRange); + } +}); + +When('I iterate manually over the IP Ranges pages', async () => { + ipRangesList = []; + listIpRangesResponse = await accessControlListsApi.listIpRanges({ + accessControlListId: '01W4FFL35P4NC4K35SIPACL002', + }); + ipRangesList.push(...listIpRangesResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listIpRangesResponse.hasNextPage) { + listIpRangesResponse = await listIpRangesResponse.nextPage(); + ipRangesList.push(...listIpRangesResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the IP Ranges list contains {string} IP Ranges', (expectedAnswer: string) => { + const expectedIpRangesCount = parseInt(expectedAnswer, 10); + assert.equal(ipRangesList.length, expectedIpRangesCount); +}); + +Then('the IP Ranges iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to update an IP Range', async () => { + ipRange = await accessControlListsApi.updateIpRange({ + accessControlListId: '01W4FFL35P4NC4K35SIPACL003', + ipRangeId: '01W4FFL35P4NC4K35IPRANGE06', + updateIpRangeRequestBody: { + description: 'West wing - updated', + ipAddress: '10.0.1.2', + range: 16, + }, + }); +}); + +Then('the response contains the IP Range details with updated data', () => { + assert.equal(ipRange.id, '01W4FFL35P4NC4K35IPRANGE06'); + assert.equal(ipRange.description, 'West wing - updated'); + assert.equal(ipRange.ipAddress, '10.0.1.2'); + assert.equal(ipRange.range, 16); + assert.deepEqual(ipRange.createTime, new Date('2024-06-06T15:56:26Z')); + assert.deepEqual(ipRange.updateTime, new Date('2024-06-06T15:58:07.295895288Z')); +}); + +When('I send a request to delete an IP Range', async () => { + deleteIpRangeResponse = await accessControlListsApi.deleteIpRange({ + accessControlListId: '01W4FFL35P4NC4K35SIPACL003', + ipRangeId: '01W4FFL35P4NC4K35IPRANGE06', + }); +}); + +Then('the delete IP Range response contains no data', () => { + assert.deepEqual(deleteIpRangeResponse, {} ); +}); + +When('I send a request to add ACLs to a SIP trunk [using the ACL service]', async () => { + addedAclsList = await accessControlListsApi.addToTrunk({ + trunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + addAccessControlListToTrunkRequestBody: { + accessControlListIds: [ + '01W4FFL35P4NC4K35TRUNKACL1', + ], + }, + }); +}); + +Then('the response contains the list of ACLs added to the trunk [using the ACL service]', () => { + assert.ok(addedAclsList.accessControlListIds); + assert.equal(addedAclsList.accessControlListIds.length, 1); +}); + +When('I send a request to list the existing ACLs for a SIP Trunk [using the ACL service]', async () => { + listAclIdsResponse = await accessControlListsApi.listForTrunk({ + trunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + }); +}); + +Then('the response [from the ACL service] contains {string} ACLs for a SIP Trunk', (expectedAnswer: string) => { + const expectedMessagesCount = parseInt(expectedAnswer, 10); + assert.equal(listAclIdsResponse.data.length, expectedMessagesCount); +}); + +When('I send a request to list all the ACLs for a SIP Trunk [using the ACL service]', async () => { + aclIdsList = []; + for await (const acl of accessControlListsApi.listForTrunk({ trunkId: '01W4FFL35P4NC4K35SIPTRUNK1' })) { + aclIdsList.push(acl); + } +}); + +When('I iterate manually over the ACLs for a SIP Trunk pages [using the ACL service]', async () => { + aclIdsList = []; + listAclIdsResponse = await accessControlListsApi.listForTrunk({ trunkId: '01W4FFL35P4NC4K35SIPTRUNK1' }); + aclIdsList.push(...listAclIdsResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listAclIdsResponse.hasNextPage) { + listAclIdsResponse = await listAclIdsResponse.nextPage(); + aclIdsList.push(...listAclIdsResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the ACLs list [from the ACL service] contains {string} ACLs for a SIP Trunk', (expectedAnswer: string) => { + const expectedAclIdsListCount = parseInt(expectedAnswer, 10); + assert.equal(aclIdsList.length, expectedAclIdsListCount); +}); + +// eslint-disable-next-line max-len +Then('the ACLs for a SIP Trunk iteration result [using the ACL service] contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to delete an ACL from a SIP trunk [using the ACL service]', async () => { + deleteAclFromTrunkResponse = await accessControlListsApi.deleteFromTrunk({ + trunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + accessControlListId: '01W4FFL35P4NC4K35TRUNKACL1', + }); +}); + +Then('the delete ACL from a SIP trunk response [using the ACL service] contains no data', () => { + assert.deepEqual(deleteAclFromTrunkResponse, {} ); +}); diff --git a/packages/elastic-sip-trunking/tests/rest/v1/calls-history/calls-history-api.test.ts b/packages/elastic-sip-trunking/tests/rest/v1/calls-history/calls-history-api.test.ts index 5801b35f..1a839b51 100644 --- a/packages/elastic-sip-trunking/tests/rest/v1/calls-history/calls-history-api.test.ts +++ b/packages/elastic-sip-trunking/tests/rest/v1/calls-history/calls-history-api.test.ts @@ -28,19 +28,19 @@ describe('CallsApi', () => { callId: '01AQ3D80ZKSSK35TZFKM3JG9CT', to: '+15551239898', from: '+14155553434', - direction: 'INBOUND', + direction: 'inbound', answerTime: new Date('2021-11-01T23:26:50Z'), endTime: new Date('2021-11-01T23:27:35Z'), durationSeconds: 45, callResult: 'COMPLETED', pricePerMinute: { currencyCode: 'USD', - amount: '0.0040', + amount: 0.0040, }, billingDurationSeconds: 60, price: { currencyCode: 'USD', - amount: '0.0040', + amount: 0.0040, }, createTime: new Date('2021-11-01T23:20:50Z'), projectId: '1bf62742-7b84-4666-9cbe-8e5734fd57d0', diff --git a/packages/elastic-sip-trunking/tests/rest/v1/calls-history/calls-history.steps.ts b/packages/elastic-sip-trunking/tests/rest/v1/calls-history/calls-history.steps.ts new file mode 100644 index 00000000..8704b398 --- /dev/null +++ b/packages/elastic-sip-trunking/tests/rest/v1/calls-history/calls-history.steps.ts @@ -0,0 +1,95 @@ +import { ElasticSipTrunking, ElasticSipTrunkingService, CallsHistoryApi } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let callsHistoryApi: CallsHistoryApi; +let listResponse: PageResult; +let callsHistoryList: ElasticSipTrunking.Call[]; +let pagesIteration: number; + +Given('the Elastic SIP Trunking service "Calls History" is available', function () { + const elasticSipTrunkingService = new ElasticSipTrunkingService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + elasticSipTrunkingHostname: 'http://localhost:3016', + }); + callsHistoryApi = elasticSipTrunkingService.calls; +}); + +When('I send a request to find the a page from the Calls History with no filtering parameters', async () => { + listResponse = await callsHistoryApi.find({}); +}); + +Then('the response contains {string} Calls from history', (expectedAnswer: string) => { + const expectedCallsCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedCallsCount); +}); + +Then('a Call History object from the page result contains the Call History details', () => { + const call = listResponse.data[0]; + assert.equal(call.callId, 'N00DL3C4T5'); + assert.equal(call.to, '12017777777'); + assert.equal(call.from, 'sip:12015555555@76.184.202.212'); + const direction: ElasticSipTrunking.DirectionEnum = 'outbound'; + assert.equal(call.direction, direction); + assert.deepEqual(call.answerTime, new Date('2024-06-06T16:57:52Z')); + assert.deepEqual(call.endTime, new Date('2024-06-06T16:57:55Z')); + assert.equal(call.durationSeconds, 4); + assert.equal(call.billingDurationSeconds, 60); + const callResult: ElasticSipTrunking.CallResult = 'COMPLETED'; + assert.equal(call.callResult, callResult); + const price: ElasticSipTrunking.Money = { + amount: 0.004, + currencyCode: 'USD', + }; + assert.deepEqual(call.pricePerMinute, price); + assert.deepEqual(call.price, price); + assert.deepEqual(call.createTime, new Date('2024-06-06T16:57:45+0000')); + assert.equal(call.projectId, 'tinyfrog-jump-high-over-lilypadbasin'); + assert.equal(call.trunkId, '01W4FFL35P4NC4K35SIPTRUNK2'); +}); + +When('I send a request to list all the Calls from the Calls History', async () => { + callsHistoryList = []; + for await (const call of callsHistoryApi.find({})) { + callsHistoryList.push(call); + } +}); + +When('I iterate manually over the Calls History pages', async () => { + callsHistoryList = []; + listResponse = await callsHistoryApi.find({}); + callsHistoryList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + callsHistoryList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the Calls History list contains {string} Calls', (expectedAnswer: string) => { + const expectedSipEndpointsCount = parseInt(expectedAnswer, 10); + assert.equal(callsHistoryList.length, expectedSipEndpointsCount); +}); + +Then('the Calls History iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to find the a page from the Calls History with a createTime range filter', async () => { + listResponse = await callsHistoryApi.find({ + createTimeRange: { + from: '2024-06-06T16:00:00', + }, + }); +}); diff --git a/packages/elastic-sip-trunking/tests/rest/v1/country-permissions/country-permissions.steps.ts b/packages/elastic-sip-trunking/tests/rest/v1/country-permissions/country-permissions.steps.ts new file mode 100644 index 00000000..17d554a6 --- /dev/null +++ b/packages/elastic-sip-trunking/tests/rest/v1/country-permissions/country-permissions.steps.ts @@ -0,0 +1,58 @@ +import { CountryPermissionsApi, ElasticSipTrunkingService, ElasticSipTrunking } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import assert from 'assert'; + +let countryPermissionsApi: CountryPermissionsApi; +let countryPermissionsListResponse: ElasticSipTrunking.ListCountryPermissionsResponse; +let countryPermissions: ElasticSipTrunking.CountryPermission; + +Given('the Elastic SIP Trunking service "Country Permissions" is available', function () { + const elasticSipTrunkingService = new ElasticSipTrunkingService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + elasticSipTrunkingHostname: 'http://localhost:3016', + }); + countryPermissionsApi = elasticSipTrunkingService.countryPermissions; +}); + +When('I send a request to list the EST countries permissions', async () => { + countryPermissionsListResponse = await countryPermissionsApi.list({}); +}); + +Then('the response contains the list of EST countries permissions', () => { + assert.ok(countryPermissionsListResponse.countryPermissions); + assert.equal(countryPermissionsListResponse.countryPermissions.length, 51); +}); + +When('I send a request to retrieve an EST country\'s permissions', async () => { + countryPermissions = await countryPermissionsApi.get({ + isoCode: 'SE', + }); +}); + +Then('the response contains the EST country\'s permissions details', () => { + assert.equal(countryPermissions.isoCode, 'SE'); + assert.equal(countryPermissions.name, 'Sweden'); + assert.equal(countryPermissions.continent, 'Europe'); + assert.deepEqual(countryPermissions.countryDialingCodes, ['+46']); + assert.equal(countryPermissions.enabled, false); +}); + +When('I send a request to update an EST country\'s permissions', async () => { + countryPermissions = await countryPermissionsApi.update({ + isoCode: 'SE', + updateCountryPermissionRequestBody: { + enabled: true, + }, + }); +}); + +Then('the response contains the EST country\'s permissions details with updated data', () => { + assert.equal(countryPermissions.isoCode, 'SE'); + assert.equal(countryPermissions.name, 'Sweden'); + assert.equal(countryPermissions.continent, 'Europe'); + assert.deepEqual(countryPermissions.countryDialingCodes, ['+46']); + assert.equal(countryPermissions.enabled, true); +}); diff --git a/packages/elastic-sip-trunking/tests/rest/v1/sip-endpoints/sip-endpoints.steps.ts b/packages/elastic-sip-trunking/tests/rest/v1/sip-endpoints/sip-endpoints.steps.ts new file mode 100644 index 00000000..c4023cc9 --- /dev/null +++ b/packages/elastic-sip-trunking/tests/rest/v1/sip-endpoints/sip-endpoints.steps.ts @@ -0,0 +1,141 @@ +import { ElasticSipTrunking, ElasticSipTrunkingService, SipEndpointsApi } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let sipEndpointsApi: SipEndpointsApi; +let sipEndpoint: ElasticSipTrunking.SipEndpoint; +let listResponse: PageResult; +let sipEndpointsList: ElasticSipTrunking.SipEndpoint[]; +let pagesIteration: number; +let deleteSipEndpointResponse: void; + +Given('the Elastic SIP Trunking service "SIP Endpoints" is available', function () { + const elasticSipTrunkingService = new ElasticSipTrunkingService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + elasticSipTrunkingHostname: 'http://localhost:3016', + }); + sipEndpointsApi = elasticSipTrunkingService.sipEndpoints; +}); + +When('I send a request to create a SIP Endpoint', async () => { + sipEndpoint = await sipEndpointsApi.create({ + sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + createSipEndpointRequestBody: { + name: 'Capsule Corp Endpoint', + address: '127.0.0.1', + priority: 2, + }, + }); +}); + +Then('the SIP Endpoint is created', () => { + assert.equal(sipEndpoint.id, '01W4FFL35P4NC4K35SIPENDP01'); + assert.equal(sipEndpoint.sipTrunkId, '01W4FFL35P4NC4K35SIPTRUNK1'); + assert.equal(sipEndpoint.name, 'Capsule Corp Endpoint'); + assert.equal(sipEndpoint.address, '127.0.0.1'); + assert.equal(sipEndpoint.port, 5060); + assert.equal(sipEndpoint.transport, 'UDP'); + assert.equal(sipEndpoint.priority, 2); + assert.equal(sipEndpoint.enabled, true); + assert.deepEqual(sipEndpoint.createTime, new Date('2024-06-06T14:42:42.337854345Z')); + assert.equal(sipEndpoint.updateTime, null); +}); + +When('I send a request to list the existing SIP Endpoints', async () => { + listResponse = await sipEndpointsApi.list({ + sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + }); +}); + +Then('the response contains {string} SIP Endpoints', (expectedAnswer: string) => { + const expectedSipEndpointsCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedSipEndpointsCount); +}); + +When('I send a request to list all the SIP Endpoints', async () => { + sipEndpointsList = []; + for await (const sipEndpoint of sipEndpointsApi.list({ sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1' })) { + sipEndpointsList.push(sipEndpoint); + } +}); + +When('I iterate manually over the SIP Endpoints pages', async () => { + sipEndpointsList = []; + listResponse = await sipEndpointsApi.list({ sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1' }); + sipEndpointsList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + sipEndpointsList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the SIP Endpoints list contains {string} SIP Endpoints', (expectedAnswer: string) => { + const expectedSipEndpointsCount = parseInt(expectedAnswer, 10); + assert.equal(sipEndpointsList.length, expectedSipEndpointsCount); +}); + +Then('the SIP Endpoints iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve a SIP Endpoint', async () => { + sipEndpoint = await sipEndpointsApi.get({ + sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + sipEndpointId: '01W4FFL35P4NC4K35SIPENDP01', + }); +}); + +Then('the response contains the SIP Endpoint details', () => { + assert.equal(sipEndpoint.id, '01W4FFL35P4NC4K35SIPENDP01'); + assert.deepEqual(sipEndpoint.createTime, new Date('2024-06-06T14:42:42Z')); +}); + +When('I send a request to update a SIP Endpoint', async () => { + sipEndpoint = await sipEndpointsApi.update({ + sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + sipEndpointId: '01W4FFL35P4NC4K35SIPENDP01', + updateSipEndpointRequestBody: { + name: 'Capsule Corp Endpoint - updated', + address: '127.0.0.2', + priority: 3, + port: 5061, + transport: 'TCP', + enabled: false, + }, + }); +}); + +Then('the response contains the SIP Endpoint details with updated data', () => { + assert.equal(sipEndpoint.id, '01W4FFL35P4NC4K35SIPENDP01'); + assert.equal(sipEndpoint.name, 'Capsule Corp Endpoint - updated'); + assert.equal(sipEndpoint.address, '127.0.0.2'); + assert.equal(sipEndpoint.port, 5061); + assert.equal(sipEndpoint.transport, 'TCP'); + assert.equal(sipEndpoint.priority, 3); + assert.equal(sipEndpoint.enabled, false); + assert.deepEqual(sipEndpoint.createTime, new Date('2024-06-06T14:42:42Z')); + assert.deepEqual(sipEndpoint.updateTime, new Date('2024-06-06T14:45:11.428052267Z')); +}); + +When('I send a request to delete a SIP Endpoint', async () => { + deleteSipEndpointResponse = await sipEndpointsApi.delete({ + sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + sipEndpointId: '01W4FFL35P4NC4K35SIPENDP01', + }); +}); + +Then('the delete SIP Endpoint response contains no data', () => { + assert.deepEqual(deleteSipEndpointResponse, {} ); +}); diff --git a/packages/elastic-sip-trunking/tests/rest/v1/sip-trunks/sip-trunks.steps.ts b/packages/elastic-sip-trunking/tests/rest/v1/sip-trunks/sip-trunks.steps.ts new file mode 100644 index 00000000..45c2857a --- /dev/null +++ b/packages/elastic-sip-trunking/tests/rest/v1/sip-trunks/sip-trunks.steps.ts @@ -0,0 +1,218 @@ +import { ElasticSipTrunking, ElasticSipTrunkingService, SipTrunksApi } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let sipTrunkApi: SipTrunksApi; +let sipTrunk: ElasticSipTrunking.SipTrunk; +let listResponse: PageResult; +let sipTrunksList: ElasticSipTrunking.SipTrunk[]; +let pagesIteration: number; +let deleteSipTrunkResponse: void; +let aclIdsList: ElasticSipTrunking.AddAccessControlListToTrunk; +let listAclsResponse: PageResult; +let aclsList: string[]; +let deleteAclFromTrunkResponse: void; + +Given('the Elastic SIP Trunking service "SIP Trunks" is available', function () { + const elasticSipTrunkingService = new ElasticSipTrunkingService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + elasticSipTrunkingHostname: 'http://localhost:3016', + }); + sipTrunkApi = elasticSipTrunkingService.sipTrunks; +}); + +When('I send a request to create a SIP Trunk', async () => { + sipTrunk = await sipTrunkApi.create({ + createSipTrunkRequestBody: { + name: 'Friendly name for e2e test', + hostName: 'e2e-sip-trunk-domain', + }, + }); +}); + +Then('the SIP Trunk is created', () => { + assert.equal(sipTrunk.id, '01W4FFL35P4NC4K35SIPTRUNK1'); + assert.equal(sipTrunk.projectId, 'tinyfrog-jump-high-over-lilypadbasin'); + assert.equal(sipTrunk.name, 'Friendly name for e2e test'); + assert.equal(sipTrunk.hostName, 'e2e-sip-trunk-domain'); + assert.equal(sipTrunk.domain, 'e2e-sip-trunk-domain.pstn.sinch.com'); + assert.equal(sipTrunk.topLevelDomain, 'pstn.sinch.com'); + assert.equal(sipTrunk.callsPerSecond, 1); + assert.equal(sipTrunk.enableCallerName, false); + assert.deepEqual(sipTrunk.createTime, new Date('2024-06-06T14:42:42.820177628Z')); + assert.equal(sipTrunk.updateTime, null); +}); + +When('I send a request to list the existing SIP trunks', async () => { + listResponse = await sipTrunkApi.list({}); +}); + +Then('the response contains {string} SIP trunks', (expectedAnswer: string) => { + const expectedSipTrunksCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedSipTrunksCount); +}); + +When('I send a request to list all the SIP trunks', async () => { + sipTrunksList = []; + for await (const sipTrunk of sipTrunkApi.list({})) { + sipTrunksList.push(sipTrunk); + } +}); + +When('I iterate manually over the SIP trunks pages', async () => { + sipTrunksList = []; + listResponse = await sipTrunkApi.list({}); + sipTrunksList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + sipTrunksList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the SIP trunks list contains {string} SIP trunks', (expectedAnswer: string) => { + const expectedSipTrunksCount = parseInt(expectedAnswer, 10); + assert.equal(sipTrunksList.length, expectedSipTrunksCount); +}); + +Then('the SIP trunks iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve a SIP trunk', async () => { + sipTrunk = await sipTrunkApi.get({ + sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + }); +}); + +Then('the response contains the SIP trunk details', () => { + assert.equal(sipTrunk.id, '01W4FFL35P4NC4K35SIPTRUNK1'); + assert.deepEqual(sipTrunk.createTime, new Date('2024-06-06T14:42:42Z')); +}); + +When('I send a request to update a SIP trunk', async () => { + sipTrunk = await sipTrunkApi.update({ + sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + updateSipTrunkRequestBody: { + name: 'Updated name for e2e test', + hostName: 'us-sip-trunk-domain', + enableCallerName: true, + }, + }); +}); + +Then('the response contains the SIP trunk details with updated data', () => { + assert.equal(sipTrunk.id, '01W4FFL35P4NC4K35SIPTRUNK1'); + assert.equal(sipTrunk.name, 'Updated name for e2e test'); + assert.equal(sipTrunk.hostName, 'us-sip-trunk-domain'); + assert.equal(sipTrunk.domain, 'e2e-sip-trunk-domain.pstn.sinch.com'); + assert.equal(sipTrunk.enableCallerName, true); + assert.deepEqual(sipTrunk.createTime, new Date('2024-06-06T14:42:42Z')); + assert.deepEqual(sipTrunk.updateTime, new Date('2024-06-06T14:48:14.87833248Z')); +}); + +When('I send a request to delete a SIP trunk', async () => { + deleteSipTrunkResponse = await sipTrunkApi.delete({ + sipTrunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + }); +}); + +Then('the delete SIP trunk response contains no data', () => { + assert.deepEqual(deleteSipTrunkResponse, {} ); +}); + +When('I send a request to add ACLs to a SIP trunk', async () => { + aclIdsList = await sipTrunkApi.addAccessControlList({ + trunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + addAccessControlListToTrunkRequestBody: { + accessControlListIds: [ + '01W4FFL35P4NC4K35TRUNKACL1', + ], + }, + }); +}); + +Then('the response contains the list of ACLs added to the trunk', () => { + assert.ok(aclIdsList.accessControlListIds); + assert.equal(aclIdsList.accessControlListIds.length, 1); +}); + +When('I send a request to add an empty ACLs list to a SIP trunk', async () => { + aclIdsList = await sipTrunkApi.addAccessControlList({ + trunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + addAccessControlListToTrunkRequestBody: { + accessControlListIds: [], + }, + }); +}); + +Then('the response contains unexpectedly an empty JSON object', () => { + assert.deepEqual(aclIdsList, {}); +}); + +When('I send a request to list the existing ACLs for a SIP Trunk', async () => { + listAclsResponse = await sipTrunkApi.listAccessControlLists({ + trunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + }); +}); + +Then('the response contains {string} ACLs for a SIP Trunk', (expectedAnswer: string) => { + const expectedMessagesCount = parseInt(expectedAnswer, 10); + assert.equal(listAclsResponse.data.length, expectedMessagesCount); +}); + +When('I send a request to list all the ACLs for a SIP Trunk', async () => { + aclsList = []; + for await (const acl of sipTrunkApi.listAccessControlLists({ trunkId: '01W4FFL35P4NC4K35SIPTRUNK1' })) { + aclsList.push(acl); + } +}); + +When('I iterate manually over the ACLs for a SIP Trunk pages', async () => { + aclsList = []; + listAclsResponse = await sipTrunkApi.listAccessControlLists({ trunkId: '01W4FFL35P4NC4K35SIPTRUNK1' }); + aclsList.push(...listAclsResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listAclsResponse.hasNextPage) { + listAclsResponse = await listAclsResponse.nextPage(); + aclsList.push(...listAclsResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the ACLs list contains {string} ACLs for a SIP Trunk', (expectedAnswer: string) => { + const expectedServices = parseInt(expectedAnswer, 10); + assert.equal(sipTrunksList.length, expectedServices); +}); + +Then('the ACLs for a SIP Trunk iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to delete an ACL from a SIP trunk', async () => { + deleteAclFromTrunkResponse = await sipTrunkApi.deleteAccessControlList({ + trunkId: '01W4FFL35P4NC4K35SIPTRUNK1', + accessControlListId: '01W4FFL35P4NC4K35TRUNKACL1', + }); +}); + +Then('the delete ACL from a SIP trunk response contains no data', () => { + assert.deepEqual(deleteAclFromTrunkResponse, {} ); +}); diff --git a/packages/fax/CHANGELOG.md b/packages/fax/CHANGELOG.md index d8c2044e..61c6c108 100644 --- a/packages/fax/CHANGELOG.md +++ b/packages/fax/CHANGELOG.md @@ -1,3 +1,11 @@ +## Version 1.2.0 +- [Tech] Update dependency `@sinch/sdk-client` to `1.2.0`. +- [Feature] Support date range filter for listing faxes. +- [Feature] Add `emails.listForNumber()` method (identical to `services.listEmailsForNumber()`). +- [Bugfix] Fix `faxes.send()` to send one or several faxes over JSON or FormData. Add examples in the [simple-examples](https://github.com/sinch/sinch-sdk-node/tree/main/examples/simple-examples/src/fax/faxes) package. +- [Feature][Breaking] Update interfaces according to OAS updates. +- [E2E] Add Cucumber steps implementation. + ## Version 1.1.0 - [Tech] Update dependency `@sinch/sdk-client` to `1.1.0` @@ -6,7 +14,7 @@ ## Version 0.0.5 - [Tech] Update dependency `@sinch/sdk-client` to `0.0.5` -- [Tech][Breaking] Export all model interfaces under the namespace `Fax` +- [Tech][Breaking TS] Export all model interfaces under the namespace `Fax` - [Bugfix] Fix pagination - [Bugfix] Fix content format sent when using `multipart/form-data`: boolean is not allowed - [Feature] Support hostname override diff --git a/packages/fax/cucumber.js b/packages/fax/cucumber.js new file mode 100644 index 00000000..d03dd4ab --- /dev/null +++ b/packages/fax/cucumber.js @@ -0,0 +1,8 @@ +module.exports = { + default: [ + 'tests/e2e/features/**/*.feature', + '--require-module ts-node/register', + '--require tests/rest/v3/**/*.steps.ts', + `--format-options '{"snippetInterface": "synchronous"}'`, + ].join(' '), +}; diff --git a/packages/fax/package.json b/packages/fax/package.json index 43529112..05b8b216 100644 --- a/packages/fax/package.json +++ b/packages/fax/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/fax", - "version": "1.1.0", + "version": "1.2.0", "description": "Sinch Fax API", "homepage": "", "repository": { @@ -25,13 +25,15 @@ "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf dist tsconfig.tsbuildinfo", - "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests && rimraf tsconfig.build.tsbuildinfo" + "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests && rimraf tsconfig.build.tsbuildinfo", + "test:e2e": "cucumber-js" }, "dependencies": { - "@sinch/sdk-client": "^1.1.0" + "@sinch/sdk-client": "^1.2.0" }, "devDependencies": {}, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/fax/src/models/v3/date-range-filter/date-range-filter.ts b/packages/fax/src/models/v3/date-range-filter/date-range-filter.ts new file mode 100644 index 00000000..758ce222 --- /dev/null +++ b/packages/fax/src/models/v3/date-range-filter/date-range-filter.ts @@ -0,0 +1,18 @@ +import { DateFormat } from '@sinch/sdk-client'; + +/** + * Filter calls based on `createTime`. If not value is submitted, the default value is the prior week. + * - `from: '2024-02-15'` will return all faxes from February 2024, 15th + * - `from: '2024-02-01T14:00:00Z'` will return all faxes after 14:00:00 on the first of February 2024. + * - `from: '2024-02-01T14:00:00Z'` + `to: '2024-02-01T15:00:00Z'` will return all faxes between 14:00:00 and 15:00:00 (inclusive) on the first of February 2024. + * - `from: '2024-02-01'` + `to: '2024-02-29'` will return all faxes for all of February 2024. + * + * Note: It is also possible to submit partial dates. + * - `from: '2024-02'` will return all faxes for February 2024 + */ +export interface DateRangeFilter { + /** */ + from?: string | Date | DateFormat; + /** */ + to?: string | Date | DateFormat; +} diff --git a/packages/fax/src/models/v3/date-range-filter/index.ts b/packages/fax/src/models/v3/date-range-filter/index.ts new file mode 100644 index 00000000..8ee98119 --- /dev/null +++ b/packages/fax/src/models/v3/date-range-filter/index.ts @@ -0,0 +1 @@ +export type { DateRangeFilter } from './date-range-filter'; diff --git a/packages/fax/src/models/v3/enums.ts b/packages/fax/src/models/v3/enums.ts index 7ce8b33a..74a30bb5 100644 --- a/packages/fax/src/models/v3/enums.ts +++ b/packages/fax/src/models/v3/enums.ts @@ -1,11 +1,3 @@ -// export type { TypeEnum as BarCodeTypeEnum } from './bar-code/bar-code'; -// export type { TypeEnum as CallErrorTypeEnum, ErrorCodeEnum as CallErrorErrorCodeEnum } from './call-error/call-error'; -// export type { TypeEnum as DocumentConversionErrorTypeEnum, ErrorCodeEnum as DocumentConversionErrorErrorCodeEnum } from './document-conversion-error/document-conversion-error'; -// export type { CallbackContentTypeEnum as FaxCallbackContentTypeEnum, ImageConversionMethodEnum as FaxImageConversionMethodEnum } from './fax/fax'; -// export type { FileTypeEnum as FaxBase64FileFileTypeEnum } from './fax-base64-file/fax-base64-file'; -// export type { TypeEnum as FaxErrorTypeEnum } from './fax-error/fax-error'; -// export type { CallbackContentTypeEnum as SendFaxRequest1CallbackContentTypeEnum, ImageConversionMethodEnum as SendFaxRequest1ImageConversionMethodEnum } from './send-fax-request1/send-fax-request1'; - export type ImageConversionMethod = 'HALFTONE' | 'MONOCHROME'; export type WebhookContentType = 'multipart/form-data' | 'application/json'; @@ -21,7 +13,7 @@ export type FaxDirection = 'OUTBOUND' | 'INBOUND'; export type FaxStatus = 'QUEUED' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILURE'; /** - * Error type + * Type of error for the fax */ export type ErrorType = 'DOCUMENT_CONVERSION_ERROR' @@ -33,4 +25,7 @@ export type ErrorType = export type FaxBase64FileType = 'DOC' | 'DOCX' | 'PDF' | 'TIF' | 'JPG' | 'ODT' | 'TXT' | 'HTML' | 'PNG'; +export const validBase64FileTypes: FaxBase64FileType[] + = ['DOC', 'DOCX', 'PDF', 'TIF', 'JPG', 'ODT', 'TXT', 'HTML', 'PNG']; + export type FaxWebhookEvent = 'INCOMING_FAX' | 'FAX_COMPLETED'; diff --git a/packages/fax/src/models/v3/fax-request/fax-request.ts b/packages/fax/src/models/v3/fax-request/fax-request.ts index 1571a84a..6ae9ff88 100644 --- a/packages/fax/src/models/v3/fax-request/fax-request.ts +++ b/packages/fax/src/models/v3/fax-request/fax-request.ts @@ -2,11 +2,11 @@ import { ImageConversionMethod, WebhookContentType } from '../enums'; import { FaxContentUrl } from '../fax-content-url'; import { FaxBase64File } from '../fax-base64-file'; -export type FaxRequest = FaxRequestJson | FaxRequestFormData; +export type SingleFaxRequest = SingleFaxRequestJson | SingleFaxRequestFormData; -interface FaxRequestBase { - /** A phone number in [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537) format, including the leading '+'. */ - to: string; +export type MultipleFaxRequest = MultipleFaxRequestJson | MultipleFaxRequestFormData; + +export interface FaxRequestBase { /** A phone number in [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537) format, including the leading '+'. */ from?: string; /** */ @@ -24,7 +24,7 @@ interface FaxRequestBase { /** The URL to which a callback will be sent when the fax is completed. The callback will be sent as a POST request with a JSON body. The callback will be sent to the URL specified in the `callbackUrl` parameter, if provided, otherwise it will be sent to the URL specified in the `callbackUrl` field of the Fax Service object. */ callbackUrl?: string; /** The content type of the callback. */ - callbackContentType?: WebhookContentType; + callbackUrlContentType?: WebhookContentType; /** Determines how documents are converted to black and white. Defaults to value selected on Fax Service object. */ imageConversionMethod?: ImageConversionMethod; /** ID of the fax service used. */ @@ -36,9 +36,31 @@ interface FaxRequestBase { export type FaxRequestJson = FaxRequestBase & { /** An array of base64 encoded files */ files: FaxBase64File[]; + filePaths?: never; +} + +export type SingleFaxRequestJson = FaxRequestJson & { + /** A phone number in [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537) format, including the leading '+'. */ + to: string; +} + +export type MultipleFaxRequestJson = FaxRequestJson & { + /** A list of phone number in [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537) format, including the leading '+'. */ + to: string[]; } export interface FaxRequestFormData extends FaxRequestBase { /** The file(s) you want to send as a fax as body attachment. */ - file?: any; + filePaths?: string | string[]; + files?: never; +} + +export type SingleFaxRequestFormData = FaxRequestFormData & { + /** A phone number in [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537) format, including the leading '+'. */ + to: string; +} + +export type MultipleFaxRequestFormData = FaxRequestFormData & { + /** A list of phone number in [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537) format, including the leading '+'. */ + to: string[]; } diff --git a/packages/fax/src/models/v3/fax-request/index.ts b/packages/fax/src/models/v3/fax-request/index.ts index ef1da073..15e01ebb 100644 --- a/packages/fax/src/models/v3/fax-request/index.ts +++ b/packages/fax/src/models/v3/fax-request/index.ts @@ -1 +1,11 @@ -export type { FaxRequest, FaxRequestJson, FaxRequestFormData } from './fax-request'; +export type { + SingleFaxRequest, + SingleFaxRequestJson, + SingleFaxRequestFormData, + FaxRequestBase, + FaxRequestJson, + FaxRequestFormData, + MultipleFaxRequest, + MultipleFaxRequestJson, + MultipleFaxRequestFormData, +} from './fax-request'; diff --git a/packages/fax/src/models/v3/fax/fax.ts b/packages/fax/src/models/v3/fax/fax.ts index 6ccef1fb..42ad1de1 100644 --- a/packages/fax/src/models/v3/fax/fax.ts +++ b/packages/fax/src/models/v3/fax/fax.ts @@ -40,15 +40,15 @@ export interface Fax { /** The URL to which a callback will be sent when the fax is completed. The callback will be sent as a POST request with a JSON body. The callback will be sent to the URL specified in the `callbackUrl` parameter, if provided, otherwise it will be sent to the URL specified in the `callbackUrl` field of the Fax Service object. */ callbackUrl?: string; /** The content type of the callback. */ - callbackContentType?: WebhookContentType; + callbackUrlContentType?: WebhookContentType; /** Determines how documents are converted to black and white. Defaults to value selected on Fax Service object. */ imageConversionMethod?: ImageConversionMethod; /** @see ErrorType */ errorType?: ErrorType; /** One of the error numbers listed in the [Fax Error Messages section](#FaxErrors). */ - errorId?: number; - /** One of the error codes listed in the [Fax Error Messages section](#FaxErrors). */ - errorCode?: string; + errorCode?: number; + /** One of the error messages listed in the [Fax Error Messages section](#FaxErrors). */ + errorMessage?: string; /** The `Id` of the project associated with the call. */ projectId?: string; /** ID of the fax service used. */ diff --git a/packages/fax/src/models/v3/faxes-list/faxes-list.ts b/packages/fax/src/models/v3/faxes-list/faxes-list.ts new file mode 100644 index 00000000..cb25d1e2 --- /dev/null +++ b/packages/fax/src/models/v3/faxes-list/faxes-list.ts @@ -0,0 +1,5 @@ +import { Fax } from '../fax'; + +export interface FaxesList { + faxes: Fax[]; +} diff --git a/packages/fax/src/models/v3/faxes-list/index.ts b/packages/fax/src/models/v3/faxes-list/index.ts new file mode 100644 index 00000000..4a1c6156 --- /dev/null +++ b/packages/fax/src/models/v3/faxes-list/index.ts @@ -0,0 +1 @@ +export type { FaxesList } from './faxes-list'; diff --git a/packages/fax/src/models/v3/helper.ts b/packages/fax/src/models/v3/helper.ts new file mode 100644 index 00000000..dbe256c0 --- /dev/null +++ b/packages/fax/src/models/v3/helper.ts @@ -0,0 +1,8 @@ +import { FaxBase64FileType, validBase64FileTypes } from './enums'; + +export const convertToSupportedFileType = (fileType: string | undefined): FaxBase64FileType | undefined => { + if (!fileType || !validBase64FileTypes.includes(fileType.toUpperCase() as FaxBase64FileType)) { + return undefined; + } + return fileType.toUpperCase() as FaxBase64FileType; +}; diff --git a/packages/fax/src/models/v3/index.ts b/packages/fax/src/models/v3/index.ts index a64bcd07..1e66afd0 100644 --- a/packages/fax/src/models/v3/index.ts +++ b/packages/fax/src/models/v3/index.ts @@ -1,32 +1,18 @@ -// export * from './bad-request-detail'; export * from './bar-code'; -// export * from './call-error'; -// export * from './document-conversion-error'; +export * from './date-range-filter'; export * from './email'; -// export * from './error'; -// export * from './error-detail'; -// export * from './error-info'; -// export * from './error-type'; export * from './fax'; export * from './fax-base64-file'; export * from './fax-content-url'; -// export * from './fax-error'; -// export * from './fax-errors'; -// export * from './fax-status'; -// export * from './field-violation'; -// export * from './generic-event'; export * from './fax-money'; export * from './fax-request'; +export * from './faxes-list'; export * from './mod-events'; -// export * from './pagination'; -// export * from './quota-failure'; -// export * from './quota-failure-detail'; -// export * from './request-info-detail'; -// export * from './send-fax-request1'; export * from './service'; export * from './service-email-settings'; export * from './service-phone-number'; export * from './update-email-request'; -export * from './webhook-event-parsed/webhook-event-parsed'; +export * from './webhook-event-parsed'; export * from './enums'; export * from './requests'; +export * from './helper'; diff --git a/packages/fax/src/models/v3/mod-events/fax-completed-event/fax-completed-event.ts b/packages/fax/src/models/v3/mod-events/fax-completed-event/fax-completed-event.ts index 3952ebc4..17b3110a 100644 --- a/packages/fax/src/models/v3/mod-events/fax-completed-event/fax-completed-event.ts +++ b/packages/fax/src/models/v3/mod-events/fax-completed-event/fax-completed-event.ts @@ -1,13 +1,15 @@ -import { FaxBase64File } from '../../fax-base64-file'; import { FaxEventFormData, FaxEventJson } from '../base-fax-event'; +import { FaxBase64FileType } from '../../enums'; export type FaxCompletedEvent = FaxCompletedEventJson | FaxCompletedEventFormData; export interface FaxCompletedEventJson extends FaxEventJson { /** Always FAX_COMPLETED for this event. */ event?: 'FAX_COMPLETED'; - /** */ - files?: FaxBase64File[]; + /** The base64 encoded file. */ + file?: string; + /** The file type of the attached file. */ + fileType?: FaxBase64FileType; } export interface FaxCompletedEventFormData extends FaxEventFormData { diff --git a/packages/fax/src/models/v3/requests/faxes/faxes-request-data.ts b/packages/fax/src/models/v3/requests/faxes/faxes-request-data.ts index 5985722b..f8e37eb1 100644 --- a/packages/fax/src/models/v3/requests/faxes/faxes-request-data.ts +++ b/packages/fax/src/models/v3/requests/faxes/faxes-request-data.ts @@ -1,5 +1,6 @@ import { FaxDirection, FaxStatus } from '../../enums'; -import { FaxRequest } from '../../fax-request'; +import { SingleFaxRequest, MultipleFaxRequest } from '../../fax-request'; +import { DateRangeFilter } from '../../date-range-filter'; export interface DeleteFaxContentRequestData { /** The ID of the fax. */ @@ -16,8 +17,10 @@ export interface GetFaxRequestData { 'id': string; } export interface ListFaxesRequestData { - /** Filter calls based on `createTime`. If you make the query more precise, fewer results will be returned. For example, `2021-02-01` will return all calls from the first of February 2021, and `2021-02-01T14:00:00Z` will return all calls after 14:00 on the first of February. This field also supports `<=` and `>=` to search for calls in a range `?createTime>=2021-10-01&startTime<=2021-10-30` to get a list of calls for all of October 2021. It is also possible to submit partial dates. For example, `createTime=2021-02` will return all calls for February 2021. If not value is submitted, the default value is the prior week. */ - 'createTime'?: string; + /** Filter calls based on `createTime`. It can be a year, a month or a day. */ + 'createTime'?: string | Date; + /** Filter calls based on `createTime`. It will filter the faxes on a range of dates. */ + 'createTimeRange'?: DateRangeFilter; /** Limits results to faxes with the specified direction. */ 'direction'?: FaxDirection; /** Limits results to faxes with the specified status. */ @@ -31,6 +34,13 @@ export interface ListFaxesRequestData { /** The page you want to retrieve returned from a previous List request, if any */ 'page'?: string; } -export interface SendFaxRequestData { - 'sendFaxRequestBody': FaxRequest; + +export type SendFaxRequestData = SendSingleFaxRequestData | SendMultipleFaxRequestData; + +export interface SendSingleFaxRequestData { + 'sendFaxRequestBody': SingleFaxRequest; +} + +export interface SendMultipleFaxRequestData { + 'sendFaxRequestBody': MultipleFaxRequest; } diff --git a/packages/fax/src/models/v3/webhook-event-parsed/index.ts b/packages/fax/src/models/v3/webhook-event-parsed/index.ts new file mode 100644 index 00000000..9bb76639 --- /dev/null +++ b/packages/fax/src/models/v3/webhook-event-parsed/index.ts @@ -0,0 +1 @@ +export type { FaxWebhookEventParsed } from './webhook-event-parsed'; diff --git a/packages/fax/src/rest/v3/emails/emails-api.jest.fixture.ts b/packages/fax/src/rest/v3/emails/emails-api.jest.fixture.ts index 33c9f31b..75f343ef 100644 --- a/packages/fax/src/rest/v3/emails/emails-api.jest.fixture.ts +++ b/packages/fax/src/rest/v3/emails/emails-api.jest.fixture.ts @@ -2,6 +2,7 @@ import { AddEmailToNumbersRequestData, DeleteEmailRequestData, Email, + ListEmailsForNumberRequestData, ListEmailsForProjectRequestData, ListNumbersByEmailRequestData, ServicePhoneNumber, @@ -17,19 +18,23 @@ export class EmailsApiFixture implements Partial> { */ public addToNumbers: jest.Mock, [AddEmailToNumbersRequestData]> = jest.fn(); /** - * Fixture associated to function deleteEmail + * Fixture associated to function delete */ public delete: jest.Mock, [DeleteEmailRequestData]> = jest.fn(); /** - * Fixture associated to function getEmailsForProject + * Fixture associated to function list */ public list: jest.Mock, [ListEmailsForProjectRequestData]> = jest.fn(); /** - * Fixture associated to function getNumbersByEmail + * Fixture associated to function listForNumber + */ + public listForNumber: jest.Mock, [ListEmailsForNumberRequestData]> = jest.fn(); + /** + * Fixture associated to function listNumbers */ public listNumbers: jest.Mock, [ListNumbersByEmailRequestData]> = jest.fn(); /** - * Fixture associated to function updateEmail + * Fixture associated to function update */ public update: jest.Mock, [UpdateEmailRequestData]> = jest.fn(); } diff --git a/packages/fax/src/rest/v3/emails/emails-api.ts b/packages/fax/src/rest/v3/emails/emails-api.ts index c15e5c74..a873d393 100644 --- a/packages/fax/src/rest/v3/emails/emails-api.ts +++ b/packages/fax/src/rest/v3/emails/emails-api.ts @@ -2,6 +2,7 @@ import { AddEmailToNumbersRequestData, DeleteEmailRequestData, Email, + ListEmailsForNumberRequestData, ListEmailsForProjectRequestData, ListNumbersByEmailRequestData, ServicePhoneNumber, @@ -17,9 +18,12 @@ import { SinchClientParameters, } from '@sinch/sdk-client'; import { FaxDomainApi } from '../fax-domain-api'; +import { ServicesApi } from '../services'; export class EmailsApi extends FaxDomainApi { + private servicesApi: ServicesApi; + /** * Initialize your interface * @@ -27,6 +31,7 @@ export class EmailsApi extends FaxDomainApi { */ constructor(sinchClientParameters: SinchClientParameters) { super(sinchClientParameters, 'EmailsApi'); + this.servicesApi = new ServicesApi(sinchClientParameters); } /** @@ -126,6 +131,16 @@ export class EmailsApi extends FaxDomainApi { return listPromise as ApiListPromise; } + /** + * List emails for a number + * List any emails for a number. + * @param { ListEmailsForNumberRequestData } data - The data to provide to the API call. + * @return {ApiListPromise} - The list of emails for a given number + */ + public listForNumber(data: ListEmailsForNumberRequestData): ApiListPromise { + return this.servicesApi.listEmailsForNumber(data); + } + /** * Get numbers for email * Get configured numbers for an email @@ -135,7 +150,6 @@ export class EmailsApi extends FaxDomainApi { public listNumbers(data: ListNumbersByEmailRequestData): ApiListPromise { this.client = this.getSinchClient(); const getParams = this.client.extractQueryParams(data, [ - 'email', 'pageSize', 'page']); const headers: { [key: string]: string | undefined } = { diff --git a/packages/fax/src/rest/v3/enums.ts b/packages/fax/src/rest/v3/enums.ts index 28ebfa1d..cb0ff5c3 100644 --- a/packages/fax/src/rest/v3/enums.ts +++ b/packages/fax/src/rest/v3/enums.ts @@ -1,2 +1 @@ -// export type { GetFaxFileByIdFileFormatEnum, SendFaxCallbackContentTypeEnum, SendFaxImageConversionMethodEnum } from './faxes'; export {}; diff --git a/packages/fax/src/rest/v3/faxes/faxes-api.jest.fixture.ts b/packages/fax/src/rest/v3/faxes/faxes-api.jest.fixture.ts index ff2ac536..3eba119a 100644 --- a/packages/fax/src/rest/v3/faxes/faxes-api.jest.fixture.ts +++ b/packages/fax/src/rest/v3/faxes/faxes-api.jest.fixture.ts @@ -12,24 +12,24 @@ import { ApiListPromise, FileBuffer } from '@sinch/sdk-client'; export class FaxesApiFixture implements Partial> { /** - * Fixture associated to function deleteFaxContentById + * Fixture associated to function deleteContent */ public deleteContent: jest.Mock, [DeleteFaxContentRequestData]> = jest.fn(); /** - * Fixture associated to function getFaxFileById + * Fixture associated to function downloadContent */ public downloadContent: jest.Mock, [DownloadFaxContentRequestData]> = jest.fn(); /** - * Fixture associated to function getFaxInfoPerId + * Fixture associated to function get */ public get: jest.Mock, [GetFaxRequestData]> = jest.fn(); /** - * Fixture associated to function getFaxes + * Fixture associated to function list */ public list: jest.Mock, [ListFaxesRequestData]> = jest.fn(); /** - * Fixture associated to function sendFax + * Fixture associated to function send */ - public send: jest.Mock, [SendFaxRequestData]> = jest.fn(); + public send: jest.Mock, [SendFaxRequestData]> = jest.fn(); } diff --git a/packages/fax/src/rest/v3/faxes/faxes-api.ts b/packages/fax/src/rest/v3/faxes/faxes-api.ts index 072c0ac1..9704fdc8 100644 --- a/packages/fax/src/rest/v3/faxes/faxes-api.ts +++ b/packages/fax/src/rest/v3/faxes/faxes-api.ts @@ -3,6 +3,8 @@ import { buildPageResultPromise, createIteratorMethodsForPagination, FileBuffer, + formatCreateTimeFilter, + formatCreateTimeRangeFilter, PaginatedApiProperties, PaginationEnum, RequestBody, @@ -12,12 +14,19 @@ import { FaxDomainApi } from '../fax-domain-api'; import { Fax, FaxRequestJson, - FaxRequestFormData, DeleteFaxContentRequestData, DownloadFaxContentRequestData, GetFaxRequestData, - ListFaxesRequestData, SendFaxRequestData, + ListFaxesRequestData, + SendSingleFaxRequestData, + SingleFaxRequestFormData, + FaxesList, + SendMultipleFaxRequestData, + MultipleFaxRequestFormData, + SendFaxRequestData, } from '../../../models'; +import FormData = require('form-data'); +import * as fs from 'fs'; export class FaxesApi extends FaxDomainApi { @@ -122,13 +131,16 @@ export class FaxesApi extends FaxDomainApi { public list(data: ListFaxesRequestData): ApiListPromise { this.client = this.getSinchClient(); const getParams = this.client.extractQueryParams(data, [ - 'createTime', 'direction', 'status', 'to', 'from', 'pageSize', 'page']); + (getParams as any).createTime = JSON.stringify(formatCreateTimeFilter(data.createTime)); + (getParams as any)['createTime>'] = JSON.stringify(formatCreateTimeRangeFilter(data.createTimeRange?.from)); + (getParams as any)['createTime<'] = JSON.stringify(formatCreateTimeRangeFilter(data.createTimeRange?.to)); + const headers: { [key: string]: string | undefined } = { 'Content-Type': 'application/json', 'Accept': 'application/json', @@ -163,13 +175,13 @@ export class FaxesApi extends FaxDomainApi { } /** - * Send a fax - * Create and send a fax. Fax content may be supplied via one or more files or URLs of supported filetypes. + * Send a fax or multiple faxes + * Create and send one or multiple faxes. Fax content may be supplied via one or more files or URLs of supported filetypes. * This endpoint supports the following content types for the fax payload: * - Multipart/form-data * - Application/json - * We will however always return a fax object in the response in application json. - * If you supply a callbackUrl the callback will be sent as multipart/form-data with the content of the fax as an attachment to the body, *unless* you specify callbackContentType as application/json. + * We will however always return a fax array in the response in application json. + * If you supply a callbackUrl the callback will be sent as multipart/form-data with the content of the fax as an attachment to the body, *unless* you specify callbackUrlContentType as application/json. * #### file(s) * Files may be included in the POST request as multipart/form-data parts. * #### contentUrl @@ -177,7 +189,7 @@ export class FaxesApi extends FaxDomainApi { * Please note: If you are passing fax a secure URL (starting with 'https://'), make sure that your SSL certificate (including your intermediate cert, if you have one) is installed properly, valid, and up-to-date. * @param { SendFaxRequestData } data - The data to provide to the API call. */ - public async send(data: SendFaxRequestData): Promise { + public async send(data: SendFaxRequestData): Promise { this.client = this.getSinchClient(); const requestBody = data.sendFaxRequestBody; requestBody['headerText'] = requestBody['headerText'] !== undefined @@ -188,11 +200,12 @@ export class FaxesApi extends FaxDomainApi { ? requestBody['headerTimeZone'] : 'America/New_York'; requestBody['retryDelaySeconds'] = requestBody['retryDelaySeconds'] !== undefined ? requestBody['retryDelaySeconds'] : 60; - requestBody['callbackContentType'] = requestBody['callbackContentType'] !== undefined - ? requestBody['callbackContentType'] : 'multipart/form-data'; + requestBody['callbackUrlContentType'] = requestBody['callbackUrlContentType'] !== undefined + ? requestBody['callbackUrlContentType'] : 'multipart/form-data'; requestBody['imageConversionMethod'] = requestBody['imageConversionMethod'] !== undefined ? requestBody['imageConversionMethod'] : 'HALFTONE'; - const getParams = this.client.extractQueryParams(data, [] as never[]); + const getParams + = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { 'Accept': 'application/json', }; @@ -205,23 +218,61 @@ export class FaxesApi extends FaxDomainApi { headers['Content-Type'] = 'application/json'; body = JSON.stringify(data['sendFaxRequestBody']); } else { - const formParams: any = {}; - const requestData = data.sendFaxRequestBody as FaxRequestFormData; - if( requestData.to ) { formParams.to = requestData.to; } - if( requestData.file ) { formParams.file = requestData.file; } - if( requestData.from ) { formParams.from = requestData.from; } - if( requestData.contentUrl ) { formParams.contentUrl = requestData.contentUrl; } - if( requestData.headerText ) { formParams.headerText = requestData.headerText; } - if( requestData.headerPageNumbers ) { formParams.headerPageNumbers = requestData.headerPageNumbers.toString(); } - if( requestData.headerTimeZone ) { formParams.headerTimeZone = requestData.headerTimeZone; } - if( requestData.retryDelaySeconds ) { formParams.retryDelaySeconds = requestData.retryDelaySeconds; } - if( requestData.labels ) { formParams.labels = requestData.labels; } - if( requestData.callbackUrl ) { formParams.callbackUrl = requestData.callbackUrl; } - if( requestData.callbackContentType ) { formParams.callbackContentType = requestData.callbackContentType; } - if( requestData.imageConversionMethod ) {formParams.imageConversionMethod = requestData.imageConversionMethod;} - if( requestData.serviceId ) { formParams.serviceId = requestData.serviceId; } - if( requestData.maxRetries ) { formParams.maxRetries = requestData.maxRetries; } - body = this.client.processFormData(formParams, 'multipart/form-data'); + const formData = new FormData(); + let requestData; + if (Array.isArray(data.sendFaxRequestBody.to)) { + requestData = data.sendFaxRequestBody as MultipleFaxRequestFormData; + requestData.to.forEach((to) => formData.append('to', to)); + } else { + requestData = data.sendFaxRequestBody as SingleFaxRequestFormData; + formData.append('to', requestData.to); + } + if (requestData.filePaths) { + let attachmentPaths = requestData.filePaths; + if (typeof attachmentPaths === 'string') { + attachmentPaths = [String(requestData.filePaths)]; + } + attachmentPaths.forEach((filePath) => { + formData.append('file', fs.readFileSync(filePath), filePath.split('/').pop()); + }); + } + if (requestData.from) { + formData.append('from', requestData.from); + } + if (requestData.contentUrl) { + formData.append('contentUrl', requestData.contentUrl); + } + if (requestData.headerText) { + formData.append('headerText', requestData.headerText); + } + if (requestData.headerPageNumbers) { + formData.append('headerPageNumbers', requestData.headerPageNumbers.toString()); + } + if (requestData.headerTimeZone) { + formData.append('headerTimeZone', requestData.headerTimeZone); + } + if (requestData.retryDelaySeconds) { + formData.append('retryDelaySeconds', requestData.retryDelaySeconds); + } + if (requestData.labels) { + formData.append('labels', requestData.labels); + } + if (requestData.callbackUrl) { + formData.append('callbackUrl', requestData.callbackUrl); + } + if (requestData.callbackUrlContentType) { + formData.append('callbackUrlContentType', requestData.callbackUrlContentType); + } + if (requestData.imageConversionMethod) { + formData.append('imageConversionMethod', requestData.imageConversionMethod); + } + if (requestData.serviceId) { + formData.append('serviceId', requestData.serviceId); + } + if (requestData.maxRetries) { + formData.append('maxRetries', requestData.maxRetries); + } + body = formData; } const basePathUrl = `${this.client.apiClientOptions.hostname}/v3/projects/${this.client.apiClientOptions.projectId}/faxes`; @@ -229,12 +280,29 @@ export class FaxesApi extends FaxDomainApi { const requestOptions = await this.client.prepareOptions(basePathUrl, 'POST', getParams, headers, body || undefined); const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); - return this.client.processCall({ - url, - requestOptions, - apiName: this.apiName, - operationId: 'SendFax', - }); + if (Array.isArray(data.sendFaxRequestBody.to)) { + const responseData = await this.client.processCall({ + url, + requestOptions, + apiName: this.apiName, + operationId: 'SendMultipleFax', + }); + // When there is a single element in the 'to' array, the server will return a Fax, not a FaxList + if (Array.isArray((responseData as FaxesList).faxes)) { + return (responseData as FaxesList).faxes; + } else { + return [responseData as Fax]; + } + } else { + const responseData = await this.client.processCall({ + url, + requestOptions, + apiName: this.apiName, + operationId: 'SendFax', + }); + return [responseData]; + } + } } diff --git a/packages/fax/tests/e2e/resources/sinch-logo.png b/packages/fax/tests/e2e/resources/sinch-logo.png new file mode 100644 index 00000000..ee498982 Binary files /dev/null and b/packages/fax/tests/e2e/resources/sinch-logo.png differ diff --git a/packages/fax/tests/models/v3/helper.test.ts b/packages/fax/tests/models/v3/helper.test.ts new file mode 100644 index 00000000..adbc2911 --- /dev/null +++ b/packages/fax/tests/models/v3/helper.test.ts @@ -0,0 +1,42 @@ +import { convertToSupportedFileType } from '../../../src/models'; + +describe('Fax models helpers', () => { + + describe('convertToSupportedFileType', () => { + it('should convert a file extension to a FaxBase64FileType', () => { + let convertedFileExtension = convertToSupportedFileType('doc'); + expect(convertedFileExtension).toBe('DOC'); + + convertedFileExtension = convertToSupportedFileType('docx'); + expect(convertedFileExtension).toBe('DOCX'); + + convertedFileExtension = convertToSupportedFileType('pdf'); + expect(convertedFileExtension).toBe('PDF'); + + convertedFileExtension = convertToSupportedFileType('tif'); + expect(convertedFileExtension).toBe('TIF'); + + convertedFileExtension = convertToSupportedFileType('jpg'); + expect(convertedFileExtension).toBe('JPG'); + + convertedFileExtension = convertToSupportedFileType('odt'); + expect(convertedFileExtension).toBe('ODT'); + + convertedFileExtension = convertToSupportedFileType('txt'); + expect(convertedFileExtension).toBe('TXT'); + + convertedFileExtension = convertToSupportedFileType('html'); + expect(convertedFileExtension).toBe('HTML'); + + convertedFileExtension = convertToSupportedFileType('png'); + expect(convertedFileExtension).toBe('PNG'); + + convertedFileExtension = convertToSupportedFileType(undefined); + expect(convertedFileExtension).toBeUndefined(); + + convertedFileExtension = convertToSupportedFileType('unknown'); + expect(convertedFileExtension).toBeUndefined(); + }); + }); + +}); diff --git a/packages/fax/tests/rest/v3/emails/emails-api.test.ts b/packages/fax/tests/rest/v3/emails/emails-api.test.ts index 551efd8d..f574c57a 100644 --- a/packages/fax/tests/rest/v3/emails/emails-api.test.ts +++ b/packages/fax/tests/rest/v3/emails/emails-api.test.ts @@ -100,6 +100,35 @@ describe('EmailsApi', () => { }); }); + describe('getEmailsForNumber', () => { + it('should make a GET request to list the emails for a number', async () => { + // Given + const requestData: Fax.ListEmailsForNumberRequestData = { + serviceId: 'serviceId', + phoneNumber: '+15551235656', + }; + const mockData: string[] = [ + 'user@example.com', + ]; + const expectedResponse = { + data: mockData, + hasNextPage: false, + nextPageValue: '', + nextPage: jest.fn(), + }; + + // When + fixture.listForNumber.mockResolvedValue(expectedResponse); + emailsApi.listForNumber = fixture.listForNumber; + const response = await emailsApi.listForNumber(requestData); + + // Then + expect(response).toEqual(expectedResponse); + expect(response.data).toBeDefined(); + expect(fixture.listForNumber).toHaveBeenCalledWith(requestData); + }); + }); + describe ('getNumbersByEmail', () => { it('should make a GET request to list the configured numbers for an email', async () => { // Given diff --git a/packages/fax/tests/rest/v3/emails/emails.steps.ts b/packages/fax/tests/rest/v3/emails/emails.steps.ts new file mode 100644 index 00000000..ff39fe47 --- /dev/null +++ b/packages/fax/tests/rest/v3/emails/emails.steps.ts @@ -0,0 +1,141 @@ +import { EmailsApi, Fax, FaxService } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let emailsApi: EmailsApi; +let listEmailsResponse: PageResult; +const emailsList: string[] = []; +let listFaxEmails: PageResult; +const faxEmailsList: Fax.Email[] = []; +let email: Fax.Email; +let deleteEmailResponse: void; +let listNumbersResponse: PageResult; +const numbersList: Fax.ServicePhoneNumber[] = []; + +Given('the Fax service "Emails" is available', () => { + const faxService = new FaxService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + faxHostname: 'http://localhost:3012', + }); + emailsApi = faxService.emails; +}); + +When('I send a request to list the emails associated to a phone number via the "Emails" Service', async () => { + listEmailsResponse = await emailsApi.listForNumber({ + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + phoneNumber: '+12014444444', + }); +}); + +// eslint-disable-next-line max-len +Then('the "Emails" Service response contains {string} emails associated to the phone number', (expectedAnswer: string) => { + // To implement + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.equal(listEmailsResponse.data.length, expectedNumbers); +}); + +When('I send a request to list all the emails associated to a phone number via the "Emails" Service', async () => { + for await (const email of emailsApi.listForNumber({ + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + phoneNumber: '+12014444444' }) + ) { + emailsList.push(email); + } +}); + +// eslint-disable-next-line max-len +Then('the emails list from the "Emails" Service contains {string} emails associated to a phone number', (expectedAnswer: string) => { + // To implement + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.equal(emailsList.length, expectedNumbers); +}); + +When('I send a request to list the emails associated to the project', async () => { + listFaxEmails = await emailsApi.list({}); +}); + +Then('the response contains {string} emails associated to the project', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.equal(listFaxEmails.data.length, expectedNumbers); +}); + +When('I send a request to list all the emails associated to the project', async () => { + for await (const email of emailsApi.list({})) { + faxEmailsList.push(email); + } +}); + +Then('the emails list contains {string} emails associated to the project', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.equal(faxEmailsList.length, expectedNumbers); +}); + +When('I send a request to add a new email to the project', async () => { + email = await emailsApi.addToNumbers({ + emailRequestBody : { + email: 'spaceship@galaxy.far.far.away', + phoneNumbers: ['+12016666666'], + }, + }); +}); + +Then('the response contains the added email', () => { + assert.equal(email.email, 'spaceship@galaxy.far.far.away'); + assert.deepEqual(email.phoneNumbers, ['+12016666666']); + assert.equal(email.projectId, '123c0ffee-dada-beef-cafe-baadc0de5678'); +}); + +When('I send a request to update the phone numbers associated to an email', async () => { + email = await emailsApi.update({ + email: 'spaceship@galaxy.far.far.away', + updateEmailRequestBody: { + phoneNumbers: [ + '+12016666666', + '+12017777777', + ], + }, + }); +}); + +Then('the response contains the updated email', () => { + assert.equal(email.email, 'spaceship@galaxy.far.far.away'); + assert.deepEqual(email.phoneNumbers, ['+12016666666', '+12017777777']); + assert.equal(email.projectId, '123c0ffee-dada-beef-cafe-baadc0de5678'); +}); + +When('I send a request to delete an email from the project', async () => { + deleteEmailResponse = await emailsApi.delete({ + email: 'spaceship@galaxy.far.far.away', + }); +}); + +Then('the delete email response contains no data', () => { + assert.deepEqual(deleteEmailResponse, {}); +}); + +When('I send a request to list the phone numbers associated to an email', async () => { + listNumbersResponse = await emailsApi.listNumbers({ + email: 'cookie.monster@nom.nom', + pageSize: 2, + }); +}); + +Then('the response contains {string} phone numbers associated to the email', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.strictEqual(listNumbersResponse.data.length, expectedNumbers); +}); + +When('I send a request to list all the phone numbers associated to an email', async () => { + for await (const number of emailsApi.listNumbers({ email: 'cookie.monster@nom.nom' })) { + numbersList.push(number); + } +}); + +Then('the phone numbers list contains {string} phone numbers associated to the email', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.strictEqual(numbersList.length, expectedNumbers); +}); diff --git a/packages/fax/tests/rest/v3/faxes/faxes-api.test.ts b/packages/fax/tests/rest/v3/faxes/faxes-api.test.ts index 8c3cb79d..8bc2944f 100644 --- a/packages/fax/tests/rest/v3/faxes/faxes-api.test.ts +++ b/packages/fax/tests/rest/v3/faxes/faxes-api.test.ts @@ -75,10 +75,10 @@ describe('FaxesApi', () => { status: 'FAILURE', headerTimeZone: 'America/New_York', retryDelaySeconds: 60, - callbackContentType: 'multipart/form-data', + callbackUrlContentType: 'multipart/form-data', errorType: 'LINE_ERROR', - errorId: 89, - errorCode: 'The call dropped prematurely', + errorCode: 89, + errorMessage: 'The call dropped prematurely', projectId: 'projectId', serviceId: 'serviceId', maxRetries: 3, @@ -117,10 +117,10 @@ describe('FaxesApi', () => { status: 'FAILURE', headerTimeZone: 'America/New_York', retryDelaySeconds: 60, - callbackContentType: 'multipart/form-data', + callbackUrlContentType: 'multipart/form-data', errorType: 'LINE_ERROR', - errorId: 89, - errorCode: 'The call dropped prematurely', + errorCode: 89, + errorMessage: 'The call dropped prematurely', projectId: 'projectId', serviceId: 'serviceId', maxRetries: 3, @@ -156,29 +156,87 @@ describe('FaxesApi', () => { describe ('sendFax', () => { it('should make a POST request to create and send a fax', async () => { // Given - const requestData: Fax.SendFaxRequestData = { + const requestData: Fax.SendSingleFaxRequestData = { sendFaxRequestBody: { to: '+12015555555', }, }; - const expectedResponse: Fax.Fax = { - id: 'fax_id', - direction: 'OUTBOUND', - to: '+12015555555', - status: 'IN_PROGRESS', - headerTimeZone: 'America/New_York', - retryDelaySeconds: 60, - callbackContentType: 'multipart/form-data', - projectId: 'projectId', - serviceId: 'serviceId', - maxRetries: 3, - createTime: new Date('2024-02-27T12:28:09Z'), - headerPageNumbers: true, - contentUrl: [ - 'https://developers.sinch.com/fax/fax.pdf', - ], - imageConversionMethod: 'HALFTONE', + const expectedResponse: Fax.Fax[] = [ + { + id: 'fax_id', + direction: 'OUTBOUND', + to: '+12015555555', + status: 'IN_PROGRESS', + headerTimeZone: 'America/New_York', + retryDelaySeconds: 60, + callbackUrlContentType: 'multipart/form-data', + projectId: 'projectId', + serviceId: 'serviceId', + maxRetries: 3, + createTime: new Date('2024-02-27T12:28:09Z'), + headerPageNumbers: true, + contentUrl: [ + 'https://developers.sinch.com/fax/fax.pdf', + ], + imageConversionMethod: 'HALFTONE', + }, + ]; + + // When + fixture.send.mockResolvedValue(expectedResponse); + faxesApi.send = fixture.send; + const response = await faxesApi.send(requestData); + + // Then + expect(response).toEqual(expectedResponse); + expect(fixture.send).toHaveBeenCalledWith(requestData); + }); + + it('should make a POST request to create and send multiple faxes', async () => { + // Given + const requestData: Fax.SendMultipleFaxRequestData = { + sendFaxRequestBody: { + to: ['+12015555555', '+12015555566'], + }, }; + const expectedResponse: Fax.Fax[] = [ + { + id: 'fax_id', + direction: 'OUTBOUND', + to: '+12015555555', + status: 'IN_PROGRESS', + headerTimeZone: 'America/New_York', + retryDelaySeconds: 60, + callbackUrlContentType: 'multipart/form-data', + projectId: 'projectId', + serviceId: 'serviceId', + maxRetries: 3, + createTime: new Date('2024-02-27T12:28:09Z'), + headerPageNumbers: true, + contentUrl: [ + 'https://developers.sinch.com/fax/fax.pdf', + ], + imageConversionMethod: 'HALFTONE', + }, + { + id: 'fax_id', + direction: 'OUTBOUND', + to: '+12015555566', + status: 'IN_PROGRESS', + headerTimeZone: 'America/New_York', + retryDelaySeconds: 60, + callbackUrlContentType: 'multipart/form-data', + projectId: 'projectId', + serviceId: 'serviceId', + maxRetries: 3, + createTime: new Date('2024-02-27T12:28:09Z'), + headerPageNumbers: true, + contentUrl: [ + 'https://developers.sinch.com/fax/fax.pdf', + ], + imageConversionMethod: 'HALFTONE', + }, + ]; // When fixture.send.mockResolvedValue(expectedResponse); diff --git a/packages/fax/tests/rest/v3/faxes/faxes.steps.ts b/packages/fax/tests/rest/v3/faxes/faxes.steps.ts new file mode 100644 index 00000000..a2386fce --- /dev/null +++ b/packages/fax/tests/rest/v3/faxes/faxes.steps.ts @@ -0,0 +1,208 @@ +import { Given, When, Then } from '@cucumber/cucumber'; +import { FileBuffer, PageResult } from '@sinch/sdk-client'; +import * as assert from 'assert'; +import { FaxService, Fax, FaxesApi } from '../../../../src'; + +let faxesApi: FaxesApi; +let listResponse: PageResult; +const faxList: Fax.Fax[] = []; +let sendFaxResponse: Fax.Fax[]; +let fax: Fax.Fax; +let fileBuffer: FileBuffer; +let deleteContentResponse: void; + +Given('the Fax service "Faxes" is available', () => { + const faxService = new FaxService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + faxHostname: 'http://localhost:3012', + }); + faxesApi = faxService.faxes; +}); + +When('I send a fax with a contentUrl only to a single recipient', async () => { + sendFaxResponse = await faxesApi.send({ + sendFaxRequestBody: { + to: '+12015555555', + contentUrl: 'https://developers.sinch.com/fax/fax.pdf', + }, + }); +}); + +// eslint-disable-next-line max-len +Then('the response contains a list of fax objects with a single element received from a multipart-form-data request with contentUrl only', () => { + assert.equal(sendFaxResponse.length, 1); + assert.equal('01W4FFL35P4NC4K35URLSINGLE1', sendFaxResponse[0].id); +}); + +When('I send a fax with a contentUrl only to multiple recipients', async () => { + sendFaxResponse = await faxesApi.send({ + sendFaxRequestBody: { + to: ['+12015555555', '+12016666666'], + contentUrl: 'https://developers.sinch.com/fax/fax.pdf', + }, + }); +}); + +// eslint-disable-next-line max-len +Then('the response contains a list of fax objects with multiple elements received from a multipart-form-data request with contentUrl only', () => { + assert.equal(sendFaxResponse.length, 2); + assert.equal('01W4FFL35P4NC4K35URLMULTI01', sendFaxResponse[0].id); + assert.equal('01W4FFL35P4NC4K35URLMULTI02', sendFaxResponse[1].id); +}); + +When('I send a fax with a contentUrl and a binary file attachment to a single recipient', async () => { + sendFaxResponse = await faxesApi.send({ + sendFaxRequestBody: { + to: '+12015555555', + contentUrl: 'https://developers.sinch.com/fax/fax.pdf', + filePaths: ['./tests/e2e/resources/sinch-logo.png'], + }, + }); +}); + +// eslint-disable-next-line max-len +Then('the response contains a list of fax objects with a single element received from a multipart-form-data request', () => { + assert.equal(sendFaxResponse.length, 1); + assert.equal('01W4FFL35P4NC4K35BINSINGLE1', sendFaxResponse[0].id); +}); + +When('I send a fax with a contentUrl and a binary file attachment to multiple recipients', async () => { + sendFaxResponse = await faxesApi.send({ + sendFaxRequestBody: { + to: ['+12015555555', '+12016666666'], + contentUrl: 'https://developers.sinch.com/fax/fax.pdf', + filePaths: ['./tests/e2e/resources/sinch-logo.png'], + }, + }); +}); + +// eslint-disable-next-line max-len +Then('the response contains a list of fax objects with multiple elements received from a multipart-form-data request', () => { + assert.equal(sendFaxResponse.length, 2); + assert.equal('01W4FFL35P4NC4K35BINMULTI01', sendFaxResponse[0].id); + assert.equal('01W4FFL35P4NC4K35BINMULTI02', sendFaxResponse[1].id); +}); + +When('I send a fax with a contentUrl and a base64 file encoded to a single recipient', async () => { + sendFaxResponse = await faxesApi.send({ + sendFaxRequestBody: { + to: '+12015555555', + contentUrl: 'https://developers.sinch.com/fax/fax.pdf', + files: [ + { + file: 'WSdhIGRlcyBqb3VycywgZmF1dCBwYXMgbSdjaGVyY2hlciAhIEV0IHknYSBkZXMgam91cnMgdG91cyBsZXMgam91cnMgIQ==', + fileType: 'PDF', + }, + { + file: 'UXVhbmQgbGUgdHJvbGwgcGFybGUsIGwnaG9tbWUgYXZpc8OpIGwnw6ljb3V0ZQ==', + fileType: 'PDF', + }, + ], + }, + }); +}); + +// eslint-disable-next-line max-len +Then('the response contains a list of fax objects with a single element received from an application-json request', () => { + assert.equal(sendFaxResponse.length, 1); + assert.equal('01W4FFL35P4NC4K35B64SINGLE1', sendFaxResponse[0].id); +}); + +When('I send a fax with a contentUrl and a base64 file encoded to multiple recipients', async () => { + sendFaxResponse = await faxesApi.send({ + sendFaxRequestBody: { + to: ['+12015555555', '+12016666666'], + contentUrl: 'https://developers.sinch.com/fax/fax.pdf', + files: [ + { + file: 'WSdhIGRlcyBqb3VycywgZmF1dCBwYXMgbSdjaGVyY2hlciAhIEV0IHknYSBkZXMgam91cnMgdG91cyBsZXMgam91cnMgIQ==', + fileType: 'PDF', + }, + { + file: 'UXVhbmQgbGUgdHJvbGwgcGFybGUsIGwnaG9tbWUgYXZpc8OpIGwnw6ljb3V0ZQ==', + fileType: 'PDF', + }, + ], + }, + }); +}); + +// eslint-disable-next-line max-len +Then('the response contains a list of fax objects with multiple elements received from an application-json request', () => { + assert.equal(sendFaxResponse.length, 2); + assert.equal('01W4FFL35P4NC4K35B64MULTI01', sendFaxResponse[0].id); + assert.equal('01W4FFL35P4NC4K35B64MULTI02', sendFaxResponse[1].id); +}); + +When('I retrieve a fax', async () => { + fax = await faxesApi.get({ + id: '01W4FFL35P4NC4K35CR3P35M1N1', + }); +}); + +Then('the response contains a fax object', () => { + assert.equal('01W4FFL35P4NC4K35CR3P35M1N1', fax.id); + assert.equal('OUTBOUND', fax.direction); + assert.equal('+12014444444', fax.from); + assert.equal('+12015555555', fax.to); + assert.equal(1, fax.numberOfPages); + assert.equal('COMPLETED', fax.status); + assert.equal('America/New_York', fax.headerTimeZone); + assert.equal(60, fax.retryDelaySeconds); + assert.equal('multipart/form-data', fax.callbackUrlContentType); + assert.equal('0.07', (fax as any).pricePerPage); + assert.equal('123coffee-dada-beef-cafe-baadc0de5678', fax.projectId); + assert.equal('01K1TTENC4TSJ0LLYJ1GGLYJU1Y', fax.serviceId); + assert.equal('USD', fax.price?.currencyCode); + assert.equal('0.07', fax.price?.amount); + assert.equal(3, fax.maxRetries); + assert.equal(new Date('2024-06-06T14:42:42Z').getTime(), fax.createTime?.getTime()); + assert.equal(new Date('2024-06-06T14:43:17Z').getTime(), fax.completedTime?.getTime()); + assert.equal(true, fax.headerPageNumbers); + assert.equal('https://developers.sinch.com/fax/fax.pdf', fax.contentUrl?.[0]); + assert.equal('MONOCHROME', fax.imageConversionMethod); + assert.equal(true, fax.hasFile); +}); + +When('I send a request to list faxes', async () => { + listResponse = await faxesApi.list({}); +}); + +Then('the response contains {string} faxes', (expectedAnswer: string) => { + const expectedFaxes = parseInt(expectedAnswer, 10); + assert.strictEqual(listResponse.data.length, expectedFaxes); +}); + +When('I send a request to list all the faxes', async () => { + for await (const fax of faxesApi.list({})) { + faxList.push(fax); + } +}); + +Then('the faxes list contains {string} faxes', (expectedAnswer: string) => { + const expectedFaxes = parseInt(expectedAnswer, 10); + assert.strictEqual(faxList.length, expectedFaxes); +}); + +When('I send a request to download a fax content as PDF', async () => { + fileBuffer = await faxesApi.downloadContent({ + id: '01W4FFL35P4NC4K35CR3P35DWLD', + }); +}); + +Then('the response contains a PDF document', () => { + assert.equal(fileBuffer.fileName, '01W4FFL35P4NC4K35CR3P35DWLD.pdf'); +}); + +When('I send a request to delete a fax content on the server', async () => { + deleteContentResponse = await faxesApi.deleteContent({ + id: '01W4FFL35P4NC4K35CR3P35DEL0', + }); +}); + +Then('the response contains no data', () => { + assert.deepEqual(deleteContentResponse, {}); +}); diff --git a/packages/fax/tests/rest/v3/services/services.steps.ts b/packages/fax/tests/rest/v3/services/services.steps.ts new file mode 100644 index 00000000..29f78705 --- /dev/null +++ b/packages/fax/tests/rest/v3/services/services.steps.ts @@ -0,0 +1,176 @@ +import { FaxService, Fax, ServicesApi } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let servicesApi: ServicesApi; +let createServiceResponse: Fax.ServiceResponse; +let listResponse: PageResult; +const servicesList: Fax.ServiceResponse[] = []; +let service: Fax.ServiceResponse; +let deleteServiceResponse: void; +let listNumbersResponse: PageResult; +const numbersList: Fax.ServicePhoneNumber[] = []; +let listEmailsResponse: PageResult; +const emailsList: string[] = []; + +Given('the Fax service "Services" is available', () => { + const faxService = new FaxService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + faxHostname: 'http://localhost:3012', + }); + servicesApi = faxService.services; +}); + +When('I send a request to create a new service', async () => { + createServiceResponse = await servicesApi.create({ + createServiceRequestBody: { + name: 'Fax service for e2e tests', + incomingWebhookUrl: 'https://my-callback-server.com/fax', + webhookContentType: 'application/json', + defaultForProject: false, + defaultFrom: '+12014444444', + numberOfRetries: 2, + retryDelaySeconds: 30, + imageConversionMethod: 'MONOCHROME', + saveInboundFaxDocuments: true, + saveOutboundFaxDocuments: true, + }, + }); +}); + +Then('the service is created', () => { + assert.equal(createServiceResponse.id, '01W4FFL35P4NC4K35FAXSERVICE'); +}); + +When('I send a request to list the existing services', async () => { + listResponse = await servicesApi.list({ + pageSize: 2, + }); +}); + +Then('the response contains {string} services', (expectedAnswer: string) => { + const expectedServices = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedServices); +}); + +When('I send a request to list all the services', async () => { + for await (const service of servicesApi.list({ pageSize: 2 })) { + servicesList.push(service); + } +}); + +Then('the services list contains {string} services', (expectedAnswer: string) => { + const expectedServices = parseInt(expectedAnswer, 10); + assert.equal(servicesList.length, expectedServices); +}); + +When('I send a request to retrieve a service', async () => { + service = await servicesApi.get({ + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + }); +}); + +Then('the response contains a service object', () => { + assert.equal('01W4FFL35P4NC4K35FAXSERVICE', service.id); + assert.equal('application/json', service.webhookContentType); + assert.equal('+12014444444', service.defaultFrom); + assert.equal(2, service.numberOfRetries); + assert.equal(30, service.retryDelaySeconds); + assert.equal('MONOCHROME', service.imageConversionMethod); + assert.equal('123coffee-dada-beef-cafe-baadc0de5678', service.projectId); + assert.equal(false, service.defaultForProject); + assert.equal(true, service.saveInboundFaxDocuments); + assert.equal(true, service.saveOutboundFaxDocuments); + assert.equal('Fax service for e2e tests', service.name); + assert.equal('https://my-callback-server.com/fax', service.incomingWebhookUrl); +}); + +When('I send a request to update a service', async () => { + service = await servicesApi.update({ + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + updateServiceRequestBody: { + name: 'Updated Fax service name', + webhookContentType: 'multipart/form-data', + defaultForProject: true, + numberOfRetries: 3, + retryDelaySeconds: 60, + imageConversionMethod: 'HALFTONE', + saveOutboundFaxDocuments: false, + saveInboundFaxDocuments: false, + }, + }); +}); + +Then('the response contains a service with updated parameters', () => { + assert.equal('01W4FFL35P4NC4K35FAXSERVICE', service.id); + assert.equal('multipart/form-data', service.webhookContentType); + assert.equal(3, service.numberOfRetries); + assert.equal(60, service.retryDelaySeconds); + assert.equal('HALFTONE', service.imageConversionMethod); + assert.equal(true, service.defaultForProject); + assert.equal(false, service.saveInboundFaxDocuments); + assert.equal(false, service.saveOutboundFaxDocuments); + assert.equal('Updated Fax service name', service.name); +}); + +When('I send a request to remove a service', async () => { + deleteServiceResponse = await servicesApi.delete({ + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + }); +}); + +Then('the delete service response contains no data', function () { + assert.deepEqual(deleteServiceResponse, {}); +}); + +When('I send a request to list the numbers associated to a fax service', async () => { + listNumbersResponse = await servicesApi.listNumbers({ + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + }); +}); + +Then('the response contains {string} numbers associated to the fax service', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.strictEqual(listNumbersResponse.data.length, expectedNumbers); +}); + +When('I send a request to list all the numbers associated to a fax service', async () => { + for await (const number of servicesApi.listNumbers({ serviceId: '01W4FFL35P4NC4K35FAXSERVICE' })) { + numbersList.push(number); + } +}); + +Then('the phone numbers list contains {string} numbers associated to the fax service', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.strictEqual(numbersList.length, expectedNumbers); +}); + +When('I send a request to list the emails associated to a phone number', async () => { + listEmailsResponse = await servicesApi.listEmailsForNumber({ + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + phoneNumber: '+12014444444', + }); +}); + +Then('the response contains {string} emails associated to the phone number', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.strictEqual(listEmailsResponse.data.length, expectedNumbers); +}); + +When('I send a request to list all the emails associated to a phone number', async () => { + for await (const email of servicesApi.listEmailsForNumber({ + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + phoneNumber: '+12014444444' }) + ) { + emailsList.push(email); + } +}); + +Then('the emails list contains {string} emails associated to a phone number', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.strictEqual(emailsList.length, expectedNumbers); +}); diff --git a/packages/numbers/CHANGELOG.md b/packages/numbers/CHANGELOG.md index d86af761..e8eedae4 100644 --- a/packages/numbers/CHANGELOG.md +++ b/packages/numbers/CHANGELOG.md @@ -1,3 +1,22 @@ +## Version 1.2.0 +- [Tech] Update dependency `@sinch/sdk-client` to `1.2.0`. +- [Bugfix] Remove the "scheduledProvisioning" properties for SMS and Voice in the update number request. +- [Deprecation notice] `availableNumber` and `activeNumber` subdomain are deprecated and all methods are now defined on the upper numbers service. + All the methods names are the same except `availableNumber.list()` -> `searchForAvailableNumbers()` + +| Deprecated | New | +|------------------------------------------------------|----------------------------------------------| +| `numbersService.availableNumber.checkAvailability()` | `numbersService.checkAvailability()` | +| `numbersService.availableNumber.list()` | `numbersService.searchForAvailableNumbers()` | +| `numbersService.availableNumber.rent()` | `numbersService.rent()` | +| `numbersService.availableNumber.rentAny()` | `numbersService.rentAny()` | +| `numbersService.activeNumber.get()` | `numbersService.get()` | +| `numbersService.activeNumber.list()` | `numbersService.list()` | +| `numbersService.activeNumber.update()` | `numbersService.update()` | +| `numbersService.activeNumber.release()` | `numbersService.release()` | + +- [E2E] Add Cucumber steps implementation. + ## Version 1.1.0 - [Tech] Update dependency `@sinch/sdk-client` to `1.1.0` - [Feature] Update voiceConfiguration object to support Fax and EST configuration properties diff --git a/packages/numbers/cucumber.js b/packages/numbers/cucumber.js new file mode 100644 index 00000000..691a9809 --- /dev/null +++ b/packages/numbers/cucumber.js @@ -0,0 +1,8 @@ +module.exports = { + default: [ + 'tests/e2e/features/**/*.feature', + '--require-module ts-node/register', + '--require tests/rest/v1/**/*.steps.ts', + `--format-options '{"snippetInterface": "synchronous"}'`, + ].join(' '), +}; diff --git a/packages/numbers/package.json b/packages/numbers/package.json index 2990cf73..9ddf77d7 100644 --- a/packages/numbers/package.json +++ b/packages/numbers/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/numbers", - "version": "1.1.0", + "version": "1.2.0", "description": "Sinch Numbers API", "homepage": "", "repository": { @@ -25,13 +25,15 @@ "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf dist tsconfig.tsbuildinfo", - "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests && rimraf tsconfig.build.tsbuildinfo" + "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests && rimraf tsconfig.build.tsbuildinfo", + "test:e2e": "cucumber-js" }, "dependencies": { - "@sinch/sdk-client": "^1.1.0" + "@sinch/sdk-client": "^1.2.0" }, "devDependencies": {}, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/numbers/src/models/v1/active-number/active-number.ts b/packages/numbers/src/models/v1/active-number/active-number.ts index 132fbf9b..5de0141b 100644 --- a/packages/numbers/src/models/v1/active-number/active-number.ts +++ b/packages/numbers/src/models/v1/active-number/active-number.ts @@ -1,6 +1,6 @@ import { Money } from '../money'; -import { SMSConfiguration } from '../sms-configuration'; -import { VoiceConfiguration } from '../voice-configuration'; +import { SMSConfigurationResponse } from '../sms-configuration'; +import { VoiceConfigurationResponse } from '../voice-configuration'; import { CapabilitiesEnum, NumberTypeEnum } from '../enums'; /** @@ -28,9 +28,9 @@ export interface ActiveNumber { /** The timestamp when the subscription will expire if an expiration date has been set. */ expireAt?: Date | null; /** @see SMSConfiguration */ - smsConfiguration?: SMSConfiguration; + smsConfiguration?: SMSConfigurationResponse; /** @see VoiceConfiguration */ - voiceConfiguration?: VoiceConfiguration; + voiceConfiguration?: VoiceConfigurationResponse; /** The active number\'s callback URL to be called for provisioning / deprovisioning updates */ callbackUrl?: string; } diff --git a/packages/numbers/src/models/v1/sms-configuration/index.ts b/packages/numbers/src/models/v1/sms-configuration/index.ts index 6dd3fce9..c25d483b 100644 --- a/packages/numbers/src/models/v1/sms-configuration/index.ts +++ b/packages/numbers/src/models/v1/sms-configuration/index.ts @@ -1 +1 @@ -export type { SMSConfiguration } from './sms-configuration'; +export type { SMSConfiguration, SMSConfigurationResponse } from './sms-configuration'; diff --git a/packages/numbers/src/models/v1/sms-configuration/sms-configuration.ts b/packages/numbers/src/models/v1/sms-configuration/sms-configuration.ts index 30bc8b08..91202cd9 100644 --- a/packages/numbers/src/models/v1/sms-configuration/sms-configuration.ts +++ b/packages/numbers/src/models/v1/sms-configuration/sms-configuration.ts @@ -8,6 +8,9 @@ export interface SMSConfiguration { servicePlanId: string; /** Only for US virtual numbers. This campaign ID relates to 10DLC numbers. So, it is the current campaign ID for this number. The `campaignId` is found on your TCR platform. */ campaignId?: string; +} + +export interface SMSConfigurationResponse extends SMSConfiguration { /** @see ScheduledProvisioning */ scheduledProvisioning?: ScheduledProvisioning | null; } diff --git a/packages/numbers/src/models/v1/voice-configuration/index.ts b/packages/numbers/src/models/v1/voice-configuration/index.ts index c48d6541..5c172347 100644 --- a/packages/numbers/src/models/v1/voice-configuration/index.ts +++ b/packages/numbers/src/models/v1/voice-configuration/index.ts @@ -1 +1,7 @@ -export type { VoiceConfiguration } from './voice-configuration'; +export type { + VoiceConfiguration, + VoiceConfigurationRtc, + VoiceConfigurationFax, + VoiceConfigurationEst, + VoiceConfigurationResponse, +} from './voice-configuration'; diff --git a/packages/numbers/src/models/v1/voice-configuration/voice-configuration.ts b/packages/numbers/src/models/v1/voice-configuration/voice-configuration.ts index 92b5bbdb..3933b0b5 100644 --- a/packages/numbers/src/models/v1/voice-configuration/voice-configuration.ts +++ b/packages/numbers/src/models/v1/voice-configuration/voice-configuration.ts @@ -1,9 +1,35 @@ import { ScheduledVoiceProvisioning } from '../scheduled-voice-provisioning'; +/** + * The current voice configuration for this number. + */ +export type VoiceConfiguration = VoiceConfigurationRtc | VoiceConfigurationFax | VoiceConfigurationEst; + +export interface VoiceConfigurationRtc { + /** The voice application type. */ + type: 'RTC' + /** Your app ID for the Voice API. The `appId` can be found in your Sinch Customer Dashboard under Voice, then apps. */ + appId?: string; +} + +export interface VoiceConfigurationFax { + /** The voice application type. */ + type: 'FAX' + /** The service ID for the FAX configuration. The `serviceId` can be found in your Sinch Customer Dashboard under fax, then services.*/ + serviceId?: string; +} + +export interface VoiceConfigurationEst { + /** The voice application type. */ + type: 'EST' + /** The trunk ID for the EST voice configuration. The `trunkId` can be found in your Sinch Customer Dashboard under sip, then trunks.*/ + trunkId?: string; +} + /** * The current voice configuration for this number. During scheduled provisioning, the app ID value may be empty in a response if it is still processing or if it has failed. The status of scheduled provisioning will show under a `scheduledVoiceProvisioning` object if it\'s still running. Once processed successfully, the `appId` sent will appear directly under the `voiceConfiguration` object. */ -export interface VoiceConfiguration { +export interface VoiceConfigurationResponse { /** Your app ID for the Voice API. The `appId` can be found in your Sinch Customer Dashboard under Voice, then apps. */ appId?: string; /** Timestamp when the status was last updated. */ diff --git a/packages/numbers/src/rest/v1/numbers-service.ts b/packages/numbers/src/rest/v1/numbers-service.ts index cc6002af..d6a1aa6e 100644 --- a/packages/numbers/src/rest/v1/numbers-service.ts +++ b/packages/numbers/src/rest/v1/numbers-service.ts @@ -1,8 +1,21 @@ -import { SinchClientParameters } from '@sinch/sdk-client'; +import { ApiListPromise, SinchClientParameters } from '@sinch/sdk-client'; import { AvailableRegionsApi } from './available-regions'; import { CallbacksApi } from './callbacks'; import { AvailableNumberApi } from './available-number'; import { ActiveNumberApi } from './active-number'; +import { + ActiveNumber, + AvailableNumber, + AvailableNumbersResponse, + GetActiveNumberRequestData, + GetAvailableNumberRequestData, + ListActiveNumbersRequestData, + ListAvailableNumbersRequestData, + ReleaseNumberRequestData, + RentAnyNumberRequestData, + RentNumberRequestData, + UpdateActiveNumberRequestData, +} from '../../models'; /** * The Numbers Service exposes the following APIs: @@ -14,7 +27,9 @@ import { ActiveNumberApi } from './active-number'; export class NumbersService { public readonly availableRegions: AvailableRegionsApi; public readonly callbacks: CallbacksApi; + /** @deprecated Use the methods exposed at the Numbers Service level instead */ public readonly availableNumber: AvailableNumberApi; + /** @deprecated Use the methods exposed at the Numbers Service level instead */ public readonly activeNumber: ActiveNumberApi; /** @@ -45,4 +60,73 @@ export class NumbersService { this.availableRegions.setHostname(hostname); this.callbacks.setHostname(hostname); } + + /** + * This endpoint allows you to enter a specific phone number to check if it's available for use. + * A 200 response will return the number's capability, setup costs, monthly costs and if supporting documentation is required. + * If the phone number is not available, the API will return a 404 error. + * @param {GetAvailableNumberRequestData} data - The data to provide to the API call. + */ + public async checkAvailability(data: GetAvailableNumberRequestData): Promise { + return this.availableNumber.checkAvailability(data); + } + + /** + * Search for virtual numbers that are available for you to activate. You can filter by any property on the available number resource. + * @param {ListAvailableNumbersRequestData} data - The data to provide to the API call. + */ + public async searchForAvailableNumbers(data: ListAvailableNumbersRequestData): Promise { + return this.availableNumber.list(data); + } + + /** + * Rent any virtual number that matches the criteria (Search for and activate an available Sinch virtual number all in one API call). + * @param {RentAnyNumberRequestData} data - The data to provide to the API call. + */ + public async rentAny(data: RentAnyNumberRequestData): Promise { + return this.availableNumber.rentAny(data); + } + + /** + * Rent a virtual number to use with SMS products, Voice products, or both. You'll use 'smsConfiguration' to set up your number for SMS and 'voiceConfiguration' for Voice. + * @param {RentNumberRequestData} data - The data to provide to the API call. + */ + public async rent(data: RentNumberRequestData): Promise { + return this.availableNumber.rent(data); + } + + /** + * Retrieve a virtual number's details + * @param {GetActiveNumberRequestData} data - The data to provide to the API call. + */ + public async get(data: GetActiveNumberRequestData): Promise { + return this.activeNumber.get(data); + } + + /** + * Lists all virtual numbers for a project. + * @param {ListActiveNumbersRequestData} data - The data to provide to the API call. + * @return {ApiListPromise} + */ + public list(data: ListActiveNumbersRequestData): ApiListPromise { + return this.activeNumber.list(data); + } + + /** + * Release number. + * With this endpoint, you can cancel your subscription for a specific virtual phone number. + * @param {ReleaseNumberRequestData} data - The data to provide to the API call. + */ + public async release(data: ReleaseNumberRequestData): Promise { + return this.activeNumber.release(data); + } + + /** + * Update a virtual phone number. For example: you can configure SMS/Voice services or set a friendly name. To update the name that displays, modify the `displayName` parameter. + * You'll use `smsConfiguration` to update your SMS configuration and `voiceConfiguration` to update the voice configuration. + * @param {UpdateActiveNumberRequestData} data - The data to provide to the API call. + */ + public async update(data: UpdateActiveNumberRequestData): Promise { + return this.activeNumber.update(data); + } } diff --git a/packages/numbers/tests/rest/v1/available-regions/available-regions.steps.ts b/packages/numbers/tests/rest/v1/available-regions/available-regions.steps.ts new file mode 100644 index 00000000..db5686eb --- /dev/null +++ b/packages/numbers/tests/rest/v1/available-regions/available-regions.steps.ts @@ -0,0 +1,55 @@ +import { AvailableRegionsApi, NumbersService, Numbers } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let regionsApi: AvailableRegionsApi; +let regions: Numbers.ListAvailableRegionsResponse; + +const countRegionType = (regions: Numbers.AvailableRegion[], type: Numbers.RegionNumberTypeEnum) => { + return regions.reduce((count, region) => { + return region.types?.includes(type) ? count + 1 : count; + }, 0); +}; + +Given('the Numbers service "Regions" is available', function () { + const numbersService = new NumbersService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + numbersHostname: 'http://localhost:3013', + }); + regionsApi = numbersService.availableRegions; +}); + +When('I send a request to list all the regions', async () => { + regions = await regionsApi.list({}); +}); + +When('I send a request to list the TOLL_FREE regions', async () => { + regions = await regionsApi.list({ types: ['TOLL_FREE'] }); +}); + +When('I send a request to list the TOLL_FREE or MOBILE regions', async () => { + regions = await regionsApi.list({ types: ['TOLL_FREE', 'MOBILE'] }); +}); + +Then('the response contains {string} regions', (expectedAnswer: string) => { + const expectedRegionsNumber = parseInt(expectedAnswer, 10); + assert.equal(regions.availableRegions?.length, expectedRegionsNumber); +}); + +Then('the response contains {string} TOLL_FREE regions', (expectedAnswer: string) => { + const expectedRegionsNumber = parseInt(expectedAnswer, 10); + assert.equal(countRegionType(regions.availableRegions!, 'TOLL_FREE'), expectedRegionsNumber); +}); + +Then('the response contains {string} MOBILE regions', (expectedAnswer: string) => { + const expectedRegionsNumber = parseInt(expectedAnswer, 10); + assert.equal(countRegionType(regions.availableRegions!, 'MOBILE'), expectedRegionsNumber); +}); + +Then('the response contains {string} LOCAL regions', (expectedAnswer: string) => { + const expectedRegionsNumber = parseInt(expectedAnswer, 10); + assert.equal(countRegionType(regions.availableRegions!, 'LOCAL'), expectedRegionsNumber); +}); diff --git a/packages/numbers/tests/rest/v1/callbacks/calback-configuration.steps.ts b/packages/numbers/tests/rest/v1/callbacks/calback-configuration.steps.ts new file mode 100644 index 00000000..70a9c91d --- /dev/null +++ b/packages/numbers/tests/rest/v1/callbacks/calback-configuration.steps.ts @@ -0,0 +1,50 @@ +import { Given, Then, When } from '@cucumber/cucumber'; +import { CallbacksApi, NumbersService, Numbers } from '../../../../src'; +import * as assert from 'assert'; + +let callbackConfigurationApi: CallbacksApi; +let callbackConfiguration: Numbers.CallbackConfiguration; +let error: any; +Given('the Numbers service "Callback Configuration" is available', function () { + const numbersService = new NumbersService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + numbersHostname: 'http://localhost:3013', + }); + callbackConfigurationApi = numbersService.callbacks; +}); + +When('I send a request to retrieve the callback configuration', async () => { + callbackConfiguration = await callbackConfigurationApi.get({}); +}); + +Then('the response contains the project\'s callback configuration', () => { + assert.equal(callbackConfiguration.projectId, '12c0ffee-dada-beef-cafe-baadc0de5678'); + assert.equal(callbackConfiguration.hmacSecret, '0default-pass-word-*max-36characters'); +}); + +When('I send a request to update the callback configuration with the secret {}', async (hmacSecret: string) => { + try { + callbackConfiguration = await callbackConfigurationApi.update({ + updateCallbackConfigurationRequestBody: { + hmacSecret, + }, + }); + } catch (e) { + error = e; + } +}); + +Then('the response contains the updated project\'s callback configuration', () => { + assert.equal(callbackConfiguration.projectId, '12c0ffee-dada-beef-cafe-baadc0de5678'); + assert.equal(callbackConfiguration.hmacSecret, 'strongPa$$PhraseWith36CharactersMax'); +}); + +Then('the response contains an error', () => { + const notFound = JSON.parse(error.data) as Numbers.NotFound; + const notFoundError = notFound.error!; + assert.equal(notFoundError.code, 404); + assert.equal(notFoundError.status, 'NOT_FOUND'); +}); diff --git a/packages/numbers/tests/rest/v1/numbers.steps.ts b/packages/numbers/tests/rest/v1/numbers.steps.ts new file mode 100644 index 00000000..6dcc215f --- /dev/null +++ b/packages/numbers/tests/rest/v1/numbers.steps.ts @@ -0,0 +1,285 @@ +import { Given, Then, When } from '@cucumber/cucumber'; +import { NumbersService, Numbers } from '../../../src'; +import { PageResult } from '@sinch/sdk-client'; +import assert from 'assert'; + +let numbersService: NumbersService; +let availableNumbersResponse: Numbers.AvailableNumbersResponse; +let availablePhoneNumber: Numbers.AvailableNumber; +let listActiveNumbersResponse: PageResult; +const activeNumbersList: Numbers.ActiveNumber[] = []; +let activeNumber: Numbers.ActiveNumber; +let error: any; + +Given('the Numbers service is available', function () { + numbersService = new NumbersService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + numbersHostname: 'http://localhost:3013', + }); +}); + +When('I send a request to search for available phone numbers', async () => { + availableNumbersResponse = await numbersService.searchForAvailableNumbers({ + regionCode: 'US', + type: 'LOCAL', + }); +}); + +Then('the response contains {string} available phone numbers', (expectedAnswer: string) => { + const expectedPhoneNumbersCount = parseInt(expectedAnswer, 10); + assert.equal(availableNumbersResponse.availableNumbers?.length, expectedPhoneNumbersCount); +}); + +Then('a phone number contains all the expected properties', () => { + const phoneNumber = availableNumbersResponse.availableNumbers![0]; + assert.equal(phoneNumber.phoneNumber, '+12013504948'); + assert.equal(phoneNumber.regionCode, 'US'); + assert.equal(phoneNumber.type, 'LOCAL'); + assert.deepEqual(phoneNumber.capability, ['SMS', 'VOICE']); + assert.deepEqual(phoneNumber.setupPrice, { currencyCode: 'EUR', amount: '0.80' }); + assert.deepEqual(phoneNumber.monthlyPrice, { currencyCode: 'EUR', amount: '0.80' }); + assert.equal(phoneNumber.paymentIntervalMonths, 1); + assert.equal(phoneNumber.supportingDocumentationRequired, true); +}); + +When('I send a request to check the availability of the phone number {string}', async (phoneNumber: string) => { + availablePhoneNumber = await numbersService.checkAvailability({ phoneNumber }); +}); + +Then('the response displays the phone number {string} details', (phoneNumber: string) => { + assert.equal(availablePhoneNumber.phoneNumber, phoneNumber); +}); + +Then('the response contains an error about the number {string} not being available', (phoneNumber: string) => { + const notFound = availablePhoneNumber as Numbers.NotFound; + const notFoundError = notFound.error!; + assert.equal(notFoundError.code, 404); + assert.equal(notFoundError.status, 'NOT_FOUND'); + assert.equal((notFoundError.details![0] as any).resourceName, phoneNumber); +}); + +When('I send a request to rent a number with some criteria', async () => { + activeNumber = await numbersService.rentAny({ + rentAnyNumberRequestBody: { + regionCode: 'US', + type: 'LOCAL', + capabilities: ['SMS', 'VOICE'], + smsConfiguration: { + servicePlanId: 'SpaceMonkeySquadron', + }, + voiceConfiguration: { + appId: 'sunshine-rain-drop-very-beautifulday', + }, + numberPattern: { + pattern: '7654321', + searchPattern: 'END', + }, + }, + }); +}); + +Then('the response contains a rented phone number', () => { + assert.equal(activeNumber.phoneNumber, '+12017654321'); + assert.equal(activeNumber.projectId, '123c0ffee-dada-beef-cafe-baadc0de5678'); + assert.equal(activeNumber.displayName, ''); + assert.equal(activeNumber.regionCode, 'US'); + assert.equal(activeNumber.type, 'LOCAL'); + assert.deepEqual(activeNumber.capability, ['SMS', 'VOICE']); + assert.deepEqual(activeNumber.money, { currencyCode: 'EUR', amount: '0.80' }); + assert.equal(activeNumber.paymentIntervalMonths, 1); + assert.deepEqual(activeNumber.nextChargeDate, new Date('2024-06-06T14:42:42.022227Z')); + assert.equal(activeNumber.expireAt, null); + const expectedSmsConfiguration: Numbers.SMSConfigurationResponse = { + servicePlanId: '', + campaignId: '', + scheduledProvisioning: { + servicePlanId: 'SpaceMonkeySquadron', + campaignId: '', + status: 'WAITING', + lastUpdatedTime: new Date('2024-06-06T14:42:42.596223Z'), + errorCodes: [], + }, + }; + assert.deepEqual(activeNumber.smsConfiguration, expectedSmsConfiguration); + const expectedVoiceConfiguration: Numbers.VoiceConfigurationResponse = { + type: 'RTC', + appId: '', + trunkId: '', + serviceId: '', + lastUpdatedTime: null, + scheduledVoiceProvisioning: { + type: 'RTC', + appId: 'sunshine-rain-drop-very-beautifulday', + trunkId: '', + serviceId: '', + status: 'WAITING', + lastUpdatedTime: new Date('2024-06-06T14:42:42.604092Z'), + }, + }; + assert.deepEqual(activeNumber.voiceConfiguration, expectedVoiceConfiguration); + assert.equal(activeNumber.callbackUrl, ''); +}); + +When('I send a request to rent the phone number {string}', async (phoneNumber: string) => { + activeNumber = await numbersService.rent({ + phoneNumber, + rentNumberRequestBody: { + smsConfiguration: { + servicePlanId: 'SpaceMonkeySquadron', + }, + voiceConfiguration: { + appId: 'sunshine-rain-drop-very-beautifulday', + }, + }, + }); +}); + +Then('the response contains this rented phone number {string}', (phoneNumber: string) => { + assert.equal(activeNumber.phoneNumber, phoneNumber); +}); + +When('I send a request to rent the unavailable phone number {string}', async (phoneNumber: string) => { + activeNumber = await numbersService.rent({ + phoneNumber, + rentNumberRequestBody: { + smsConfiguration: { + servicePlanId: 'SpaceMonkeySquadron', + }, + voiceConfiguration: { + appId: 'sunshine-rain-drop-very-beautifulday', + }, + }, + }); +}); + +When('I send a request to list the phone numbers', async () => { + listActiveNumbersResponse = await numbersService.list({ + regionCode: 'US', + type: 'LOCAL', + }); +}); + +Then('the response contains {string} phone numbers', (expectedAnswer: string) => { + const expectedPhoneNumbersCount = parseInt(expectedAnswer, 10); + assert.equal(listActiveNumbersResponse.data.length, expectedPhoneNumbersCount); +}); + +When('I send a request to list all the phone numbers', async () => { + const requestData: Numbers.ListActiveNumbersRequestData = { + regionCode: 'US', + type: 'LOCAL', + }; + for await (const number of numbersService.list(requestData)) { + activeNumbersList.push(number); + } +}); + +Then('the phone numbers list contains {string} phone numbers', (expectedAnswer: string) => { + const expectedNumbers = parseInt(expectedAnswer, 10); + assert.strictEqual(activeNumbersList.length, expectedNumbers); + const phoneNumber1 = activeNumbersList[0]; + assert.equal(phoneNumber1.voiceConfiguration?.type, 'FAX'); + assert.ok(phoneNumber1.voiceConfiguration?.serviceId !== ''); + const phoneNumber2 = activeNumbersList[1]; + assert.equal(phoneNumber2.voiceConfiguration?.type, 'EST'); + assert.ok(phoneNumber2.voiceConfiguration?.trunkId !== ''); + const phoneNumber3 = activeNumbersList[2]; + assert.equal(phoneNumber3.voiceConfiguration?.type, 'RTC'); + assert.ok(phoneNumber3.voiceConfiguration?.appId !== ''); +}); + +When('I send a request to update the phone number {string}', async (phoneNumber: string) => { + activeNumber = await numbersService.update({ + phoneNumber, + updateActiveNumberRequestBody: { + displayName: 'Updated description during E2E tests', + smsConfiguration: { + servicePlanId: 'SingingMooseSociety', + }, + voiceConfiguration: { + type: 'FAX', + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + }, + callbackUrl: 'https://my-callback-server.com/numbers', + }, + }); +}); + +Then('the response contains a phone number with updated parameters', () => { + assert.equal(activeNumber.displayName, 'Updated description during E2E tests'); + assert.equal(activeNumber.callbackUrl, 'https://my-callback-server.com/numbers'); + const smsConfiguration: Numbers.SMSConfigurationResponse = { + servicePlanId: 'SpaceMonkeySquadron', + campaignId: '', + scheduledProvisioning: { + servicePlanId: 'SingingMooseSociety', + campaignId: '', + status: 'WAITING', + lastUpdatedTime: new Date('2024-06-06T20:02:20.432220Z'), + errorCodes: [], + }, + }; + assert.deepEqual(activeNumber.smsConfiguration, smsConfiguration); + const voiceVonfiguration: Numbers.VoiceConfigurationResponse = { + type: 'RTC', + appId: 'sunshine-rain-drop-very-beautifulday', + trunkId: '', + serviceId: '', + lastUpdatedTime: null, + scheduledVoiceProvisioning: { + status: 'WAITING', + type: 'FAX', + appId: '', + trunkId: '', + serviceId: '01W4FFL35P4NC4K35FAXSERVICE', + lastUpdatedTime: new Date('2024-06-06T20:02:20.437509Z'), + }, + }; + assert.deepEqual(activeNumber.voiceConfiguration, voiceVonfiguration); +}); + +When('I send a request to retrieve the phone number {string}', async (phoneNumber: string) => { + try { + activeNumber = await numbersService.get({ phoneNumber }); + } catch (e) { + error = e; + } +}); + +Then('the response contains details about the phone number {string}', (phoneNumber: string) => { + assert.equal(activeNumber.phoneNumber, phoneNumber); + assert.deepEqual(activeNumber.nextChargeDate, new Date('2024-06-06T14:42:42.677575Z')); + assert.equal(activeNumber.expireAt, null); + assert.equal(activeNumber.smsConfiguration?.servicePlanId, 'SpaceMonkeySquadron'); +}); + +// eslint-disable-next-line max-len +Then('the response contains details about the phone number {string} with an SMS provisioning error', (phoneNumber: string) => { + assert.equal(activeNumber.phoneNumber, phoneNumber); + assert.deepEqual(activeNumber.nextChargeDate, new Date('2024-07-06T14:42:42.677575Z')); + assert.equal(activeNumber.expireAt, null); + assert.equal(activeNumber.smsConfiguration?.servicePlanId, ''); + assert.equal(activeNumber.smsConfiguration?.scheduledProvisioning?.status, 'FAILED'); + assert.deepEqual(activeNumber.smsConfiguration?.scheduledProvisioning?.errorCodes, ['SMS_PROVISIONING_FAILED']); +}); + +Then('the response contains an error about the number {string} not being a rented number', (phoneNumber: string) => { + const notFound = JSON.parse(error.data) as Numbers.NotFound; + const notFoundError = notFound.error!; + assert.equal(notFoundError.code, 404); + assert.equal(notFoundError.status, 'NOT_FOUND'); + assert.equal((notFoundError.details![0] as any).resourceName, phoneNumber); +}); + +When('I send a request to release the phone number {string}', async (phoneNumber: string) => { + activeNumber = await numbersService.release({ phoneNumber }); +}); + +Then('the response contains details about the phone number {string} to be released', (phoneNumber: string) => { + assert.equal(activeNumber.phoneNumber, phoneNumber); + assert.deepEqual(activeNumber.nextChargeDate, new Date('2024-06-06T14:42:42.677575Z')); + assert.deepEqual(activeNumber.expireAt, new Date('2024-06-06T14:42:42.677575Z')); +}); diff --git a/packages/numbers/tests/rest/v1/webhooks/webhooks.steps.ts b/packages/numbers/tests/rest/v1/webhooks/webhooks.steps.ts new file mode 100644 index 00000000..e35fdc7a --- /dev/null +++ b/packages/numbers/tests/rest/v1/webhooks/webhooks.steps.ts @@ -0,0 +1,50 @@ +import { Given, Then, When } from '@cucumber/cucumber'; +import { NumbersCallbackWebhooks, Numbers } from '../../../../src'; +import assert from 'assert'; +import { IncomingHttpHeaders } from 'http'; + +const SINCH_NUMBERS_CALLBACK_SECRET = 'strongPa$$PhraseWith36CharactersMax'; +let numbersCallbackWebhook: NumbersCallbackWebhooks; +let rawEvent: any; +let event: Numbers.CallbackPayload; +let formattedHeaders: IncomingHttpHeaders; + +const processEvent = async (response: Response) => { + formattedHeaders = {}; + response.headers.forEach((value, name) => { + formattedHeaders[name.toLowerCase()] = value; + }); + rawEvent = await response.text(); + rawEvent = rawEvent.replace(/\s+/g, ''); + event = numbersCallbackWebhook.parseEvent(JSON.parse(rawEvent)); +}; + +Given('the Numbers Webhooks handler is available', function () { + numbersCallbackWebhook = new NumbersCallbackWebhooks(SINCH_NUMBERS_CALLBACK_SECRET); +}); + +When('I send a request to trigger the success to provision to voice platform event', async () => { + const response = await fetch('http://localhost:3013/webhooks/numbers/provisioning_to_voice_platform/succeeded'); + await processEvent(response); +}); + +Then('the event header contains a valid signature', () => { + assert.ok(numbersCallbackWebhook.validateAuthenticationHeader(formattedHeaders, rawEvent)); +}); + +Then('the event describes a success to provision to voice platform event', () => { + assert.equal(event.eventType, 'PROVISIONING_TO_VOICE_PLATFORM'); + assert.equal(event.status, 'SUCCEEDED'); + assert.equal(event.failureCode, null); +}); + +When('I send a request to trigger the failure to provision to voice platform event', async () => { + const response = await fetch('http://localhost:3013/webhooks/numbers/provisioning_to_voice_platform/failed'); + await processEvent(response); +}); + +Then('the event describes a failure to provision to voice platform event', () => { + assert.equal(event.eventType, 'PROVISIONING_TO_VOICE_PLATFORM'); + assert.equal(event.status, 'FAILED'); + assert.equal(event.failureCode, 'PROVISIONING_TO_VOICE_PLATFORM_FAILED'); +}); diff --git a/packages/sdk-client/CHANGELOG.md b/packages/sdk-client/CHANGELOG.md index 2e150b4d..1d9f78ba 100644 --- a/packages/sdk-client/CHANGELOG.md +++ b/packages/sdk-client/CHANGELOG.md @@ -1,3 +1,12 @@ +## Version 1.2.0 +- [Feature] Support a new mode of pagination to support the Conversation API. +- [Feature] Remove the `TimezoneResponse` plugin and autocorrect the timezones when reviving the dates in the API responses. +- [Feature] Add utility methods to support the dateRange filters for EST and Fax APIs. +- [Bugfix] Handle HTTP status 202 in the `ExceptionResponse` plugin. +- [Feature][Functionality change] Implement new strategy for selecting which credentials to use with the SMS API. +- [Deprecation Notice] The property `forceOAuth2ForSmsApi` in the `UnifiedCredentials` is deprecated and has no effect anymore. +- [Deprecation Notice] The property `forceServicePlanIdUsageForSmsApi` in the `ServicePlanIdCredentials` is deprecated and has no effect anymore. + ## Version 1.1.0 - [Feature] Add new pagination type support specific to EST diff --git a/packages/sdk-client/package.json b/packages/sdk-client/package.json index c87ee88a..05fa28f5 100644 --- a/packages/sdk-client/package.json +++ b/packages/sdk-client/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/sdk-client", - "version": "1.1.0", + "version": "1.2.0", "description": "Core services related to interacting with Sinch API", "homepage": "", "repository": { @@ -38,6 +38,7 @@ "typescript": "^5.2.2" }, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/sdk-client/src/api/api-client-options-helper.ts b/packages/sdk-client/src/api/api-client-options-helper.ts index a0b7ba22..81686770 100644 --- a/packages/sdk-client/src/api/api-client-options-helper.ts +++ b/packages/sdk-client/src/api/api-client-options-helper.ts @@ -1,4 +1,4 @@ -import { SmsRegion, SinchClientParameters } from '../domain'; +import { SinchClientParameters } from '../domain'; import { ApiClientOptions } from './api-client-options'; import { ApiTokenRequest, @@ -36,36 +36,27 @@ export const buildApplicationSignedApiClientOptions = ( return apiClientOptions; }; -export const buildFlexibleOAuth2OrApiTokenApiClientOptions = ( - params: SinchClientParameters, region: SmsRegion, apiName: string, -): ApiClientOptions => { +export const buildFlexibleOAuth2OrApiTokenApiClientOptions = (params: SinchClientParameters): ApiClientOptions => { let apiClientOptions: ApiClientOptions | undefined; - // Check the region: if US or EU, try to use the OAuth2 authentication with the access key / secret under the project Id - if ( params.forceOAuth2ForSmsApi - || (!params.forceServicePlanIdUsageForSmsApi && (region === SmsRegion.UNITED_STATES || region === SmsRegion.EUROPE)) - ) { - // Let's check the required parameters for OAuth2 authentication - if (params.projectId && params.keyId && params.keySecret) { - apiClientOptions = { - projectId: params.projectId, - requestPlugins: [new Oauth2TokenRequest(params.keyId, params.keySecret, params.authHostname)], - useServicePlanId: false, - }; - } - } - if (!apiClientOptions) { - // The API client options couldn't be initialized for with the projectId unified authentication. - // Let's try with the servicePlanId - if (params.servicePlanId && params.apiToken) { - apiClientOptions = { - projectId: params.servicePlanId, - requestPlugins: [new ApiTokenRequest(params.apiToken)], - useServicePlanId: true, - }; + + if (params.servicePlanId && params.apiToken) { + apiClientOptions = { + projectId: params.servicePlanId, + requestPlugins: [new ApiTokenRequest(params.apiToken)], + useServicePlanId: true, + }; + if (params.projectId || params.keyId || params.keySecret) { + console.warn('As the servicePlanId and the apiToken are provided, all other credentials will be disregarded.'); } + } else if (params.projectId && params.keyId && params.keySecret) { + apiClientOptions = { + projectId: params.projectId, + requestPlugins: [new Oauth2TokenRequest(params.keyId, params.keySecret, params.authHostname)], + useServicePlanId: false, + }; } if (!apiClientOptions) { - throw new Error(`Invalid parameters for the ${apiName} API: check your configuration`); + throw new Error('Invalid parameters for the SMS API: check your configuration'); } addPlugins(apiClientOptions, params); return apiClientOptions; diff --git a/packages/sdk-client/src/api/api-client.ts b/packages/sdk-client/src/api/api-client.ts index d1780a75..52ba9884 100644 --- a/packages/sdk-client/src/api/api-client.ts +++ b/packages/sdk-client/src/api/api-client.ts @@ -1,12 +1,13 @@ import { RequestBody, RequestOptions } from '../plugins/core/request-plugin'; import { ApiClientOptions } from './api-client-options'; import { Headers } from 'node-fetch'; -import FormData = require('form-data'); export enum PaginationEnum { NONE, /** Used by the Numbers API */ TOKEN, + /** Used by the Conversation API */ + TOKEN2, /** used by the SMS API */ PAGE, /** used by the Elastic SIP Trunking API */ @@ -219,18 +220,6 @@ export class ApiClient { throw new Error('Abstract method must be implemented'); } - /** - * Receives an object containing key/value pairs - * Encodes this object to match application/x-www-urlencoded or multipart/form-data - * @abstract - * @param {any} _data - * @param {string} _type - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - processFormData(_data: any, _type: string): FormData | string { - throw new Error('Abstract method must be implemented'); - } - } // ----- INTERNAL ------ \\ diff --git a/packages/sdk-client/src/client/api-client-helpers.ts b/packages/sdk-client/src/client/api-client-helpers.ts index 84a7acf1..4807191a 100644 --- a/packages/sdk-client/src/client/api-client-helpers.ts +++ b/packages/sdk-client/src/client/api-client-helpers.ts @@ -84,7 +84,7 @@ export const reviveDates = (input: any): any => { return newObj; } else if (isDateString(input)) { // Convert string date to Date object - return new Date(input); + return new Date(addTimezoneIfMissing(input)); } else { // Return other types as-is return input; @@ -98,3 +98,18 @@ const isDateString = (value: any): boolean => { } return false; }; + +const addTimezoneIfMissing = (timestampValue: string): string => { + // Check the formats +XX:XX, +XX, +XXXX and Z + const timeZoneRegex = /([+-]\d{2}(:\d{2})|[+-]\d{4}|Z)$/; + if (!timeZoneRegex.test(timestampValue)) { + const hourMinutesTimezoneRegex = /([+-]\d{2})$/; + // A timestamp with no minutes in the timezone cannot be converted into a Date => assume it's :00 + if (hourMinutesTimezoneRegex.test(timestampValue)) { + timestampValue = timestampValue + ':00'; + } else { + timestampValue = timestampValue + 'Z'; + } + } + return timestampValue; +}; diff --git a/packages/sdk-client/src/client/api-client-pagination-helper.ts b/packages/sdk-client/src/client/api-client-pagination-helper.ts index 8a720d02..31cdaf3d 100644 --- a/packages/sdk-client/src/client/api-client-pagination-helper.ts +++ b/packages/sdk-client/src/client/api-client-pagination-helper.ts @@ -60,7 +60,15 @@ class SinchIterator implements AsyncIterator { return updateQueryParamsAndSendRequest( this.apiClient, newParams, requestOptions, this.paginatedOperationProperties); } + if (this.paginatedOperationProperties.pagination === PaginationEnum.TOKEN2) { + const newParams = { + page_token: pageResult.nextPageValue, + }; + return updateQueryParamsAndSendRequest( + this.apiClient, newParams, requestOptions, this.paginatedOperationProperties); + } if (this.paginatedOperationProperties.pagination === PaginationEnum.PAGE + || this.paginatedOperationProperties.pagination === PaginationEnum.PAGE2 || this.paginatedOperationProperties.pagination === PaginationEnum.PAGE3) { const newParams = { page: pageResult.nextPageValue, @@ -136,6 +144,11 @@ export const createNextPageMethod = ( pageToken: nextPageValue, }; break; + case PaginationEnum.TOKEN2: + newParams = { + page_token: nextPageValue, + }; + break; case PaginationEnum.PAGE: case PaginationEnum.PAGE2: case PaginationEnum.PAGE3: @@ -167,7 +180,10 @@ export function hasMore( context: PaginationContext, ): boolean { if (context.pagination === PaginationEnum.TOKEN) { - return !!response['nextPageToken'] || !!response['next_page_token']; + return !!response['nextPageToken']; + } + if (context.pagination === PaginationEnum.TOKEN2) { + return !!response['next_page_token']; } if (context.pagination === PaginationEnum.PAGE) { const requestedPageSize = context.requestOptions.queryParams?.page_size; @@ -190,7 +206,10 @@ export function calculateNextPage( context: PaginationContext, ): string { if (context.pagination === PaginationEnum.TOKEN) { - return response['nextPageToken'] || response['next_page_token']; + return response['nextPageToken']; + } + if (context.pagination === PaginationEnum.TOKEN2) { + return response['next_page_token']; } if (context.pagination === PaginationEnum.PAGE) { const currentPage: number = response.page || 0; @@ -292,7 +311,7 @@ const calculateLastPageValue = ( case PaginationEnum.PAGE: return Math.ceil(response.count! / pageSize) - 1; case PaginationEnum.PAGE2: - return Math.ceil(response.totalItems! / pageSize) - 1; + return Math.ceil(response.totalItems! / pageSize); } }; diff --git a/packages/sdk-client/src/client/api-fetch-client.ts b/packages/sdk-client/src/client/api-fetch-client.ts index 6663ec6d..d6f38bb1 100644 --- a/packages/sdk-client/src/client/api-fetch-client.ts +++ b/packages/sdk-client/src/client/api-fetch-client.ts @@ -1,7 +1,6 @@ import { ResponsePlugin } from '../plugins/core/response-plugin'; import { VersionRequest } from '../plugins/version'; import { ExceptionResponse } from '../plugins/exception'; -import { TimezoneResponse } from '../plugins/timezone'; import { ApiClient, ApiCallParameters, @@ -19,7 +18,6 @@ import { ResponseJSONParseError, } from '../api/api-errors'; import fetch, { Response, Headers } from 'node-fetch'; -import FormData = require('form-data'); import { buildErrorContext, manageExpiredToken, reviveDates } from './api-client-helpers'; import { buildPaginationContext, @@ -43,7 +41,6 @@ export class ApiFetchClient extends ApiClient { ...options, requestPlugins: [new VersionRequest(), ...(options.requestPlugins || [])], responsePlugins: [ - new TimezoneResponse(), new ExceptionResponse(), ...(options.responsePlugins || []), ], @@ -281,29 +278,4 @@ export class ApiFetchClient extends ApiClient { return fileName; } - /** @inheritDoc */ - public processFormData(data: any, type: string): FormData | string { - - let encodedData: FormData | string; - - if (type === 'multipart/form-data') { - const formData: FormData = new FormData(); - for (const key in data) { - if (Object.prototype.hasOwnProperty.call(data, key)) { - formData.append(key, data[key]); - } - } - encodedData = formData; - } else { - const formData: string[] = []; - for (const key in data) { - if (Object.prototype.hasOwnProperty.call(data, key)) { - formData.push(`${key}=${encodeURIComponent(data[key])}`); - } - } - encodedData = formData.join('&'); - } - - return encodedData; - } } diff --git a/packages/sdk-client/src/domain/domain-interface.ts b/packages/sdk-client/src/domain/domain-interface.ts index 686be565..21f406b3 100644 --- a/packages/sdk-client/src/domain/domain-interface.ts +++ b/packages/sdk-client/src/domain/domain-interface.ts @@ -24,7 +24,7 @@ export interface UnifiedCredentials { keySecret: string; /** The region for the SMS API. Default region is US */ smsRegion?: SmsRegion; - /** boolean to force the usage of the OAuth2 authentication for the SMS API - to be used when a region other of US and EU supports OAuth2 but the SDK doesn't by default */ + /** @deprecated boolean to force the usage of the OAuth2 authentication for the SMS API - to be used when a region other of US and EU supports OAuth2 but the SDK doesn't by default */ forceOAuth2ForSmsApi?: boolean; /** The region for the Fax API. Default is auto-routing */ faxRegion?: FaxRegion; @@ -37,7 +37,7 @@ export interface ServicePlanIdCredentials { servicePlanId: string; /** Your API token. You can find this on your [Dashboard](https://dashboard.sinch.com/sms/api/rest). */ apiToken: string; - /** boolean to force the usage of the service plan Id + API token as credentials for the SMS API */ + /** @deprecated boolean to force the usage of the service plan Id + API token as credentials for the SMS API */ forceServicePlanIdUsageForSmsApi?: boolean; /** The region for the SMS API. Default region is US */ smsRegion?: SmsRegion; diff --git a/packages/sdk-client/src/plugins/exception/exception.response.ts b/packages/sdk-client/src/plugins/exception/exception.response.ts index f8836bd0..8f3db79a 100644 --- a/packages/sdk-client/src/plugins/exception/exception.response.ts +++ b/packages/sdk-client/src/plugins/exception/exception.response.ts @@ -48,7 +48,10 @@ export class ExceptionResponse< ); } else if (!res) { res = {} as V; - if (context.response.status !== 204 && context.response.status !== 200) { + if (context.response.status !== 204 + && context.response.status !== 200 + && context.response.status !== 202 + ) { res = {} as V; error = new EmptyResponseError( context.response.statusText, diff --git a/packages/sdk-client/src/plugins/index.ts b/packages/sdk-client/src/plugins/index.ts index caf2ec7d..c22f2a58 100644 --- a/packages/sdk-client/src/plugins/index.ts +++ b/packages/sdk-client/src/plugins/index.ts @@ -5,6 +5,5 @@ export * from './basicAuthentication'; export * from './exception'; export * from './oauth2'; export * from './signing'; -export * from './timezone'; export * from './version'; export * from './x-timestamp'; diff --git a/packages/sdk-client/src/plugins/timezone/index.ts b/packages/sdk-client/src/plugins/timezone/index.ts deleted file mode 100644 index e4b4b05e..00000000 --- a/packages/sdk-client/src/plugins/timezone/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './timezone.response'; diff --git a/packages/sdk-client/src/plugins/timezone/timezone.response.ts b/packages/sdk-client/src/plugins/timezone/timezone.response.ts deleted file mode 100644 index e5e11c8a..00000000 --- a/packages/sdk-client/src/plugins/timezone/timezone.response.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { ResponsePlugin, ResponsePluginContext } from '../core/response-plugin'; -import { PluginRunner } from '../core'; - -const buggyOperationIds: string[] = [ - 'GetCallResult', - 'VerificationStatusById', - 'VerificationStatusByIdentity', - 'VerificationStatusByReference', -]; - -const buggyFields: Record = { - 'GetCallResult': 'timestamp', - 'VerificationStatusById': 'verificationTimestamp', - 'VerificationStatusByIdentity': 'verificationTimestamp', - 'VerificationStatusByReference': 'verificationTimestamp', -}; - -export class TimezoneResponse | undefined = Record> -implements ResponsePlugin> { - - public load( - context: ResponsePluginContext, - ): PluginRunner, V> { - return { - transform(res: V) { - // HACK to fix a server-side bug: the timestamp is returned without the timezone - if (res && buggyOperationIds.includes(context.operationId) ) { - for (const key in res) { - if (Object.prototype.hasOwnProperty.call(res, key)) { - const buggyKey = buggyFields[context.operationId]; - if (key === buggyKey && typeof res[buggyKey] === 'string') { - let timestampValue = res[key] as string; - // Check the formats +XX:XX, +XX and Z - const timeZoneRegex = /([+-]\d{2}(:\d{2})|Z)$/; - if (!timeZoneRegex.test(timestampValue)) { - const hourMinutesTimezoneRegex = /([+-]\d{2})$/; - // A timestamp with no minutes in the timezone cannot be converted into a Date => assume it's :00 - if (hourMinutesTimezoneRegex.test(timestampValue)) { - timestampValue = timestampValue + ':00'; - } else { - timestampValue = timestampValue + 'Z'; - } - } - res[key] = timestampValue; - } - } - } - } - return res; - }, - }; - } - -} diff --git a/packages/sdk-client/src/utils/date.ts b/packages/sdk-client/src/utils/date.ts new file mode 100644 index 00000000..892bb65c --- /dev/null +++ b/packages/sdk-client/src/utils/date.ts @@ -0,0 +1,60 @@ +export interface DateFormat { + date: Date; + unit?: ChronoUnit; +} + +export type ChronoUnit = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'; + +export const formatDate = (date: Date, unit?: ChronoUnit): string => { + const year = date.getUTCFullYear(); + const month = String(date.getUTCMonth() + 1).padStart(2, '0'); + const day = String(date.getUTCDate()).padStart(2, '0'); + const hours = String(date.getUTCHours()).padStart(2, '0'); + const minutes = String(date.getUTCMinutes()).padStart(2, '0'); + const seconds = String(date.getUTCSeconds()).padStart(2, '0'); + + switch (unit) { + case 'year': + return `${year}`; + case 'month': + return `${year}-${month}`; + case 'day': + return `${year}-${month}-${day}`; + case 'hour': + return `${year}-${month}-${day}T${hours}:00:00Z`; + case 'minute': + return `${year}-${month}-${day}T${hours}:${minutes}:00Z`; + case 'second': + default: + return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}Z`; + } +}; + +export const formatCreateTimeFilter = (createTime: string | Date | undefined): string | undefined => { + if (createTime !== undefined) { + if (typeof createTime === 'string') { + if (createTime.indexOf('T') > -1) { + return createTime.substring(0, createTime.indexOf('T')); + } + return createTime; + } else { + return formatDate(createTime, 'day'); + } + } + return undefined; +}; + +export const formatCreateTimeRangeFilter = ( + timeBoundary: string | Date | DateFormat | undefined, +): string | undefined => { + if (timeBoundary !== undefined) { + if (typeof timeBoundary === 'string') { + return timeBoundary; + } else if (timeBoundary instanceof Date) { + return formatDate(timeBoundary); + } else { + return formatDate(timeBoundary.date, timeBoundary.unit); + } + } + return undefined; +}; diff --git a/packages/sdk-client/src/utils/index.ts b/packages/sdk-client/src/utils/index.ts index 2ea7bab3..a6ab6611 100644 --- a/packages/sdk-client/src/utils/index.ts +++ b/packages/sdk-client/src/utils/index.ts @@ -1,2 +1,3 @@ export * from './authorization.helper'; +export * from './date'; export * from './text-to-hex'; diff --git a/packages/sdk-client/tests/api/api-client-options-helper.test.ts b/packages/sdk-client/tests/api/api-client-options-helper.test.ts index af0f7cb6..7f404019 100644 --- a/packages/sdk-client/tests/api/api-client-options-helper.test.ts +++ b/packages/sdk-client/tests/api/api-client-options-helper.test.ts @@ -5,7 +5,6 @@ import { buildOAuth2ApiClientOptions, Oauth2TokenRequest, PluginRunner, - SmsRegion, SigningRequest, SinchClientParameters, XTimestampRequest, @@ -147,51 +146,48 @@ describe('API Client Options helper', () => { describe('buildFlexibleOAuth2OrApiTokenApiClientOptions', () => { + // eslint-disable-next-line max-len - it('should build some API client options to perform OAuth2 authentication when the credentials are present and the region is supported', () => { + it('should build some ApiClientOptions to perform OAuth2 authentication when only unified credentials are provided', () => { // Given const params: SinchClientParameters = { projectId: 'PROJECT_ID', keyId: 'KEY_ID', keySecret: 'KEY_SECRET', - servicePlanId: 'SERVICE_PLAN_ID', - apiToken: 'API_TOKEN', }; - const region = SmsRegion.EUROPE; // When - const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(params, region, 'foo'); + const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(params); // Then expect(apiClientOptions).toBeDefined(); + expect(apiClientOptions.useServicePlanId).toBeFalsy(); expect(apiClientOptions.requestPlugins?.length).toBe(1); expect(apiClientOptions.requestPlugins?.[0]).toBeInstanceOf(Oauth2TokenRequest); expect(apiClientOptions.responsePlugins).toBeUndefined(); }); // eslint-disable-next-line max-len - it('should build some API client options to perform API token authentication when the credentials are present and the region is not supported by OAuth2 authentication', () => { + it('should build some ApiClientOptions to perform API token authentication when only SMS credentials are provided', () => { // Given const params: SinchClientParameters = { - projectId: 'PROJECT_ID', - keyId: 'KEY_ID', - keySecret: 'KEY_SECRET', servicePlanId: 'SERVICE_PLAN_ID', apiToken: 'API_TOKEN', }; - const region = SmsRegion.CANADA; // When - const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(params, region, 'foo'); + const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(params); // Then expect(apiClientOptions).toBeDefined(); + expect(apiClientOptions.useServicePlanId).toBeTruthy(); expect(apiClientOptions.requestPlugins?.length).toBe(1); expect(apiClientOptions.requestPlugins?.[0]).toBeInstanceOf(ApiTokenRequest); expect(apiClientOptions.responsePlugins).toBeUndefined(); }); - it('should force the usage of API token authentication even when the region supports OAuth2 authentication', () => { + // eslint-disable-next-line max-len + it('should build some ApiClientOptions to perform API token authentication when both set of credentials are provided', () => { // Given const params: SinchClientParameters = { projectId: 'PROJECT_ID', @@ -199,21 +195,23 @@ describe('API Client Options helper', () => { keySecret: 'KEY_SECRET', servicePlanId: 'SERVICE_PLAN_ID', apiToken: 'API_TOKEN', - forceServicePlanIdUsageForSmsApi: true, }; - const region = SmsRegion.EUROPE; + console.warn = jest.fn(); // When - const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(params, region, 'foo'); + const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(params); // Then expect(apiClientOptions).toBeDefined(); + expect(apiClientOptions.useServicePlanId).toBeTruthy(); expect(apiClientOptions.requestPlugins?.length).toBe(1); expect(apiClientOptions.requestPlugins?.[0]).toBeInstanceOf(ApiTokenRequest); expect(apiClientOptions.responsePlugins).toBeUndefined(); + expect(console.warn).toHaveBeenCalledWith( + 'As the servicePlanId and the apiToken are provided, all other credentials will be disregarded.'); }); - it('should build some API client options with additional plugins', () => { + it('should build some ApiClientOptions with additional plugins', () => { // Given const params: SinchClientParameters = { servicePlanId: 'SERVICE_PLAN_ID', @@ -221,10 +219,9 @@ describe('API Client Options helper', () => { requestPlugins: [dummyRequestPlugin], responsePlugins: [dummyResponsePlugin], }; - const region = SmsRegion.CANADA; // When - const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(params, region, 'foo'); + const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(params); // Then expect(apiClientOptions.requestPlugins?.length).toBe(2); @@ -237,13 +234,12 @@ describe('API Client Options helper', () => { const params: SinchClientParameters = { projectId: 'PROJECT_ID', }; - const region = SmsRegion.EUROPE; // When - const buildApiClientOptionsFunction = () => buildFlexibleOAuth2OrApiTokenApiClientOptions(params, region, 'foo'); + const buildApiClientOptionsFunction = () => buildFlexibleOAuth2OrApiTokenApiClientOptions(params); // Then - expect(buildApiClientOptionsFunction).toThrow('Invalid parameters for the foo API: check your configuration'); + expect(buildApiClientOptionsFunction).toThrow('Invalid parameters for the SMS API: check your configuration'); }); // eslint-disable-next-line max-len @@ -252,49 +248,14 @@ describe('API Client Options helper', () => { const params: SinchClientParameters = { servicePlanId: 'SERVICE_PLAN_ID', }; - const region = SmsRegion.EUROPE; // When - const buildApiClientOptionsFunction = () => buildFlexibleOAuth2OrApiTokenApiClientOptions(params, region, 'foo'); + const buildApiClientOptionsFunction = () => buildFlexibleOAuth2OrApiTokenApiClientOptions(params); // Then - expect(buildApiClientOptionsFunction).toThrow('Invalid parameters for the foo API: check your configuration'); + expect(buildApiClientOptionsFunction).toThrow('Invalid parameters for the SMS API: check your configuration'); }); - // eslint-disable-next-line max-len - it('should throw an exception when the parameters are inconsistent: unsupported region for OAuth2 authentication', () => { - // Given - const params: SinchClientParameters = { - projectId: 'PROJECT_ID', - keyId: 'KEY_ID', - keySecret: 'KEY_SECRET', - }; - const region = SmsRegion.CANADA; - - // When - const buildApiClientOptionsFunction = () => buildFlexibleOAuth2OrApiTokenApiClientOptions(params, region, 'foo'); - - // Then - expect(buildApiClientOptionsFunction).toThrow('Invalid parameters for the foo API: check your configuration'); - }); - - // eslint-disable-next-line max-len - it('should throw an exception when the parameters are inconsistent: missing parameters when forcing API Token authentication', () => { - // Given - const params: SinchClientParameters = { - projectId: 'PROJECT_ID', - keyId: 'KEY_ID', - keySecret: 'KEY_SECRET', - forceServicePlanIdUsageForSmsApi: true, - }; - const region = SmsRegion.EUROPE; - - // When - const buildApiClientOptionsFunction = () => buildFlexibleOAuth2OrApiTokenApiClientOptions(params, region, 'foo'); - - // Then - expect(buildApiClientOptionsFunction).toThrow('Invalid parameters for the foo API: check your configuration'); - }); }); }); diff --git a/packages/sdk-client/tests/client/api-client-pagination-helpers.test.ts b/packages/sdk-client/tests/client/api-client-pagination-helpers.test.ts index ec436e82..3be13fce 100644 --- a/packages/sdk-client/tests/client/api-client-pagination-helpers.test.ts +++ b/packages/sdk-client/tests/client/api-client-pagination-helpers.test.ts @@ -21,6 +21,10 @@ describe('API Client Pagination Helper', () => { pagination: PaginationEnum.TOKEN, ...paginationTokenProperties, }; + const paginationContextToken2: PaginationContext = { + pagination: PaginationEnum.TOKEN2, + ...paginationTokenProperties, + }; const paginationContextPage: PaginationContext = { pagination: PaginationEnum.PAGE, ...paginationTokenProperties, @@ -69,6 +73,38 @@ describe('API Client Pagination Helper', () => { expect(hasMoreElements).toBeFalsy(); }); + it('should return "true" when the PaginationContext is "TOKEN2" and there are more elements', async () => { + // Given + const response = { + elements: ['H', 'He'], + next_page_token: 'nextPageToken', + totalSize: 10, + }; + const paginationContext = { ...paginationContextToken2 }; + + // When + const hasMoreElements = hasMore(response, paginationContext); + + // Then + expect(hasMoreElements).toBeTruthy(); + }); + + it('should return "false" when the PaginationContext is "TOKEN2" and there are no more elements', async () => { + // Given + const response = { + elements: ['H', 'He'], + next_page_token: '', + totalSize: 2, + }; + const paginationContext = { ...paginationContextToken2 }; + + // When + const hasMoreElements = hasMore(response, paginationContext); + + // Then + expect(hasMoreElements).toBeFalsy(); + }); + it('should return "true" when the PaginationContext is "PAGE" and there are more elements', async () => { // Given const response = { @@ -193,6 +229,22 @@ describe('API Client Pagination Helper', () => { expect(nextPage).toBe('nextPageToken'); }); + it('should return the next page token when the PaginationContext is "TOKEN2"', () => { + // Given + const response = { + elements: ['H', 'He'], + next_page_token: 'nextPageToken', + totalSize: 10, + }; + const paginationContext = { ...paginationContextToken2 }; + + // When + const nextPage = calculateNextPage(response, paginationContext); + + // Then + expect(nextPage).toBe('nextPageToken'); + }); + it('should return the next page value when the PaginationContext is "PAGE"', () => { // Given const response = { diff --git a/packages/sdk-client/tests/plugins/timezone/timezone.response.test.ts b/packages/sdk-client/tests/plugins/timezone/timezone.response.test.ts deleted file mode 100644 index 661ed0e9..00000000 --- a/packages/sdk-client/tests/plugins/timezone/timezone.response.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { TimezoneResponse } from '../../../src'; -import { ResponsePluginContext } from '../../../src/plugins/core/response-plugin'; -import { Headers } from 'node-fetch'; - -describe('Timezone response plugin', () => { - - let context: ResponsePluginContext; - const TIMESTAMP_WITH_MISSING_TIMEZONE = '2024-01-09T15:50:24.000'; - const TIMESTAMP_WITH_TIMEZONE = '2024-01-09T15:50:24.000Z'; - const TIMESTAMP_WITH_TIMEZONE_HOURS = '2024-01-09T15:50:24.000+00'; - const TIMESTAMP_WITH_TIMEZONE_HOURS_MINUTES = '2024-01-09T15:50:24.000+00:00'; - - beforeEach(() => { - context = { - operationId: 'GetCallResult', - apiName: '', - url: '', - requestOptions: { - headers: new Headers(), - hostname: '', - }, - }; - }); - - it('should update the timestamp if the timezone is missing', async () => { - const apiResponse = { - timestamp: TIMESTAMP_WITH_MISSING_TIMEZONE, - }; - const plugin = new TimezoneResponse(); - const runner = plugin.load(context); - const result = await runner.transform(apiResponse); - - expect(result.timestamp).toBe(TIMESTAMP_WITH_TIMEZONE); - }); - - it('should NOT update the timestamp if the timezone is already there with "Z" format', async () => { - const apiResponse = { - timestamp: TIMESTAMP_WITH_TIMEZONE, - }; - const plugin = new TimezoneResponse(); - const runner = plugin.load(context); - const result = await runner.transform(apiResponse); - - expect(result.timestamp).toBe(TIMESTAMP_WITH_TIMEZONE); - }); - - it('should update the timestamp if the timezone is already there but with "+XX format"', async () => { - const apiResponse = { - timestamp: TIMESTAMP_WITH_TIMEZONE_HOURS, - }; - const plugin = new TimezoneResponse(); - const runner = plugin.load(context); - const result = await runner.transform(apiResponse); - - expect(result.timestamp).toBe(TIMESTAMP_WITH_TIMEZONE_HOURS_MINUTES); - }); - - it('should NOT update the timestamp if the timezone is already there with "+XX:XX format"', async () => { - const apiResponse = { - timestamp: TIMESTAMP_WITH_TIMEZONE_HOURS_MINUTES, - }; - const plugin = new TimezoneResponse(); - const runner = plugin.load(context); - const result = await runner.transform(apiResponse); - - expect(result.timestamp).toBe(TIMESTAMP_WITH_TIMEZONE_HOURS_MINUTES); - }); - - it('should NOT update the timestamp if the operationId if not listed', async () => { - context.operationId = 'notListedAsBuggy'; - const apiResponse = { - timestamp: TIMESTAMP_WITH_MISSING_TIMEZONE, - }; - const plugin = new TimezoneResponse(); - const runner = plugin.load(context); - const result = await runner.transform(apiResponse); - - expect(result.timestamp).toBe(TIMESTAMP_WITH_MISSING_TIMEZONE); - }); -}); diff --git a/packages/sdk-client/tests/utils/date.test.ts b/packages/sdk-client/tests/utils/date.test.ts new file mode 100644 index 00000000..a3e2440c --- /dev/null +++ b/packages/sdk-client/tests/utils/date.test.ts @@ -0,0 +1,103 @@ +import { + DateFormat, + formatDate, + formatCreateTimeFilter, + formatCreateTimeRangeFilter, +} from '../../src'; + +it('should format a date with its required chrono unit', () => { + const date = new Date('2024-05-01T13:20:50Z'); + let formattedDate = formatDate(date, 'year'); + expect(formattedDate).toBe('2024'); + + formattedDate = formatDate(date, 'month'); + expect(formattedDate).toBe('2024-05'); + + formattedDate = formatDate(date, 'day'); + expect(formattedDate).toBe('2024-05-01'); + + formattedDate = formatDate(date, 'hour'); + expect(formattedDate).toBe('2024-05-01T13:00:00Z'); + + formattedDate = formatDate(date, 'minute'); + expect(formattedDate).toBe('2024-05-01T13:20:00Z'); + + formattedDate = formatDate(date, 'second'); + expect(formattedDate).toBe('2024-05-01T13:20:50Z'); +}); + +it('should format a dateTime filter parameter', () => { + const dateUndefined = undefined; + let formattedDateFilter = formatCreateTimeFilter(dateUndefined); + expect(formattedDateFilter).toBeUndefined(); + + const dateString = '2024-05-01'; + formattedDateFilter = formatCreateTimeFilter(dateString); + expect(formattedDateFilter).toBe('2024-05-01'); + + const dateWithSecondsString ='2024-05-01T13:00:00Z'; + formattedDateFilter = formatCreateTimeFilter(dateWithSecondsString); + expect(formattedDateFilter).toBe('2024-05-01'); + + const dateWithSeconds = new Date('2024-05-01T13:00:00Z'); + formattedDateFilter = formatCreateTimeFilter(dateWithSeconds); + expect(formattedDateFilter).toBe('2024-05-01'); +}); + +it('should format a datetime range filter parameter', () => { + const dateTimeRangeUndefined = undefined; + let formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeUndefined); + expect(formattedDateTimeRangeFilter).toBeUndefined(); + + const dateTimeRangeString = '2024-05-01'; + formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeString); + expect(formattedDateTimeRangeFilter).toBe('2024-05-01'); + + const dateTimeRangeNoUnit: DateFormat = { + date: new Date('2024-05-01T13:15:30Z'), + }; + formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeNoUnit); + expect(formattedDateTimeRangeFilter).toBe('2024-05-01T13:15:30Z'); + + const dateTimeRangeWithYear: DateFormat = { + date: new Date('2024-05-01T13:15:30Z'), + unit: 'year', + }; + formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeWithYear); + expect(formattedDateTimeRangeFilter).toBe('2024'); + + const dateTimeRangeWithMonth: DateFormat = { + date: new Date('2024-05-01T13:15:30Z'), + unit: 'month', + }; + formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeWithMonth); + expect(formattedDateTimeRangeFilter).toBe('2024-05'); + + const dateTimeRangeWithDay: DateFormat = { + date: new Date('2024-05-01T13:15:30Z'), + unit: 'day', + }; + formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeWithDay); + expect(formattedDateTimeRangeFilter).toBe('2024-05-01'); + + const dateTimeRangeWithHours: DateFormat = { + date: new Date('2024-05-01T13:15:30Z'), + unit: 'hour', + }; + formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeWithHours); + expect(formattedDateTimeRangeFilter).toBe('2024-05-01T13:00:00Z'); + + const dateTimeRangeWithMinutes: DateFormat = { + date: new Date('2024-05-01T13:15:30Z'), + unit: 'minute', + }; + formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeWithMinutes); + expect(formattedDateTimeRangeFilter).toBe('2024-05-01T13:15:00Z'); + + const dateTimeRangeWithSeconds: DateFormat = { + date: new Date('2024-05-01T13:15:30Z'), + unit: 'second', + }; + formattedDateTimeRangeFilter = formatCreateTimeRangeFilter(dateTimeRangeWithSeconds); + expect(formattedDateTimeRangeFilter).toBe('2024-05-01T13:15:30Z'); +}); diff --git a/packages/sdk-core/CHANGELOG.md b/packages/sdk-core/CHANGELOG.md index c1729eb8..1abbd1cd 100644 --- a/packages/sdk-core/CHANGELOG.md +++ b/packages/sdk-core/CHANGELOG.md @@ -1,3 +1,12 @@ +## Version 1.2.0 +- Update dependency `@sinch/numbers` to version `1.2.0` +- Update dependency `@sinch/sms` to version `1.2.0` +- Update dependency `@sinch/verification` to version `1.2.0` +- Update dependency `@sinch/voice` to version `1.2.0` +- Update dependency `@sinch/conversation` to version `1.2.0` +- Update dependency `@sinch/fax` to version `1.2.0` +- Update dependency `@sinch/elastic-sip-trunking` to version `1.2.0` + ## Version 1.1.0 - Update dependency `@sinch/numbers` to version `1.1.0` - Update dependency `@sinch/sms` to version `1.1.0` diff --git a/packages/sdk-core/package.json b/packages/sdk-core/package.json index 6df0f589..8d9d5c40 100644 --- a/packages/sdk-core/package.json +++ b/packages/sdk-core/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/sdk-core", - "version": "1.1.0", + "version": "1.2.0", "description": "Node.js client for the Sinch API platform", "homepage": "", "repository": { @@ -29,16 +29,17 @@ "compile": "tsc --build --verbose" }, "dependencies": { - "@sinch/conversation": "^1.1.0", - "@sinch/elastic-sip-trunking": "^1.1.0", - "@sinch/fax": "^1.1.0", - "@sinch/numbers": "^1.1.0", - "@sinch/sms": "^1.1.0", - "@sinch/verification": "^1.1.0", - "@sinch/voice": "^1.1.0" + "@sinch/conversation": "^1.2.0", + "@sinch/elastic-sip-trunking": "^1.2.0", + "@sinch/fax": "^1.2.0", + "@sinch/numbers": "^1.2.0", + "@sinch/sms": "^1.2.0", + "@sinch/verification": "^1.2.0", + "@sinch/voice": "^1.2.0" }, "devDependencies": {}, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/sms/CHANGELOG.md b/packages/sms/CHANGELOG.md index cd2b923c..2ee38a3b 100644 --- a/packages/sms/CHANGELOG.md +++ b/packages/sms/CHANGELOG.md @@ -1,3 +1,22 @@ +## Version 1.2.0 +- [Tech] Update dependency `@sinch/sdk-client` to `1.2.0`. +- [Feature] Align "Dry Run" response interface with OAS update: `message_part?: string` becomes `number_of_parts?: number`. +- [Feature] In the interface `MessageDeliveryStatus`, the property `recipients` becomes optional. +- [Feature] In the interface `GetDeliveryReportByBatchIdRequestData`, the property `code` can also be a `number` or a `number[]` on top of a `string`. +- [Feature] In the interface `ListInboundMessagesRequestData`, the property `to` can also be a `string[]` on top of a `string`. +- [Bugfix] Remove default values set by the SDK when forging the API request. +- [Bugfix] In the interface `UpdateGroupRequest`, the property `name` can also be set to null to remove an existing name set. +- [Deprecation Notice] All variations of a group response (`GroupResponse`, `CreateGroupResponse`, `ReplaceGroupResponse` and `UpdateGroupResponse`) are deprecated and replaced by the unique interface `Group`. +- [Deprecation Notice] In the interface `GetDeliveryReportByPhoneNumberRequestData`, the request parameter `recipient_msisdn` is deprecated and should be replaced by `phone_number`. +- [Deprecation Notice] The "parameters" related interfaces have been updated and the interface `ParameterGroup`uses an index signature to allow for arbitrary keys instead of extending a `Record`: + +| Deprecated | New | +|--------------------------|-----------------| +| ParameterObj | ParameterGroup | +| ParameterObjParameterKey | ParameterValues | + +- [E2E] Add Cucumber steps implementation. + ## Version 1.1.0 - [Tech] Update dependency `@sinch/sdk-client` to `1.1.0` diff --git a/packages/sms/cucumber.js b/packages/sms/cucumber.js new file mode 100644 index 00000000..691a9809 --- /dev/null +++ b/packages/sms/cucumber.js @@ -0,0 +1,8 @@ +module.exports = { + default: [ + 'tests/e2e/features/**/*.feature', + '--require-module ts-node/register', + '--require tests/rest/v1/**/*.steps.ts', + `--format-options '{"snippetInterface": "synchronous"}'`, + ].join(' '), +}; diff --git a/packages/sms/package.json b/packages/sms/package.json index 3483fcd0..4c5552f3 100644 --- a/packages/sms/package.json +++ b/packages/sms/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/sms", - "version": "1.1.0", + "version": "1.2.0", "description": "Sinch SMS API", "homepage": "", "repository": { @@ -25,13 +25,15 @@ "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", - "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo" + "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo", + "test:e2e": "cucumber-js" }, "dependencies": { - "@sinch/sdk-client": "^1.1.0" + "@sinch/sdk-client": "^1.2.0" }, "devDependencies": {}, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/sms/src/models/v1/api-update-mms-mt-message/api-update-mms-mt-message.ts b/packages/sms/src/models/v1/api-update-mms-mt-message/api-update-mms-mt-message.ts index 9057b61a..79574eeb 100644 --- a/packages/sms/src/models/v1/api-update-mms-mt-message/api-update-mms-mt-message.ts +++ b/packages/sms/src/models/v1/api-update-mms-mt-message/api-update-mms-mt-message.ts @@ -1,5 +1,5 @@ import { MediaBody } from '../media-body'; -import { ParameterObj } from '../parameter-obj'; +import { ParameterGroup } from '../parameter-group'; import { DeliveryReportEnum } from '../enums'; export interface ApiUpdateMmsMtMessage { @@ -22,8 +22,8 @@ export interface ApiUpdateMmsMtMessage { callback_url?: string; /** @see MediaBody */ body?: MediaBody; - /** @see ParameterObj */ - parameters?: ParameterObj; + /** @see ParameterGroup */ + parameters?: ParameterGroup; /** Whether or not you want the media included in your message to be checked against [Sinch MMS channel best practices](/docs/mms/bestpractices/). If set to true, your message will be rejected if it doesn\'t conform to the listed recommendations, otherwise no validation will be performed. */ strict_validation?: boolean; } diff --git a/packages/sms/src/models/v1/api-update-text-mt-message/api-update-text-mt-message.ts b/packages/sms/src/models/v1/api-update-text-mt-message/api-update-text-mt-message.ts index 64ae8181..1f52fee8 100644 --- a/packages/sms/src/models/v1/api-update-text-mt-message/api-update-text-mt-message.ts +++ b/packages/sms/src/models/v1/api-update-text-mt-message/api-update-text-mt-message.ts @@ -1,4 +1,4 @@ -import { ParameterObj } from '../parameter-obj'; +import { ParameterGroup } from '../parameter-group'; import { DeliveryReportEnum } from '../enums'; export interface ApiUpdateTextMtMessage { @@ -19,8 +19,8 @@ export interface ApiUpdateTextMtMessage { expire_at?: Date; /** Override the default callback URL for this batch. Constraints: Must be valid URL. */ callback_url?: string; - /** @see ParameterObj */ - parameters?: ParameterObj; + /** @see ParameterGroup */ + parameters?: ParameterGroup; /** The message content */ body?: string; } diff --git a/packages/sms/src/models/v1/create-group-response/index.ts b/packages/sms/src/models/v1/create-group-response/index.ts deleted file mode 100644 index 6757713b..00000000 --- a/packages/sms/src/models/v1/create-group-response/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type { CreateGroupResponse } from './create-group-response'; -export type { CreateGroupResponse as GroupResponse } from './create-group-response'; -export type { CreateGroupResponse as ReplaceGroupResponse } from './create-group-response'; -export type { CreateGroupResponse as UpdateGroupResponse } from './create-group-response'; diff --git a/packages/sms/src/models/v1/dry-run-response-per-recipient-inner/dry-run-response-per-recipient-inner.ts b/packages/sms/src/models/v1/dry-run-response-per-recipient-inner/dry-run-response-per-recipient-inner.ts index 2094dba5..ea6b9e51 100644 --- a/packages/sms/src/models/v1/dry-run-response-per-recipient-inner/dry-run-response-per-recipient-inner.ts +++ b/packages/sms/src/models/v1/dry-run-response-per-recipient-inner/dry-run-response-per-recipient-inner.ts @@ -1,7 +1,7 @@ export interface DryRunResponsePerRecipientInner { recipient?: string; - message_part?: string; + number_of_parts?: number; body?: string; encoding?: string; } diff --git a/packages/sms/src/models/v1/create-group-response/create-group-response.ts b/packages/sms/src/models/v1/group/group.ts similarity index 67% rename from packages/sms/src/models/v1/create-group-response/create-group-response.ts rename to packages/sms/src/models/v1/group/group.ts index db546bc0..2a642e57 100644 --- a/packages/sms/src/models/v1/create-group-response/create-group-response.ts +++ b/packages/sms/src/models/v1/group/group.ts @@ -1,6 +1,18 @@ import { GroupAutoUpdate } from '../group-auto-update'; -export interface CreateGroupResponse { +/** @deprecated Use Group instead */ +export type CreateGroupResponse = Group; + +/** @deprecated Use Group instead */ +export type GroupResponse = Group; + +/** @deprecated Use Group instead */ +export type ReplaceGroupResponse = Group; + +/** @deprecated Use Group instead */ +export type UpdateGroupResponse = Group; + +export interface Group { /** The ID used to reference this group. */ id?: string; diff --git a/packages/sms/src/models/v1/group/index.ts b/packages/sms/src/models/v1/group/index.ts new file mode 100644 index 00000000..ba87b37c --- /dev/null +++ b/packages/sms/src/models/v1/group/index.ts @@ -0,0 +1 @@ +export type { Group, GroupResponse, CreateGroupResponse, ReplaceGroupResponse, UpdateGroupResponse } from './group'; diff --git a/packages/sms/src/models/v1/index.ts b/packages/sms/src/models/v1/index.ts index b8c19abc..810d1a46 100644 --- a/packages/sms/src/models/v1/index.ts +++ b/packages/sms/src/models/v1/index.ts @@ -9,7 +9,7 @@ export * from './api-update-mt-message'; export * from './api-update-text-mt-message'; export * from './binary-request'; export * from './binary-response'; -export * from './create-group-response'; +export * from './group'; export * from './delivery-report'; export * from './delivery-report-list'; export * from './dry-run-request'; @@ -28,8 +28,8 @@ export * from './media-body'; export * from './media-request'; export * from './media-response'; export * from './message-delivery-status'; -export * from './parameter-obj'; -export * from './parameter-obj-parameter-key'; +export * from './parameter-group'; +export * from './parameter-values'; export * from './recipient-delivery-report'; export * from './replace-group-request'; export * from './send-sms-response'; diff --git a/packages/sms/src/models/v1/list-groups-response/list-groups-response.ts b/packages/sms/src/models/v1/list-groups-response/list-groups-response.ts index 17bb4adc..65ae69df 100644 --- a/packages/sms/src/models/v1/list-groups-response/list-groups-response.ts +++ b/packages/sms/src/models/v1/list-groups-response/list-groups-response.ts @@ -1,4 +1,4 @@ -import { CreateGroupResponse } from '../create-group-response'; +import { Group } from '../group'; export interface ListGroupsResponse { @@ -9,7 +9,7 @@ export interface ListGroupsResponse { /** The total number of groups. */ count?: number; /** List of GroupObjects */ - groups?: CreateGroupResponse[]; + groups?: Group[]; } diff --git a/packages/sms/src/models/v1/media-request/media-request.ts b/packages/sms/src/models/v1/media-request/media-request.ts index 2c39855e..0226502a 100644 --- a/packages/sms/src/models/v1/media-request/media-request.ts +++ b/packages/sms/src/models/v1/media-request/media-request.ts @@ -1,5 +1,5 @@ import { MediaBody } from '../media-body'; -import { ParameterObj } from '../parameter-obj'; +import { ParameterGroup } from '../parameter-group'; import { DeliveryReportEnum } from '../enums'; /** @@ -12,8 +12,8 @@ export interface MediaRequest { from?: string; /** @see MediaBody */ body: MediaBody; - /** @see ParameterObj */ - parameters?: ParameterObj; + /** @see ParameterGroup */ + parameters?: ParameterGroup; /** MMS */ type?: 'mt_media'; /** Request delivery report callback. Note that delivery reports can be fetched from the API regardless of this setting. */ diff --git a/packages/sms/src/models/v1/media-response/media-response.ts b/packages/sms/src/models/v1/media-response/media-response.ts index d355c255..964cddfa 100644 --- a/packages/sms/src/models/v1/media-response/media-response.ts +++ b/packages/sms/src/models/v1/media-response/media-response.ts @@ -1,5 +1,5 @@ import { MediaBody } from '../media-body'; -import { ParameterObj } from '../parameter-obj'; +import { ParameterGroup } from '../parameter-group'; import { DeliveryReportEnum } from '../enums'; export interface MediaResponse { @@ -13,8 +13,8 @@ export interface MediaResponse { canceled?: boolean; /** @see MediaBody */ body?: MediaBody; - /** @see ParameterObj */ - parameters?: ParameterObj; + /** @see ParameterGroup */ + parameters?: ParameterGroup; /** Media message */ type?: 'mt_media'; /** Timestamp for when batch was created. YYYY-MM-DDThh:mm:ss.SSSZ format */ diff --git a/packages/sms/src/models/v1/message-delivery-status/message-delivery-status.ts b/packages/sms/src/models/v1/message-delivery-status/message-delivery-status.ts index d33094ff..417a1c74 100644 --- a/packages/sms/src/models/v1/message-delivery-status/message-delivery-status.ts +++ b/packages/sms/src/models/v1/message-delivery-status/message-delivery-status.ts @@ -10,7 +10,7 @@ export interface MessageDeliveryStatus { /** The number of messages that currently has this code. */ count: number; /** Only for `full` report. A list of the phone number recipients which messages has this status code. */ - recipients: string[]; + recipients?: string[]; /** The simplified status as described in _Delivery Report Statuses_. */ status: DeliveryReportStatusEnum; } diff --git a/packages/sms/src/models/v1/parameter-group/index.ts b/packages/sms/src/models/v1/parameter-group/index.ts new file mode 100644 index 00000000..39b8f6c3 --- /dev/null +++ b/packages/sms/src/models/v1/parameter-group/index.ts @@ -0,0 +1 @@ +export type { ParameterObj, ParameterGroup } from './parameter-group'; diff --git a/packages/sms/src/models/v1/parameter-obj/parameter-obj.ts b/packages/sms/src/models/v1/parameter-group/parameter-group.ts similarity index 63% rename from packages/sms/src/models/v1/parameter-obj/parameter-obj.ts rename to packages/sms/src/models/v1/parameter-group/parameter-group.ts index 15beaa86..55c2564d 100644 --- a/packages/sms/src/models/v1/parameter-obj/parameter-obj.ts +++ b/packages/sms/src/models/v1/parameter-group/parameter-group.ts @@ -1,12 +1,15 @@ -import { ParameterObjParameterKey } from '../parameter-obj-parameter-key'; +import { ParameterObjParameterKey, ParameterValues } from '../parameter-values'; /** * Contains the parameters that will be used for customizing the message for each recipient. [Click here to learn more about parameterization](/docs/sms/resources/message-info/message-parameterization). */ +export interface ParameterGroup { + [parameterKey: string]: ParameterValues; +} + +/** @deprecated Use ParameterGroup instead */ export interface ParameterObj extends Record { /** @see ParameterObjParameterKey */ '{parameter_key}'?: ParameterObjParameterKey; } - - diff --git a/packages/sms/src/models/v1/parameter-obj-parameter-key/index.ts b/packages/sms/src/models/v1/parameter-obj-parameter-key/index.ts deleted file mode 100644 index 2b230f38..00000000 --- a/packages/sms/src/models/v1/parameter-obj-parameter-key/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { ParameterObjParameterKey } from './parameter-obj-parameter-key'; diff --git a/packages/sms/src/models/v1/parameter-obj/index.ts b/packages/sms/src/models/v1/parameter-obj/index.ts deleted file mode 100644 index 39022580..00000000 --- a/packages/sms/src/models/v1/parameter-obj/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { ParameterObj } from './parameter-obj'; diff --git a/packages/sms/src/models/v1/parameter-values/index.ts b/packages/sms/src/models/v1/parameter-values/index.ts new file mode 100644 index 00000000..c4d2048b --- /dev/null +++ b/packages/sms/src/models/v1/parameter-values/index.ts @@ -0,0 +1 @@ +export type { ParameterObjParameterKey, ParameterValues } from './parameter-values'; diff --git a/packages/sms/src/models/v1/parameter-obj-parameter-key/parameter-obj-parameter-key.ts b/packages/sms/src/models/v1/parameter-values/parameter-values.ts similarity index 54% rename from packages/sms/src/models/v1/parameter-obj-parameter-key/parameter-obj-parameter-key.ts rename to packages/sms/src/models/v1/parameter-values/parameter-values.ts index 2e33d1d2..e8938bea 100644 --- a/packages/sms/src/models/v1/parameter-obj-parameter-key/parameter-obj-parameter-key.ts +++ b/packages/sms/src/models/v1/parameter-values/parameter-values.ts @@ -1,6 +1,14 @@ /** * The name of the parameter that will be replaced in the message body. Letters A-Z and a-z, digits 0-9 and .-_ allowed. */ +export interface ParameterValues { + /** The key is the recipient that should have the `parameter_key` replaced with the value */ + [msisdn: string]: string | undefined; + /** The fall-back value for omitted recipient phone numbers MSISDNs. */ + default?: string; +} + +/** @deprecated Use ParameterValues instead */ export interface ParameterObjParameterKey { /** The key is the recipient that should have the `parameter_key` replaced with the value */ diff --git a/packages/sms/src/models/v1/requests/delivery-reports/delivery-reports-request-data.ts b/packages/sms/src/models/v1/requests/delivery-reports/delivery-reports-request-data.ts index c658abef..428e2c2f 100644 --- a/packages/sms/src/models/v1/requests/delivery-reports/delivery-reports-request-data.ts +++ b/packages/sms/src/models/v1/requests/delivery-reports/delivery-reports-request-data.ts @@ -8,14 +8,30 @@ export interface GetDeliveryReportByBatchIdRequestData { /** Comma separated list of delivery_report_statuses to include */ 'status'?: DeliveryReportStatusEnum[]; /** Comma separated list of delivery_receipt_error_codes to include */ - 'code'?: string; + 'code'?: string | number | number[]; } -export interface GetDeliveryReportByPhoneNumberRequestData { + +export type GetDeliveryReportByPhoneNumberRequestData = + GetDeliveryReportByPhoneNumberRequestDataBC | GetDeliveryReportByPhoneNumberRequestDataDeprecated; + +export interface GetDeliveryReportByPhoneNumberRequestDataBC { /** The batch ID you received from sending a message. */ 'batch_id': string; /** Phone number for which you to want to search. */ - 'recipient_msisdn': string; + 'phone_number': string; + /** @deprecated Use phone_number instead */ + recipient_msisdn?: never; } + +export interface GetDeliveryReportByPhoneNumberRequestDataDeprecated { + /** The batch ID you received from sending a message. */ + 'batch_id': string; + /** @deprecated: use phone_number instead */ + recipient_msisdn: string; + /** Phone number for which you to want to search. */ + phone_number?: never; +} + export interface ListDeliveryReportsRequestData { /** The page number starting from 0. */ 'page'?: number; diff --git a/packages/sms/src/models/v1/requests/inbounds/inbounds-request-data.ts b/packages/sms/src/models/v1/requests/inbounds/inbounds-request-data.ts index 5d83edc2..591b5576 100644 --- a/packages/sms/src/models/v1/requests/inbounds/inbounds-request-data.ts +++ b/packages/sms/src/models/v1/requests/inbounds/inbounds-request-data.ts @@ -4,7 +4,7 @@ export interface ListInboundMessagesRequestData { /** Determines the size of a page */ 'page_size'?: number; /** Only list messages sent to this destination. Multiple phone numbers formatted as either E.164 or short codes can be comma separated. */ - 'to'?: string; + 'to'?: string | string[]; /** Only list messages received at or after this date/time. Formatted as [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601): `YYYY-MM-DDThh:mm:ss.SSSZ`. Default: Now-24 */ 'start_date'?: Date; /** Only list messages received before this date/time. Formatted as [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601): `YYYY-MM-DDThh:mm:ss.SSSZ`. */ diff --git a/packages/sms/src/models/v1/text-request/text-request.ts b/packages/sms/src/models/v1/text-request/text-request.ts index 6bd814ec..8fdb6fa3 100644 --- a/packages/sms/src/models/v1/text-request/text-request.ts +++ b/packages/sms/src/models/v1/text-request/text-request.ts @@ -1,4 +1,4 @@ -import { ParameterObj } from '../parameter-obj'; +import { ParameterGroup } from '../parameter-group'; import { DeliveryReportEnum } from '../enums'; export interface TextRequest { @@ -6,8 +6,8 @@ export interface TextRequest { to: string[]; /** Sender number. Must be valid phone number, short code or alphanumeric. Required if Automatic Default Originator not configured. */ from?: string; - /** @see ParameterObj */ - parameters?: ParameterObj; + /** @see ParameterGroup */ + parameters?: ParameterGroup; /** The message content */ body: string; /** Regular SMS */ diff --git a/packages/sms/src/models/v1/text-response/text-response.ts b/packages/sms/src/models/v1/text-response/text-response.ts index 292c90f6..fc27053a 100644 --- a/packages/sms/src/models/v1/text-response/text-response.ts +++ b/packages/sms/src/models/v1/text-response/text-response.ts @@ -1,4 +1,4 @@ -import { ParameterObj } from '../parameter-obj'; +import { ParameterGroup } from '../parameter-group'; import { DeliveryReportEnum } from '../enums'; export interface TextResponse { @@ -10,8 +10,8 @@ export interface TextResponse { from?: string; /** Indicates if the batch has been canceled or not. */ canceled?: boolean; - /** @see ParameterObj */ - parameters?: ParameterObj; + /** @see ParameterGroup */ + parameters?: ParameterGroup; /** The message content */ body?: string; /** Regular SMS */ diff --git a/packages/sms/src/models/v1/update-group-request/update-group-request.ts b/packages/sms/src/models/v1/update-group-request/update-group-request.ts index e98f1eff..98ef9e2a 100644 --- a/packages/sms/src/models/v1/update-group-request/update-group-request.ts +++ b/packages/sms/src/models/v1/update-group-request/update-group-request.ts @@ -3,7 +3,7 @@ import { UpdateGroupRequestAutoUpdate } from '../update-group-request-auto-updat export interface UpdateGroupRequest { /** The name of the group. Omitting `name` from the JSON body will leave the name unchanged. To remove an existing name set, name explicitly to the JSON value `null`. */ - name?: string; + name?: string | null; /** Add a list of phone numbers (MSISDNs) to this group. The phone numbers are a strings within an array and must be in E.164 format. */ add?: string[]; /** Remove a list of phone numbers (MSISDNs) to this group.The phone numbers are a strings within an array and must be in E.164 format. */ diff --git a/packages/sms/src/rest/v1/batches/batches-api.ts b/packages/sms/src/rest/v1/batches/batches-api.ts index 6e21f723..efc72af1 100644 --- a/packages/sms/src/rest/v1/batches/batches-api.ts +++ b/packages/sms/src/rest/v1/batches/batches-api.ts @@ -104,7 +104,6 @@ export class BatchesApi extends SmsDomainApi { */ public async dryRun(data: DryRunRequestData): Promise { this.client = this.getSinchClient(); - data['number_of_recipients'] = data['number_of_recipients'] !== undefined ? data['number_of_recipients'] : 100; const getParams = this.client.extractQueryParams(data, [ 'per_recipient', 'number_of_recipients']); @@ -164,7 +163,6 @@ export class BatchesApi extends SmsDomainApi { */ public list(data: ListBatchesRequestData): ApiListPromise { this.client = this.getSinchClient(); - data['page_size'] = data['page_size'] !== undefined ? data['page_size'] : 30; const getParams = this.client.extractQueryParams( data, ['page', 'page_size', 'from', 'start_date', 'end_date', 'client_reference'], diff --git a/packages/sms/src/rest/v1/delivery-reports/delivery-reports-api.ts b/packages/sms/src/rest/v1/delivery-reports/delivery-reports-api.ts index 948d1762..40fbef95 100644 --- a/packages/sms/src/rest/v1/delivery-reports/delivery-reports-api.ts +++ b/packages/sms/src/rest/v1/delivery-reports/delivery-reports-api.ts @@ -34,7 +34,6 @@ export class DeliveryReportsApi extends SmsDomainApi { */ public async get(data: GetDeliveryReportByBatchIdRequestData): Promise { this.client = this.getSinchClient(); - data['type'] = data['type'] !== undefined ? data['type'] : 'summary'; const getParams = this.client.extractQueryParams( data, ['type', 'status', 'code'], @@ -72,8 +71,17 @@ export class DeliveryReportsApi extends SmsDomainApi { 'Accept': 'application/json', }; + // TODO: Remove in v2.0 + let phoneNumber; + if (data['phone_number']) { + phoneNumber = data['phone_number']; + } + else if (data['recipient_msisdn']) { + phoneNumber = data['recipient_msisdn']; + } + const body: RequestBody = ''; - const basePathUrl = `${this.client.apiClientOptions.hostname}/xms/v1/${this.client.apiClientOptions.projectId}/batches/${data['batch_id']}/delivery_report/${data['recipient_msisdn']}`; + const basePathUrl = `${this.client.apiClientOptions.hostname}/xms/v1/${this.client.apiClientOptions.projectId}/batches/${data['batch_id']}/delivery_report/${phoneNumber}`; const requestOptions = await this.client.prepareOptions(basePathUrl, 'GET', getParams, headers, body || undefined); @@ -95,8 +103,6 @@ export class DeliveryReportsApi extends SmsDomainApi { */ public list(data: ListDeliveryReportsRequestData): ApiListPromise { this.client = this.getSinchClient(); - data['page'] = data['page'] !== undefined ? data['page'] : 0; - data['page_size'] = data['page_size'] !== undefined ? data['page_size'] : 30; const getParams = this.client.extractQueryParams( data, ['page', 'page_size', 'start_date', 'end_date', 'status', 'code', 'client_reference'], diff --git a/packages/sms/src/rest/v1/groups/groups-api.jest.fixture.ts b/packages/sms/src/rest/v1/groups/groups-api.jest.fixture.ts index 6c1884ff..49c678c4 100644 --- a/packages/sms/src/rest/v1/groups/groups-api.jest.fixture.ts +++ b/packages/sms/src/rest/v1/groups/groups-api.jest.fixture.ts @@ -1,7 +1,6 @@ import { GroupsApi } from './groups-api'; import { - CreateGroupResponse, - GroupResponse, + Group, CreateGroupRequestData, DeleteGroupRequestData, ListMembersRequestData, @@ -17,7 +16,7 @@ export class GroupsApiFixture implements Partial> { /** * Fixture associated to function createGroup */ - public create: jest.Mock, [CreateGroupRequestData]> = jest.fn(); + public create: jest.Mock, [CreateGroupRequestData]> = jest.fn(); /** * Fixture associated to function deleteGroup */ @@ -29,17 +28,17 @@ export class GroupsApiFixture implements Partial> { /** * Fixture associated to function listGroups */ - public list: jest.Mock, [ListGroupsRequestData]> = jest.fn(); + public list: jest.Mock, [ListGroupsRequestData]> = jest.fn(); /** * Fixture associated to function replaceGroup */ - public replace: jest.Mock, [ReplaceGroupRequestData]> = jest.fn(); + public replace: jest.Mock, [ReplaceGroupRequestData]> = jest.fn(); /** * Fixture associated to function retrieveGroup */ - public get: jest.Mock, [GetGroupRequestData]> = jest.fn(); + public get: jest.Mock, [GetGroupRequestData]> = jest.fn(); /** * Fixture associated to function updateGroup */ - public update: jest.Mock, [UpdateGroupRequestData]> = jest.fn(); + public update: jest.Mock, [UpdateGroupRequestData]> = jest.fn(); } diff --git a/packages/sms/src/rest/v1/groups/groups-api.ts b/packages/sms/src/rest/v1/groups/groups-api.ts index c077867c..102a4f43 100644 --- a/packages/sms/src/rest/v1/groups/groups-api.ts +++ b/packages/sms/src/rest/v1/groups/groups-api.ts @@ -1,9 +1,12 @@ import { CreateGroupRequestData, - CreateGroupResponse, DeleteGroupRequestData, GetGroupRequestData, - GroupResponse, ListGroupsRequestData, ListMembersRequestData, ReplaceGroupRequestData, - ReplaceGroupResponse, UpdateGroupRequestData, - UpdateGroupResponse, + DeleteGroupRequestData, + GetGroupRequestData, + ListGroupsRequestData, + ListMembersRequestData, + ReplaceGroupRequestData, + UpdateGroupRequestData, + Group, } from '../../../models'; import { RequestBody, @@ -32,7 +35,7 @@ export class GroupsApi extends SmsDomainApi { * A group is a set of phone numbers (MSISDNs) that can be used as a target in the `send_batch_msg` operation. An MSISDN can only occur once in a group and any attempts to add a duplicate would be ignored but not rejected. * @param { CreateGroupRequestData } data - The data to provide to the API call. */ - public async create(data: CreateGroupRequestData): Promise { + public async create(data: CreateGroupRequestData): Promise { this.client = this.getSinchClient(); const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { @@ -47,7 +50,7 @@ export class GroupsApi extends SmsDomainApi { = await this.client.prepareOptions(basePathUrl, 'POST', getParams, headers, body || undefined); const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); - return this.client.processCall({ + return this.client.processCall({ url, requestOptions, apiName: this.apiName, @@ -115,12 +118,10 @@ export class GroupsApi extends SmsDomainApi { * List Groups * With the list operation you can list all groups that you have created. This operation supports pagination. Groups are returned in reverse chronological order. * @param { ListGroupsRequestData } data - The data to provide to the API call. - * @return {ApiListPromise} + * @return {ApiListPromise} */ - public list(data: ListGroupsRequestData): ApiListPromise { + public list(data: ListGroupsRequestData): ApiListPromise { this.client = this.getSinchClient(); - data['page'] = data['page'] !== undefined ? data['page'] : 0; - data['page_size'] = data['page_size'] !== undefined ? data['page_size'] : 30; const getParams = this.client.extractQueryParams(data, ['page', 'page_size']); const headers: { [key: string]: string | undefined } = { 'Content-Type': 'application/json', @@ -141,7 +142,7 @@ export class GroupsApi extends SmsDomainApi { }; // Create the promise containing the response wrapped as a PageResult - const listPromise = buildPageResultPromise( + const listPromise = buildPageResultPromise( this.client, requestOptionsPromise, operationProperties); @@ -149,11 +150,11 @@ export class GroupsApi extends SmsDomainApi { // Add properties to the Promise to offer the possibility to use it as an iterator Object.assign( listPromise, - createIteratorMethodsForPagination( + createIteratorMethodsForPagination( this.client, requestOptionsPromise, listPromise, operationProperties), ); - return listPromise as ApiListPromise; + return listPromise as ApiListPromise; } /** @@ -161,7 +162,7 @@ export class GroupsApi extends SmsDomainApi { * The replace operation will replace all parameters, including members, of an existing group with new values. Replacing a group targeted by a batch message scheduled in the future is allowed and changes will be reflected when the batch is sent. * @param { ReplaceGroupRequestData } data - The data to provide to the API call. */ - public async replace(data: ReplaceGroupRequestData): Promise { + public async replace(data: ReplaceGroupRequestData): Promise { this.client = this.getSinchClient(); const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { @@ -176,7 +177,7 @@ export class GroupsApi extends SmsDomainApi { = await this.client.prepareOptions(basePathUrl, 'PUT', getParams, headers, body || undefined); const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); - return this.client.processCall({ + return this.client.processCall({ url, requestOptions, apiName: this.apiName, @@ -189,7 +190,7 @@ export class GroupsApi extends SmsDomainApi { * This operation retrieves a specific group with the provided group ID. * @param { GetGroupRequestData } data - The data to provide to the API call. */ - public async get(data: GetGroupRequestData): Promise { + public async get(data: GetGroupRequestData): Promise { this.client = this.getSinchClient(); const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { @@ -204,7 +205,7 @@ export class GroupsApi extends SmsDomainApi { = await this.client.prepareOptions(basePathUrl, 'GET', getParams, headers, body || undefined); const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); - return this.client.processCall({ + return this.client.processCall({ url, requestOptions, apiName: this.apiName, @@ -217,7 +218,7 @@ export class GroupsApi extends SmsDomainApi { * With the update group operation, you can add and remove members in an existing group as well as rename the group. This method encompasses a few ways to update a group: 1. By using `add` and `remove` arrays containing phone numbers, you control the group movements. Any list of valid numbers in E.164 format can be added. 2. By using the `auto_update` object, your customer can add or remove themselves from groups. 3. You can also add or remove other groups into this group with `add_from_group` and `remove_from_group`. #### Other group update info - The request will not be rejected for duplicate adds or unknown removes. - The additions will be done before the deletions. If an phone number is on both lists, it will not be apart of the resulting group. - Updating a group targeted by a batch message scheduled in the future is allowed. Changes will be reflected when the batch is sent. * @param { UpdateGroupRequestData } data - The data to provide to the API call. */ - public async update(data: UpdateGroupRequestData): Promise { + public async update(data: UpdateGroupRequestData): Promise { this.client = this.getSinchClient(); const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { @@ -232,7 +233,7 @@ export class GroupsApi extends SmsDomainApi { = await this.client.prepareOptions(basePathUrl, 'POST', getParams, headers, body || undefined); const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); - return this.client.processCall({ + return this.client.processCall({ url, requestOptions, apiName: this.apiName, diff --git a/packages/sms/src/rest/v1/sms-domain-api.ts b/packages/sms/src/rest/v1/sms-domain-api.ts index 1b372b05..eadc3e32 100644 --- a/packages/sms/src/rest/v1/sms-domain-api.ts +++ b/packages/sms/src/rest/v1/sms-domain-api.ts @@ -84,7 +84,7 @@ export class SmsDomainApi implements Api { if(!Object.values(SupportedSmsRegion).includes(region as SupportedSmsRegion)) { console.warn(`The region "${region}" is not known as a supported region for the SMS API`); } - const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(this.sinchClientParameters, region, 'SMS'); + const apiClientOptions = buildFlexibleOAuth2OrApiTokenApiClientOptions(this.sinchClientParameters); this.client = new ApiFetchClient(apiClientOptions); const useZapStack = !this.client.apiClientOptions.useServicePlanId; this.client.apiClientOptions.hostname = this.sinchClientParameters.smsHostname diff --git a/packages/sms/tests/rest/v1/batches/batches.steps.ts b/packages/sms/tests/rest/v1/batches/batches.steps.ts new file mode 100644 index 00000000..be06582d --- /dev/null +++ b/packages/sms/tests/rest/v1/batches/batches.steps.ts @@ -0,0 +1,306 @@ +import { BatchesApi, SmsService, Sms } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; +import { ParameterGroup } from '../../../../src/models'; + +let batchesApi: BatchesApi; +let sendSmsResponse: Sms.TextResponse; +let dryRunResponse: Sms.DryRunResponse; +let listResponse: PageResult; +let batchesList: Sms.SendSMSResponse[]; +let pagesIteration: number; +let batch: Sms.SendSMSResponse; +let deliveryFeedbackResponse: void; + +Given('the SMS service "Batches" is available', () => { + const smsService = new SmsService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + smsHostname: 'http://localhost:3017', + }); + batchesApi = smsService.batches; +}); + +When('I send a request to send a text message', async () => { + const sendSmsRequest: Sms.SendTextSMSRequestData = { + sendSMSRequestBody: { + body: 'SMS body message', + to: ['+12017777777'], + from: '+12015555555', + send_at: new Date('2024-06-06T09:25:00Z'), + delivery_report: 'full', + feedback_enabled: true, + }, + }; + sendSmsResponse = await batchesApi.sendTextMessage(sendSmsRequest); +}); + +Then('the response contains the text SMS details', () => { + assert.equal(sendSmsResponse.id, '01W4FFL35P4NC4K35SMSBATCH1'); + assert.deepEqual(sendSmsResponse.to, ['12017777777']); + assert.equal(sendSmsResponse.from, '12015555555'); + assert.equal(sendSmsResponse.canceled, false); + assert.equal(sendSmsResponse.body, 'SMS body message'); + assert.equal(sendSmsResponse.type, 'mt_text'); + assert.deepEqual(sendSmsResponse.created_at, new Date('2024-06-06T09:22:14.304Z')); + assert.deepEqual(sendSmsResponse.modified_at, new Date('2024-06-06T09:22:14.304Z')); + const fullDeliveryReport: Sms.DeliveryReportEnum = 'full'; + assert.equal(sendSmsResponse.delivery_report, fullDeliveryReport); + assert.deepEqual(sendSmsResponse.send_at, new Date('2024-06-06T09:25:00Z')); + assert.deepEqual(sendSmsResponse.expire_at, new Date('2024-06-09T09:25:00Z')); + assert.equal(sendSmsResponse.feedback_enabled, true); + assert.equal(sendSmsResponse.flash_message, false); +}); + +When('I send a request to send a text message with multiple parameters', async () => { + const sendSmsRequest: Sms.SendTextSMSRequestData = { + sendSMSRequestBody: { + body: 'Hello ${name}! Get 20% off with this discount code ${code}', + to: ['+12017777777', '+12018888888'], + from: '+12015555555', + parameters: { + name: { + '+12017777777': 'John', + '+12018888888': 'Paul', + default: 'there', + }, + code: { + '+12017777777': 'HALLOWEEN20 🎃', + }, + }, + delivery_report: 'full', + }, + }; + sendSmsResponse = await batchesApi.sendTextMessage(sendSmsRequest); +}); + +Then('the response contains the text SMS details with multiple parameters', () => { + assert.equal(sendSmsResponse.id, '01W4FFL35P4NC4K35SMSBATCH2'); + assert.deepEqual(sendSmsResponse.to, ['12017777777', '12018888888']); + assert.equal(sendSmsResponse.from, '12015555555'); + assert.equal(sendSmsResponse.canceled, false); + const parameters: ParameterGroup = { + name: { + default: 'there', + '+12017777777': 'John', + '+12018888888': 'Paul', + }, + code: { + '+12017777777': 'HALLOWEEN20 🎃', + }, + }; + assert.deepEqual(sendSmsResponse.parameters, parameters); + assert.equal(sendSmsResponse.body, 'Hello ${name}! Get 20% off with this discount code ${code}'); + assert.equal(sendSmsResponse.type, 'mt_text'); + assert.deepEqual(sendSmsResponse.created_at, new Date('2024-06-06T09:22:14.304Z')); + assert.deepEqual(sendSmsResponse.modified_at, new Date('2024-06-06T09:22:14.304Z')); + const fullDeliveryReport: Sms.DeliveryReportEnum = 'full'; + assert.equal(sendSmsResponse.delivery_report, fullDeliveryReport); + assert.deepEqual(sendSmsResponse.expire_at, new Date('2024-06-06T09:22:14.304Z')); + assert.equal(sendSmsResponse.flash_message, false); +}); + +When('I send a request to perform a dry run of a batch', async () => { + const sendSmsRequest: Sms.DryRunRequestData = { + dryRunRequestBody: { + from: '+12015555555', + to: [ + '+12017777777', + '+12018888888', + '+12019999999', + ], + parameters: { + name: { + '+12017777777': 'John', + default: 'there', + }, + }, + body: 'Hello ${name}!', + delivery_report: 'none', + type: 'mt_text', + }, + }; + dryRunResponse = await batchesApi.dryRun(sendSmsRequest); +}); + +Then('the response contains the calculated bodies and number of parts for all messages in the batch', () => { + assert.equal(dryRunResponse.number_of_messages, 3); + assert.equal(dryRunResponse.number_of_recipients, 3); + assert.ok(dryRunResponse.per_recipient); + assert.equal(dryRunResponse.per_recipient.length, 3); + const johnMessage = dryRunResponse.per_recipient.filter( + (perRecipient) => perRecipient.recipient === '12017777777', + ).pop(); + assert.ok(johnMessage); + assert.equal(johnMessage.body, 'Hello John!'); + assert.equal(johnMessage.number_of_parts, 1); + assert.equal(johnMessage.encoding, 'text'); + const defaultMessage = dryRunResponse.per_recipient.filter( + (perRecipient) => perRecipient.recipient === '12018888888', + ).pop(); + assert.ok(defaultMessage); + assert.equal(defaultMessage.body, 'Hello there!'); + assert.equal(defaultMessage.number_of_parts, 1); + assert.equal(defaultMessage.encoding, 'text'); +}); + +When('I send a request to list the SMS batches', async () => { + const listBatchRequest: Sms.ListBatchesRequestData = { + page_size: 2, + }; + listResponse = await batchesApi.list(listBatchRequest); +}); + +Then('the response contains {string} SMS batches', (expectedAnswer: string) => { + const expectedBatchesCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedBatchesCount); +}); + +When('I send a request to list all the SMS batches', async () => { + batchesList = []; + for await (const batch of batchesApi.list({ page_size: 2 })) { + batchesList.push(batch); + } +}); + +When('I iterate manually over the SMS batches pages', async () => { + batchesList = []; + listResponse = await batchesApi.list({ + page_size: 2, + }); + batchesList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + batchesList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the SMS batches list contains {string} SMS batches', (expectedAnswer: string) => { + const expectedBatchesCount = parseInt(expectedAnswer, 10); + assert.equal(batchesList.length, expectedBatchesCount); +}); + +Then('the SMS batches iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve an SMS batch', async () => { + batch = await batchesApi.get({ + batch_id: '01W4FFL35P4NC4K35SMSBATCH1', + }); +}); + +Then('the response contains the SMS batch details', () => { + assert.equal(batch.id, '01W4FFL35P4NC4K35SMSBATCH1'); + assert.deepEqual(batch.to, ['12017777777']); + assert.equal(batch.from, '12015555555'); + assert.equal(batch.canceled, false); + assert.equal(batch.body, 'SMS body message'); + assert.equal(batch.type, 'mt_text'); + assert.deepEqual(batch.created_at, new Date('2024-06-06T09:22:14.304Z')); + assert.deepEqual(batch.modified_at, new Date('2024-06-06T09:22:14.304Z')); + const fullDeliveryReport: Sms.DeliveryReportEnum = 'full'; + assert.equal(batch.delivery_report, fullDeliveryReport); + assert.deepEqual(batch.send_at, new Date('2024-06-06T09:25:00Z')); + assert.deepEqual(batch.expire_at, new Date('2024-06-09T09:25:00Z')); + assert.equal(batch.feedback_enabled, true); + assert.equal((batch as Sms.TextResponse).flash_message, false); +}); + +When('I send a request to update an SMS batch', async () => { + batch = await batchesApi.update({ + batch_id: '01W4FFL35P4NC4K35SMSBATCH1', + updateBatchMessageRequestBody: { + from: '+12016666666', + to_add: [ + '01W4FFL35P4NC4K35SMSGROUP1', + ], + delivery_report: 'summary', + }, + }); +}); + +Then('the response contains the SMS batch details with updated data', () => { + assert.equal(batch.id, '01W4FFL35P4NC4K35SMSBATCH1'); + assert.deepEqual(batch.to, ['12017777777', '01W4FFL35P4NC4K35SMSGROUP1']); + assert.equal(batch.from, '12016666666'); + assert.equal(batch.canceled, false); + assert.equal(batch.body, 'SMS body message'); + assert.equal(batch.type, 'mt_text'); + assert.deepEqual(batch.created_at, new Date('2024-06-06T09:22:14.304Z')); + assert.deepEqual(batch.modified_at, new Date('2024-06-06T09:22:48.054Z')); + const summaryDeliveryReport: Sms.DeliveryReportEnum = 'summary'; + assert.equal(batch.delivery_report, summaryDeliveryReport); + assert.deepEqual(batch.send_at, new Date('2024-06-06T09:25:00Z')); + assert.deepEqual(batch.expire_at, new Date('2024-06-09T09:25:00Z')); + assert.equal(batch.feedback_enabled, true); + assert.equal((batch as Sms.TextResponse).flash_message, false); +}); + +When('I send a request to replace an SMS batch', async () => { + batch = await batchesApi.replace({ + batch_id: '01W4FFL35P4NC4K35SMSBATCH1', + replaceBatchMessageRequestBody: { + from: '+12016666666', + to: [ + '+12018888888', + ], + body: 'This is the replacement batch', + send_at: new Date('2024-06-06T09:35:00Z'), + }, + }); +}); + +Then('the response contains the new SMS batch details with the provided data for replacement', () => { + assert.equal(batch.id, '01W4FFL35P4NC4K35SMSBATCH1'); + assert.deepEqual(batch.to, ['12018888888']); + assert.equal(batch.from, '12016666666'); + assert.equal(batch.canceled, false); + assert.equal(batch.body, 'This is the replacement batch'); + assert.equal(batch.type, 'mt_text'); + assert.deepEqual(batch.created_at, new Date('2024-06-06T09:22:14.304Z')); + assert.deepEqual(batch.modified_at, new Date('2024-06-06T09:23:32.504Z')); + const noDeliveryReport: Sms.DeliveryReportEnum = 'none'; + assert.equal(batch.delivery_report, noDeliveryReport); + assert.deepEqual(batch.send_at, new Date('2024-06-06T09:35:00Z')); + assert.deepEqual(batch.expire_at, new Date('2024-06-09T09:35:00Z')); + assert.equal(batch.feedback_enabled, undefined); + assert.equal((batch as Sms.TextResponse).flash_message, false); +}); + +When('I send a request to cancel an SMS batch', async () => { + batch = await batchesApi.cancel({ + batch_id: '01W4FFL35P4NC4K35SMSBATCH1', + }); +}); + +Then('the response contains the SMS batch details with a cancelled status', () => { + assert.equal(batch.id, '01W4FFL35P4NC4K35SMSBATCH1'); + assert.equal(batch.canceled, true); +}); + +When('I send a request to send delivery feedbacks', async () => { + deliveryFeedbackResponse = await batchesApi.sendDeliveryFeedback({ + batch_id: '01W4FFL35P4NC4K35SMSBATCH1', + deliveryFeedbackRequestBody: { + recipients: [ + '+12017777777', + ], + }, + }); +}); + +Then('the delivery feedback response contains no data', () => { + assert.deepEqual(deliveryFeedbackResponse, {}); +}); diff --git a/packages/sms/tests/rest/v1/callbacks/webhooks-events.steps.ts b/packages/sms/tests/rest/v1/callbacks/webhooks-events.steps.ts new file mode 100644 index 00000000..b1de8ac8 --- /dev/null +++ b/packages/sms/tests/rest/v1/callbacks/webhooks-events.steps.ts @@ -0,0 +1,84 @@ +import { Given, Then, When } from '@cucumber/cucumber'; +import { SmsCallbackWebhooks, SmsCallback, Sms } from '../../../../src'; +import assert from 'assert'; + +let smsCallbackWebhook: SmsCallbackWebhooks; +let rawEvent: any; +let event: SmsCallback; + +const processEvent = async (response: Response) => { + rawEvent = await response.text(); + event = smsCallbackWebhook.parseEvent(JSON.parse(rawEvent)); +}; + +Given('the SMS Webhooks handler is available', () => { + smsCallbackWebhook = new SmsCallbackWebhooks(); +}); + +When('I send a request to trigger an "incoming SMS" event', async () => { + const response = await fetch('http://localhost:3017/webhooks/sms/incoming-sms'); + await processEvent(response); +}); + +Then('the SMS event describes an "incoming SMS" event', () => { + const incomingSmsEvent = event as Sms.MOText; + assert.equal(incomingSmsEvent.id, '01W4FFL35P4NC4K35SMSBATCH8'); + assert.equal(incomingSmsEvent.from, '12015555555'); + assert.equal(incomingSmsEvent.to, '12017777777'); + assert.equal(incomingSmsEvent.body, 'Hello John! 👋'); + assert.equal(incomingSmsEvent.type, 'mo_text'); + assert.equal(incomingSmsEvent.operator_id, '311071'); + assert.deepEqual(incomingSmsEvent.received_at, new Date('2024-06-06T07:52:37.386Z')); +}); + +When('I send a request to trigger an "SMS delivery report" event', async () => { + const response = await fetch('http://localhost:3017/webhooks/sms/delivery-report-sms'); + await processEvent(response); +}); + +Then('the SMS event describes an "SMS delivery report" event', () => { + const deliveryReportEvent = event as Sms.DeliveryReport; + assert.equal(deliveryReportEvent.batch_id, '01W4FFL35P4NC4K35SMSBATCH8'); + assert.equal(deliveryReportEvent.client_reference, 'client-ref'); + assert.ok(deliveryReportEvent.statuses); + const status = deliveryReportEvent.statuses[0]; + assert.equal(status.code, 0); + assert.equal(status.count, 2); + const deliveryStatus: Sms.DeliveryReportStatusEnum = 'Delivered'; + assert.equal(status.status, deliveryStatus); + assert.ok(status.recipients); + assert.equal(status.recipients[0], '12017777777'); + assert.equal(status.recipients[1], '33612345678'); + assert.equal(deliveryReportEvent.type, 'delivery_report_sms'); +}); + +// eslint-disable-next-line max-len +When('I send a request to trigger an "SMS recipient delivery report" event with the status {string}', async (status: string) => { + const response = await fetch(`http://localhost:3017/webhooks/sms/recipient-delivery-report-sms-${status.toLowerCase()}`); + await processEvent(response); +}); + +Then('the SMS event describes an SMS recipient delivery report event with the status "Delivered"', () => { + const recipientDeliveryReportEvent = event as Sms.RecipientDeliveryReport; + assert.equal(recipientDeliveryReportEvent.batch_id, '01W4FFL35P4NC4K35SMSBATCH9'); + assert.equal(recipientDeliveryReportEvent.recipient, '12017777777'); + assert.equal(recipientDeliveryReportEvent.code, 0); + const deliveryStatus: Sms.DeliveryReportStatusEnum = 'Delivered'; + assert.equal(recipientDeliveryReportEvent.status, deliveryStatus); + assert.equal(recipientDeliveryReportEvent.type, 'recipient_delivery_report_sms'); + assert.equal(recipientDeliveryReportEvent.client_reference, 'client-ref'); + assert.deepEqual(recipientDeliveryReportEvent.at, new Date('2024-06-06T08:17:19.210Z')); + assert.deepEqual(recipientDeliveryReportEvent.operator_status_at, new Date('2024-06-06T08:17:00Z')); +}); + +Then('the SMS event describes an SMS recipient delivery report event with the status "Aborted"', () => { + const recipientDeliveryReportEvent = event as Sms.RecipientDeliveryReport; + assert.equal(recipientDeliveryReportEvent.batch_id, '01W4FFL35P4NC4K35SMSBATCH9'); + assert.equal(recipientDeliveryReportEvent.recipient, '12010000000'); + assert.equal(recipientDeliveryReportEvent.code, 412); + const deliveryStatus: Sms.DeliveryReportStatusEnum = 'Aborted'; + assert.equal(recipientDeliveryReportEvent.status, deliveryStatus); + assert.equal(recipientDeliveryReportEvent.type, 'recipient_delivery_report_sms'); + assert.equal(recipientDeliveryReportEvent.client_reference, 'client-ref'); + assert.deepEqual(recipientDeliveryReportEvent.at, new Date('2024-06-06T08:17:15.603Z')); +}); diff --git a/packages/sms/tests/rest/v1/delivery-reports/delivery-reports-api.test.ts b/packages/sms/tests/rest/v1/delivery-reports/delivery-reports-api.test.ts index 6a6c7f27..f4d98518 100644 --- a/packages/sms/tests/rest/v1/delivery-reports/delivery-reports-api.test.ts +++ b/packages/sms/tests/rest/v1/delivery-reports/delivery-reports-api.test.ts @@ -58,7 +58,7 @@ describe('DeliveryReportsApi', () => { // Given const requestData: Sms.GetDeliveryReportByPhoneNumberRequestData = { batch_id: '01HF28S9AAGRKWP2CY92BJB569', - recipient_msisdn: '+33444555666', + phone_number: '+33444555666', }; const expectedResponse: Sms.RecipientDeliveryReport = { batch_id: '01HF28S9AAGRKWP2CY92BJB569', diff --git a/packages/sms/tests/rest/v1/delivery-reports/delivery-reports.steps.ts b/packages/sms/tests/rest/v1/delivery-reports/delivery-reports.steps.ts new file mode 100644 index 00000000..090f3ef8 --- /dev/null +++ b/packages/sms/tests/rest/v1/delivery-reports/delivery-reports.steps.ts @@ -0,0 +1,140 @@ +import { DeliveryReportsApi, SmsService, Sms } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let deliveryReportsApi: DeliveryReportsApi; +let deliveryReport: Sms.DeliveryReport; +let recipientDeliveryReport: Sms.RecipientDeliveryReport; +let listResponse: PageResult; +let recipientDeliveryReportList: Sms.RecipientDeliveryReport[]; +let pagesIteration: number; + +Given('the SMS service "Delivery Reports" is available', () => { + const smsService = new SmsService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + smsHostname: 'http://localhost:3017', + }); + deliveryReportsApi = smsService.deliveryReports; +}); + +When('I send a request to retrieve a summary SMS delivery report', async () => { + const requestData: Sms.GetDeliveryReportByBatchIdRequestData = { + batch_id: '01W4FFL35P4NC4K35SMSBATCH1', + status: [ + 'Delivered', + 'Failed', + ], + code: [15, 0], + }; + deliveryReport = await deliveryReportsApi.get(requestData); +}); + +Then('the response contains a summary SMS delivery report', () => { + assert.equal(deliveryReport.batch_id, '01W4FFL35P4NC4K35SMSBATCH1'); + assert.equal(deliveryReport.client_reference, 'reference_e2e'); + assert.ok(deliveryReport.statuses); + let status = deliveryReport.statuses[0]; + assert.equal(status.code, 15); + assert.equal(status.count, 1); + assert.equal(status.recipients, undefined); + const failedStatus: Sms.DeliveryReportStatusEnum = 'Failed'; + assert.equal(status.status, failedStatus); + status = deliveryReport.statuses[1]; + assert.equal(status.code, 0); + assert.equal(status.count, 1); + assert.equal(status.recipients, undefined); + const deliveredStatus: Sms.DeliveryReportStatusEnum = 'Delivered'; + assert.equal(status.status, deliveredStatus); + assert.equal(deliveryReport.total_message_count, 2); + assert.equal(deliveryReport.type, 'delivery_report_sms'); +}); + +When('I send a request to retrieve a full SMS delivery report', async () => { + const requestData: Sms.GetDeliveryReportByBatchIdRequestData = { + batch_id: '01W4FFL35P4NC4K35SMSBATCH1', + type: 'full', + }; + deliveryReport = await deliveryReportsApi.get(requestData); +}); + +Then('the response contains a full SMS delivery report', () => { + assert.equal(deliveryReport.batch_id, '01W4FFL35P4NC4K35SMSBATCH1'); + assert.ok(deliveryReport.statuses); + const status = deliveryReport.statuses[0]; + assert.ok(status.recipients); + assert.equal(status.code, 0); + assert.equal(status.count, 1); + assert.equal(status.recipients[0], '12017777777'); + const deliveredStatus: Sms.DeliveryReportStatusEnum = 'Delivered'; + assert.equal(status.status, deliveredStatus); +}); + +When('I send a request to retrieve a recipient\'s delivery report', async () => { + const requestData: Sms.GetDeliveryReportByPhoneNumberRequestData = { + batch_id: '01W4FFL35P4NC4K35SMSBATCH1', + phone_number: '12017777777', + }; + recipientDeliveryReport = await deliveryReportsApi.getForNumber(requestData); +}); + +Then('the response contains the recipient\'s delivery report details', () => { + assert.equal(recipientDeliveryReport.batch_id, '01W4FFL35P4NC4K35SMSBATCH1'); + assert.equal(recipientDeliveryReport.recipient, '12017777777'); + assert.equal(recipientDeliveryReport.client_reference, 'reference_e2e'); + const deliveredStatus: Sms.DeliveryReportStatusEnum = 'Delivered'; + assert.equal(recipientDeliveryReport.status, deliveredStatus); + assert.equal(recipientDeliveryReport.type, 'recipient_delivery_report_sms'); + assert.equal(recipientDeliveryReport.code, 0); + assert.deepEqual(recipientDeliveryReport.at, new Date('2024-06-06T13:06:27.833Z')); + assert.deepEqual(recipientDeliveryReport.operator_status_at, new Date('2024-06-06T13:06:00Z')); +}); + +When('I send a request to list the SMS delivery reports', async () => { + const requestData: Sms.ListDeliveryReportsRequestData = {}; + listResponse = await deliveryReportsApi.list(requestData); +}); + +Then('the response contains {string} SMS delivery reports', (expectedAnswer: string) => { + const expectedDeliveryReportsCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedDeliveryReportsCount); +}); + +When('I send a request to list all the SMS delivery reports', async () => { + recipientDeliveryReportList = []; + for await (const deliveryReport of deliveryReportsApi.list({ page_size: 2 })) { + recipientDeliveryReportList.push(deliveryReport); + } +}); + +When('I iterate manually over the SMS delivery reports pages', async () => { + recipientDeliveryReportList = []; + listResponse = await deliveryReportsApi.list({ + page_size: 2, + }); + recipientDeliveryReportList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + recipientDeliveryReportList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the SMS delivery reports list contains {string} SMS delivery reports', (expectedAnswer: string) => { + const expectedDeliveryReportsCount = parseInt(expectedAnswer, 10); + assert.equal(recipientDeliveryReportList.length, expectedDeliveryReportsCount); +}); + +Then('the SMS delivery reports iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); diff --git a/packages/sms/tests/rest/v1/groups/groups-api.test.ts b/packages/sms/tests/rest/v1/groups/groups-api.test.ts index 97685e34..691f7fed 100644 --- a/packages/sms/tests/rest/v1/groups/groups-api.test.ts +++ b/packages/sms/tests/rest/v1/groups/groups-api.test.ts @@ -31,7 +31,7 @@ describe('GroupsApi', () => { ], }, }; - const expectedResponse: Sms.CreateGroupResponse = { + const expectedResponse: Sms.Group = { id: '01HF6EFE21REWJC3B3JWG4FYZ7', name: 'My group', size: 2, @@ -96,7 +96,7 @@ describe('GroupsApi', () => { // Given const requestData: Sms.ListGroupsRequestData = {}; - const mockData: Sms.GroupResponse[] =[ + const mockData: Sms.Group[] =[ { id: '01HF6EFE21REWJC3B3JWG4FYZ7', name: 'My group', @@ -137,7 +137,7 @@ describe('GroupsApi', () => { ], }, }; - const expectedResponse: Sms.GroupResponse = { + const expectedResponse: Sms.Group = { id: '01HF6EFE21REWJC3B3JWG4FYZ7', name: 'My new group name', size: 1, @@ -162,7 +162,7 @@ describe('GroupsApi', () => { const requestData: Sms.GetGroupRequestData = { group_id: '01HF6EFE21REWJC3B3JWG4FYZ7', }; - const expectedResponse: Sms.GroupResponse = { + const expectedResponse: Sms.Group = { id: '01HF6EFE21REWJC3B3JWG4FYZ7', name: 'My new group name', size: 1, @@ -193,7 +193,7 @@ describe('GroupsApi', () => { ], }, }; - const expectedResponse: Sms.GroupResponse = { + const expectedResponse: Sms.Group = { id: '01HF6EFE21REWJC3B3JWG4FYZ7', name: 'My new group name', size: 3, diff --git a/packages/sms/tests/rest/v1/groups/groups.steps.ts b/packages/sms/tests/rest/v1/groups/groups.steps.ts new file mode 100644 index 00000000..ab959c95 --- /dev/null +++ b/packages/sms/tests/rest/v1/groups/groups.steps.ts @@ -0,0 +1,191 @@ +import { GroupsApi, SmsService, Sms } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let groupsApi: GroupsApi; +let group: Sms.Group; +let listResponse: PageResult; +let groupsList: Sms.Group[]; +let pagesIteration: number; +let deleteGroupResponse: void; +let phoneNumbersList: string[]; + +Given('the SMS service "Groups" is available', () => { + const smsService = new SmsService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + smsHostname: 'http://localhost:3017', + }); + groupsApi = smsService.groups; +}); + +When('I send a request to create an SMS group', async () => { + group = await groupsApi.create({ + createGroupRequestBody: { + name: 'Group master', + members: [ + '+12017778888', + '+12018887777', + ], + child_groups: [ + '01W4FFL35P4NC4K35SUBGROUP1', + ], + }, + }); +}); + +Then('the response contains the SMS group details', () => { + assert.equal(group.id, '01W4FFL35P4NC4K35SMSGROUP1'); + assert.equal(group.name, 'Group master'); + assert.equal(group.size, 2); + assert.deepEqual(group.created_at, new Date('2024-06-06T08:59:22.156Z')); + assert.deepEqual(group.modified_at, new Date('2024-06-06T08:59:22.156Z')); + assert.ok(group.child_groups); + assert.equal(group.child_groups[0], '01W4FFL35P4NC4K35SUBGROUP1'); +}); + +When('I send a request to list the existing SMS groups', async () => { + listResponse = await groupsApi.list({ + page_size: 2, + }); +}); + +Then('the response contains {string} SMS groups', (expectedAnswer: string) => { + const expectedGroupsCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedGroupsCount); +}); + +When('I send a request to list all the SMS groups', async () => { + groupsList = []; + for await (const group of groupsApi.list({ page_size: 2 })) { + groupsList.push(group); + } +}); + +When('I iterate manually over the SMS groups pages', async () => { + groupsList = []; + listResponse = await groupsApi.list({ + page_size: 2, + }); + groupsList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + groupsList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the SMS groups list contains {string} SMS groups', (expectedAnswer: string) => { + const expectedGroupsCount = parseInt(expectedAnswer, 10); + assert.equal(groupsList.length, expectedGroupsCount); +}); + +Then('the SMS groups iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); + +When('I send a request to retrieve an SMS group', async () => { + group = await groupsApi.get({ + group_id: '01W4FFL35P4NC4K35SMSGROUP1', + }); +}); + +When('I send a request to update an SMS group', async () => { + group = await groupsApi.update({ + group_id: '01W4FFL35P4NC4K35SMSGROUP1', + updateGroupRequestBody: { + name: 'Updated group name', + add: [ + '+12017771111', + '+12017772222', + ], + remove: [ + '+12017773333', + '+12017774444', + ], + add_from_group: '01W4FFL35P4NC4K35SMSGROUP2', + remove_from_group: '01W4FFL35P4NC4K35SMSGROUP3', + }, + }); +}); + +Then('the response contains the updated SMS group details', () => { + assert.equal(group.id, '01W4FFL35P4NC4K35SMSGROUP1'); + assert.equal(group.name, 'Updated group name'); + assert.equal(group.size, 6); + assert.deepEqual(group.created_at, new Date('2024-06-06T08:59:22.156Z')); + assert.deepEqual(group.modified_at, new Date('2024-06-06T09:19:58.147Z')); + assert.ok(group.child_groups); + assert.equal(group.child_groups[0], '01W4FFL35P4NC4K35SUBGROUP1'); +}); + +When('I send a request to update an SMS group to remove its name', async () => { + group = await groupsApi.update({ + group_id: '01W4FFL35P4NC4K35SMSGROUP2', + updateGroupRequestBody: { + name: null, + }, + }); +}); + +Then('the response contains the updated SMS group details where the name has been removed', () => { + assert.equal(group.id, '01W4FFL35P4NC4K35SMSGROUP2'); + assert.equal(group.name, undefined); +}); + +When('I send a request to replace an SMS group', async () => { + group = await groupsApi.replace({ + group_id: '01W4FFL35P4NC4K35SMSGROUP1', + replaceGroupRequestBody: { + name: 'Replacement group', + members: [ + '+12018881111', + '+12018882222', + '+12018883333', + ], + }, + }); +}); + +Then('the response contains the replaced SMS group details', () => { + assert.equal(group.id, '01W4FFL35P4NC4K35SMSGROUP1'); + assert.equal(group.name, 'Replacement group'); + assert.equal(group.size, 3); + assert.deepEqual(group.created_at, new Date('2024-06-06T08:59:22.156Z')); + assert.deepEqual(group.modified_at, new Date('2024-08-21T09:39:36.679Z')); + assert.ok(group.child_groups); + assert.equal(group.child_groups[0], '01W4FFL35P4NC4K35SUBGROUP1'); +}); + +When('I send a request to delete an SMS group', async () => { + deleteGroupResponse = await groupsApi.delete({ + group_id: '01W4FFL35P4NC4K35SMSGROUP1', + }); +}); + +Then('the delete SMS group response contains no data', () => { + assert.deepEqual(deleteGroupResponse, {} ); +}); + +When('I send a request to list the members of an SMS group', async () => { + phoneNumbersList = await groupsApi.listMembers({ + group_id: '01W4FFL35P4NC4K35SMSGROUP1', + }); +}); + +Then('the response contains the phone numbers of the SMS group', () => { + assert.ok(phoneNumbersList); + assert.equal(phoneNumbersList[0], '12018881111'); + assert.equal(phoneNumbersList[1], '12018882222'); + assert.equal(phoneNumbersList[2], '12018883333'); +}); diff --git a/packages/sms/tests/rest/v1/inbounds/inbounds.steps.ts b/packages/sms/tests/rest/v1/inbounds/inbounds.steps.ts new file mode 100644 index 00000000..332d4027 --- /dev/null +++ b/packages/sms/tests/rest/v1/inbounds/inbounds.steps.ts @@ -0,0 +1,100 @@ +import { InboundsApi, SmsService, Sms } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { PageResult } from '@sinch/sdk-client'; + +let inboundsApi: InboundsApi; +let inboundMessage: Sms.InboundMessageResponse; +let listResponse: PageResult; +let inboundMessagesList: Sms.InboundMessageResponse[]; +let pagesIteration: number; + +Given('the SMS service "Inbounds" is available', () => { + const smsService = new SmsService({ + projectId: 'tinyfrog-jump-high-over-lilypadbasin', + keyId: 'keyId', + keySecret: 'keySecret', + authHostname: 'http://localhost:3011', + smsHostname: 'http://localhost:3017', + }); + inboundsApi = smsService.inbounds; +}); + +When('I send a request to retrieve an inbound message', async () => { + inboundMessage = await inboundsApi.get({ + inbound_id: '01W4FFL35P4NC4K35INBOUND01', + }); +}); + +Then('the response contains the inbound message details', () => { + assert.equal(inboundMessage.id, '01W4FFL35P4NC4K35INBOUND01'); + assert.equal(inboundMessage.from, '12015555555'); + assert.equal(inboundMessage.to, '12017777777'); + assert.equal(inboundMessage.body, 'Hello John!'); + assert.equal(inboundMessage.type, 'mo_text'); + assert.equal(inboundMessage.operator_id, '311071'); + assert.deepEqual(inboundMessage.received_at, new Date('2024-06-06T14:16:54.777Z')); +}); + +When('I send a request to list the inbound messages', async () => { + const listInboundMessagesRequest: Sms.ListInboundMessagesRequestData = { + page_size: 2, + to: [ + '+12017777777', + '+12018888888', + ], + }; + listResponse = await inboundsApi.list(listInboundMessagesRequest); +}); + +Then('the response contains {string} inbound messages', (expectedAnswer: string) => { + const expectedInboundMessagesCount = parseInt(expectedAnswer, 10); + assert.equal(listResponse.data.length, expectedInboundMessagesCount); +}); + +When('I send a request to list all the inbound messages', async () => { + inboundMessagesList = []; + const listInboundMessagesRequest: Sms.ListInboundMessagesRequestData = { + page_size: 2, + to: [ + '+12017777777', + '+12018888888', + ], + }; + for await (const inboundMessage of inboundsApi.list(listInboundMessagesRequest)) { + inboundMessagesList.push(inboundMessage); + } +}); + +When('I iterate manually over the inbound messages pages', async () => { + inboundMessagesList = []; + listResponse = await inboundsApi.list({ + page_size: 2, + to: [ + '+12017777777', + '+12018888888', + ], + }); + inboundMessagesList.push(...listResponse.data); + pagesIteration = 1; + let reachedEndOfPages = false; + while (!reachedEndOfPages) { + if (listResponse.hasNextPage) { + listResponse = await listResponse.nextPage(); + inboundMessagesList.push(...listResponse.data); + pagesIteration++; + } else { + reachedEndOfPages = true; + } + } +}); + +Then('the inbound messages list contains {string} inbound messages', (expectedAnswer: string) => { + const expectedInboundMessagesCount = parseInt(expectedAnswer, 10); + assert.equal(inboundMessagesList.length, expectedInboundMessagesCount); +}); + +Then('the inbound messages iteration result contains the data from {string} pages', (expectedAnswer: string) => { + const expectedPagesCount = parseInt(expectedAnswer, 10); + assert.equal(pagesIteration, expectedPagesCount); +}); diff --git a/packages/sms/tests/rest/v1/sms-api.test.ts b/packages/sms/tests/rest/v1/sms-api.test.ts index a4792982..08576075 100644 --- a/packages/sms/tests/rest/v1/sms-api.test.ts +++ b/packages/sms/tests/rest/v1/sms-api.test.ts @@ -44,7 +44,6 @@ describe('SMS API', () => { it('should log a warning when using an unsupported region', async () => { paramsWithProjectId.smsRegion = 'bzh'; - paramsWithProjectId.forceOAuth2ForSmsApi = true; smsApi = new SmsDomainApi(paramsWithProjectId, 'dummy'); console.warn = jest.fn(); smsApi.getSinchClient(); @@ -109,38 +108,58 @@ describe('SMS API', () => { expect(smsApi.client?.apiClientOptions.hostname).toBe('https://sms.api.sinch.com'); }); - it('should not update the credentials when adding servicePlanId credentials on default region', () => { + it('should update the credentials and URL when adding SMS credentials to the unified credentials', () => { smsApi = new SmsDomainApi(paramsWithProjectId, 'dummy'); smsApi.getSinchClient(); expect(smsApi.client?.apiClientOptions.projectId).toBe('PROJECT_ID'); expect(smsApi.client?.apiClientOptions.hostname).toBe('https://zt.us.sms.api.sinch.com'); - smsApi.setCredentials(paramsWithServicePlanId); - expect(smsApi.client?.apiClientOptions.projectId).toBe('PROJECT_ID'); - expect(smsApi.client?.apiClientOptions.hostname).toBe('https://zt.us.sms.api.sinch.com'); + smsApi.setCredentials({ + ...paramsWithServicePlanId, + }); + expect(smsApi.client?.apiClientOptions.projectId).toBe('SERVICE_PLAN_ID'); + expect(smsApi.client?.apiClientOptions.hostname).toBe('https://us.sms.api.sinch.com'); }); - it('should update the credentials and URL when forcing servicePlanId credentials', () => { + // eslint-disable-next-line max-len + it('should update the credentials and URL when adding SMS credentials on BR region to the unified credentials', () => { smsApi = new SmsDomainApi(paramsWithProjectId, 'dummy'); smsApi.getSinchClient(); expect(smsApi.client?.apiClientOptions.projectId).toBe('PROJECT_ID'); expect(smsApi.client?.apiClientOptions.hostname).toBe('https://zt.us.sms.api.sinch.com'); smsApi.setCredentials({ ...paramsWithServicePlanId, - forceServicePlanIdUsageForSmsApi: true, + smsRegion: SmsRegion.BRAZIL, + }); + expect(smsApi.client?.apiClientOptions.projectId).toBe('SERVICE_PLAN_ID'); + expect(smsApi.client?.apiClientOptions.hostname).toBe('https://br.sms.api.sinch.com'); + }); + + // eslint-disable-next-line max-len + it('should not update the credentials nor URL when adding unified credentials to the SMS credentials', () => { + smsApi = new SmsDomainApi(paramsWithServicePlanId, 'dummy'); + smsApi.getSinchClient(); + expect(smsApi.client?.apiClientOptions.projectId).toBe('SERVICE_PLAN_ID'); + expect(smsApi.client?.apiClientOptions.hostname).toBe('https://us.sms.api.sinch.com'); + smsApi.setCredentials({ + ...paramsWithProjectId, }); expect(smsApi.client?.apiClientOptions.projectId).toBe('SERVICE_PLAN_ID'); expect(smsApi.client?.apiClientOptions.hostname).toBe('https://us.sms.api.sinch.com'); }); - it('should update the credentials and URL when adding servicePlanId credentials on BR region', () => { - smsApi = new SmsDomainApi(paramsWithProjectId, 'dummy'); + // eslint-disable-next-line max-len + it('should update the region in the URL when adding unified credentials and region to the SMS credentials', () => { + smsApi = new SmsDomainApi(paramsWithServicePlanId, 'dummy'); smsApi.getSinchClient(); - expect(smsApi.client?.apiClientOptions.projectId).toBe('PROJECT_ID'); - expect(smsApi.client?.apiClientOptions.hostname).toBe('https://zt.us.sms.api.sinch.com'); + expect(smsApi.client?.apiClientOptions.projectId).toBe('SERVICE_PLAN_ID'); + expect(smsApi.client?.apiClientOptions.hostname).toBe('https://us.sms.api.sinch.com'); + console.warn = jest.fn(); smsApi.setCredentials({ - ...paramsWithServicePlanId, + ...paramsWithProjectId, smsRegion: SmsRegion.BRAZIL, }); + expect(console.warn).toHaveBeenCalledWith( + 'As the servicePlanId and the apiToken are provided, all other credentials will be disregarded.'); expect(smsApi.client?.apiClientOptions.projectId).toBe('SERVICE_PLAN_ID'); expect(smsApi.client?.apiClientOptions.hostname).toBe('https://br.sms.api.sinch.com'); }); diff --git a/packages/verification/CHANGELOG.md b/packages/verification/CHANGELOG.md index 266a7765..6e98766f 100644 --- a/packages/verification/CHANGELOG.md +++ b/packages/verification/CHANGELOG.md @@ -1,3 +1,64 @@ +## Version 1.2.0 +- [Tech] Update dependency `@sinch/sdk-client` to `1.2.0`. +- [Feature] Support the `locale` parameter in the `SmsOptions` interface. +- [Deprecation Notice] In the interface `VerificationStatusByIdentityRequestData`, the property `method` supports ‘flashcall’ on top of ‘flashCall’. +- [Deprecation Notice] All the references to "callout" and "seamless" will be replaced by "phoneCall" and "data" respectively. + - In the interface `VerificationStatusByIdentityRequestData`, the property `method` supports ‘phonecall’ on top of ‘callout’. + - API methods: + +| Deprecated | New | +|-------------------------------------------|---------------------------------------------| +| `verifications.startCallout()` | `verifications.startPhoneCall()` | +| `verifications.reportCalloutById()` | `verifications.reportPhoneCallById()` | +| `verifications.reportCalloutByIdentity()` | `verifications.reportPhoneCallByIdentity()` | +- + - Helpers: + +| Deprecated | New | +|------------------------------------------------------------|---------------------------------------------------| +| `startVerificationHelper.buildCalloutRequest()` | `startVerificationHelper.buildPhoneCallRequest()` | +| `startVerificationHelper.buildSeamlessRequest()` | `startVerificationHelper.buildDataRequest()` | +| `reportVerificationByIdHelper.buildCalloutRequest()` | `startVerificationHelper.buildPhoneCallRequest()` | +| `reportVerificationByIdentityHelper.buildCalloutRequest()` | `startVerificationHelper.buildPhoneCallRequest()` | +- + - Interfaces + +| Deprecated | New | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|
StartCalloutVerificationRequestData {
  startVerificationWithCalloutRequestBody: StartVerificationWithCallout;
}
|
StartPhoneCallVerificationRequestData {
  startVerificationWithPhoneCallRequestBody: StartVerificationWithPhoneCall;
}
| +|
StartSeamlessVerificationRequestData {
  startSeamlessVerificationRequestBody: StartSeamlessVerification;
}
|
StartDataVerificationRequestData {
  startDataVerificationRequestBody: StartDataVerification;
}
| +|
ReportCalloutVerificationByIdRequestData {
  reportCalloutVerificationByIdentityRequestBody: CalloutVerificationReportRequest;
}
|
ReportPhoneCallVerificationByIdRequestData {
  reportPhoneCallVerificationByIdRequestBody: PhoneCallVerificationReportRequest;
}
| +|
ReportCalloutVerificationByIdentityRequestData {
  reportCalloutVerificationByIdentityRequestBody: CalloutVerificationReportRequest;
}
|
ReportPhoneCallVerificationByIdentityRequestData {
  reportPhoneCallVerificationByIdentityRequestBody: PhoneCallVerificationReportRequest;
}
| +|
StartVerificationWithCallout {
  calloutOptions: CalloutOptions;
}
|
StartVerificationWithPhoneCall {
  phoneCallOptions: PhoneCallOptions;
}
| +| `StartSeamlessVerification` | `StartDataVerification` | +| `CalloutOptions` | `PhoneCallOptions` | +| `CalloutOptionsSpeech` | `PhoneCallOptionsSpeech` | +| `CalloutVerificationStatusResponse` | `PhoneCallVerificationStatusResponse` | +| `CalloutVerificationReportResponse` | `PhoneCallVerificationReportResponse` | +| `StartCalloutVerificationResponse` | `StartPhoneCallVerificationResponse` | +| `CalloutRequestEventResponse` | `PhoneCallRequestEventResponse` | +| `CalloutProperties` | `PhoneCallProperties` | +| `CalloutContent` | `PhoneCallContent` | +- [Deprecation Notice] The interfaces containing `SMS` in uppercase will be replaced with the same name but with `Sms` in PascaleCase: + +| Deprecated | New | +|-------------------------------|-------------------------------| +| VerificationPriceSMS | VerificationPriceSms | +| SMSRequestEventResponse | SmsRequestEventResponse | +| SMSVerificationStatusResponse | SmsVerificationStatusResponse | +| SMSVerificationReportResponse | SmsVerificationReportResponse | + +- [Deprecation Notice] The type `VerificationCallback` becomes `VerificationCallbackEvent` and is accessible on the `Verification` namespace. + +| Deprecated | New | +|-----------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| +|
handleEvent(event: VerificationCallback, res: Response) {
  console.log(event);
}
|
handleEvent(event: Verification.VerificationCallbackEvent, res: Response) {
  console.log(event);
}
| + +- [Deprecation Notice] The type `TypeEnum` should be replaced with the type `IdentityType`. +- [Deprecation Notice] The type `FlashCallContent` should be replaced with the type `FlashCallProperties`. +- [Deprecation Notice] The type `SmsContent` should be replaced with the type `SmsProperties`. +- [E2E] Add Cucumber steps implementation. + ## Version 1.1.0 - [Tech] Update dependency `@sinch/sdk-client` to `1.1.0` diff --git a/packages/verification/README.md b/packages/verification/README.md index 7a7631a4..5524a2fd 100644 --- a/packages/verification/README.md +++ b/packages/verification/README.md @@ -50,19 +50,22 @@ const sinch = new SinchClient(credentials); const verificationService: VerificationService = sinch.verification; // Build the request data -const requestData: Verification.StartVerificationRequestData = { - initiateVerificationRequestBody: { +const requestData: Verification.StartSmsVerificationRequestData = { + startVerificationWithSmsRequestBody: { identity: { type: 'number', endpoint: '+17813334444', }, - method: 'sms', + smsOptions: { + codeType: 'Alphanumeric', + locale: 'sv-SE', + }, }, }; // Use the 'verification' service registered on the Sinch client -const verificationInitResponse: Verification.InitiateVerificationResponse - = await verificationService.verifications.start(requestData); +const startVerificationResponse: Verification.StartSmsVerificationResponse + = await verificationService.startVerifications.startSms(requestData); ``` ### Standalone @@ -87,19 +90,22 @@ const credentials: ApplicationCredentials = { const verificationService = new VerificationService(credentials); // Build the request data -const requestData: Verification.StartVerificationRequestData = { - initiateVerificationRequestBody: { +const requestData: Verification.StartSmsVerificationRequestData = { + startVerificationWithSmsRequestBody: { identity: { type: 'number', endpoint: '+17813334444', }, - method: 'sms', + smsOptions: { + codeType: 'Alphanumeric', + locale: 'sv-SE', + }, }, }; // Use the standalone declaration of the 'verification' service -const verificationInitResponse: Verification.InitiateVerificationResponse - = await verificationService.verifications.start(requestData); +const startVerificationResponse: Verification.StartSmsVerificationResponse + = await verificationService.startVerifications.startSms(requestData); ``` ## Promises @@ -108,18 +114,18 @@ All the methods that interact with the Sinch APIs use Promises. You can use `awa ```typescript // Method 1: Wait for the Promise to complete (you need to be in an 'async' method) -let verificationInitResponse: Verification.InitiateVerificationResponse; +let startVerificationResponse: Verification.StartSmsVerificationResponse; try { - verificationInitResponse = await verificationService.verifications.start(requestData); - console.log(`Verification ID = ${verificationInitResponse.id}`); + startVerificationResponse = await verificationService.startVerifications.startSms(requestData); + console.log(`Verification ID = ${startVerificationResponse.id}`); } catch (error: any) { - console.error(`ERROR ${error.statusCode}: Impossible to start the verification for the number ${requestData.initiateVerificationRequestBody.identity.endpoint}`); + console.error(`ERROR ${error.statusCode}: Impossible to start the verification for the number ${requestData.startVerificationWithSmsRequestBody.identity.endpoint}`); } // Method 2: Resolve the promise -verificationService.verifications.start(requestData) +verificationService.startVerifications.startSms(requestData) .then(response => console.log(`Verification ID = ${response.id}`)) - .catch(error => console.error(`ERROR ${error.statusCode}: Impossible to start the verification for the number ${requestData.initiateVerificationRequestBody.identity.endpoint}`)); + .catch(error => console.error(`ERROR ${error.statusCode}: Impossible to start the verification for the number ${requestData.startVerificationWithSmsRequestBody.identity.endpoint}`)); ``` ## Contact diff --git a/packages/verification/cucumber.js b/packages/verification/cucumber.js new file mode 100644 index 00000000..691a9809 --- /dev/null +++ b/packages/verification/cucumber.js @@ -0,0 +1,8 @@ +module.exports = { + default: [ + 'tests/e2e/features/**/*.feature', + '--require-module ts-node/register', + '--require tests/rest/v1/**/*.steps.ts', + `--format-options '{"snippetInterface": "synchronous"}'`, + ].join(' '), +}; diff --git a/packages/verification/package.json b/packages/verification/package.json index fe951ea8..63213a36 100644 --- a/packages/verification/package.json +++ b/packages/verification/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/verification", - "version": "1.1.0", + "version": "1.2.0", "description": "Sinch Verification API", "homepage": "", "repository": { @@ -25,13 +25,15 @@ "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", - "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo" + "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo", + "test:e2e": "cucumber-js" }, "dependencies": { - "@sinch/sdk-client": "^1.1.0" + "@sinch/sdk-client": "^1.2.0" }, "devDependencies": {}, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/verification/src/models/v1/callout-request-event-response-callout-speech/callout-request-event-response-callout-speech.ts b/packages/verification/src/models/v1/callout-request-event-response-callout-speech/callout-request-event-response-callout-speech.ts deleted file mode 100644 index 7d8f290a..00000000 --- a/packages/verification/src/models/v1/callout-request-event-response-callout-speech/callout-request-event-response-callout-speech.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * An object defining various properties for the text-to-speech message. - */ -export interface CalloutRequestEventResponseCalloutSpeech { - - /** Indicates the language that should be used for the text-to-speech message. Currently, only `en-US` is supported. */ - locale?: string; -} diff --git a/packages/verification/src/models/v1/callout-request-event-response-callout-speech/index.ts b/packages/verification/src/models/v1/callout-request-event-response-callout-speech/index.ts deleted file mode 100644 index 6141b6a1..00000000 --- a/packages/verification/src/models/v1/callout-request-event-response-callout-speech/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { CalloutRequestEventResponseCalloutSpeech } from './callout-request-event-response-callout-speech'; diff --git a/packages/verification/src/models/v1/callout-request-event-response-callout/callout-request-event-response-callout.ts b/packages/verification/src/models/v1/callout-request-event-response-callout/callout-request-event-response-callout.ts deleted file mode 100644 index e4f36dd1..00000000 --- a/packages/verification/src/models/v1/callout-request-event-response-callout/callout-request-event-response-callout.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { CalloutRequestEventResponseCalloutSpeech } from '../callout-request-event-response-callout-speech'; - -export interface CalloutRequestEventResponseCallout { - - /** The Phone Call PIN that should be entered by the user. Sinch servers automatically generate PIN codes for Phone Call verification. If you want to set your own code, you can specify it in the response to the Verification Request Event. */ - code?: string; - /** @see CalloutRequestEventResponseCalloutSpeech */ - speech?: CalloutRequestEventResponseCalloutSpeech; -} diff --git a/packages/verification/src/models/v1/callout-request-event-response-callout/index.ts b/packages/verification/src/models/v1/callout-request-event-response-callout/index.ts deleted file mode 100644 index 490198a2..00000000 --- a/packages/verification/src/models/v1/callout-request-event-response-callout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { CalloutRequestEventResponseCallout } from './callout-request-event-response-callout'; diff --git a/packages/verification/src/models/v1/callout-request-event-response/callout-request-event-response.ts b/packages/verification/src/models/v1/callout-request-event-response/callout-request-event-response.ts deleted file mode 100644 index 9b6865b1..00000000 --- a/packages/verification/src/models/v1/callout-request-event-response/callout-request-event-response.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { CalloutRequestEventResponseCallout } from '../callout-request-event-response-callout'; -import { ActionEnum } from '../enums'; - -export interface CalloutRequestEventResponse { - - /** Determines whether the verification can be executed. */ - action?: ActionEnum; - /** @see CalloutRequestEventResponseCallout */ - callout?: CalloutRequestEventResponseCallout; -} diff --git a/packages/verification/src/models/v1/callout-request-event-response/index.ts b/packages/verification/src/models/v1/callout-request-event-response/index.ts deleted file mode 100644 index 0737de65..00000000 --- a/packages/verification/src/models/v1/callout-request-event-response/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { CalloutRequestEventResponse } from './callout-request-event-response'; diff --git a/packages/verification/src/models/v1/callout-verification-report-response/index.ts b/packages/verification/src/models/v1/callout-verification-report-response/index.ts deleted file mode 100644 index 864e706d..00000000 --- a/packages/verification/src/models/v1/callout-verification-report-response/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { CalloutVerificationReportResponse } from './callout-verification-report-response'; diff --git a/packages/verification/src/models/v1/callout-verification-status-response/index.ts b/packages/verification/src/models/v1/callout-verification-status-response/index.ts deleted file mode 100644 index 9c667213..00000000 --- a/packages/verification/src/models/v1/callout-verification-status-response/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './callout-verification-status-response'; diff --git a/packages/verification/src/models/v1/enums.ts b/packages/verification/src/models/v1/enums.ts index 5757da91..0eaefb88 100644 --- a/packages/verification/src/models/v1/enums.ts +++ b/packages/verification/src/models/v1/enums.ts @@ -1,11 +1,3 @@ -export type { TypeEnum as IdentityTypeEnum } from './identity/identity'; -export type { - MethodEnum as VerificationRequestEventMethodEnum, -} from './verification-request-event/verification-request-event'; -export type { - MethodEnum as VerificationResultEventMethodEnum, -} from './verification-result-event/verification-result-event'; - export type ActionEnum = 'allow' | 'deny'; export type VerificationStatusEnum = 'PENDING' diff --git a/packages/verification/src/models/v1/flashcall-request-event-response/index.ts b/packages/verification/src/models/v1/flashcall-request-event-response/index.ts deleted file mode 100644 index 5df2e145..00000000 --- a/packages/verification/src/models/v1/flashcall-request-event-response/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { FlashCallRequestEventResponse } from './flashcall-request-event-response'; diff --git a/packages/verification/src/models/v1/helper.ts b/packages/verification/src/models/v1/helper.ts index 44735e07..1972bc85 100644 --- a/packages/verification/src/models/v1/helper.ts +++ b/packages/verification/src/models/v1/helper.ts @@ -3,16 +3,28 @@ import { ReportCalloutVerificationByIdRequestData, ReportFlashCallVerificationByIdentityRequestData, ReportFlashCallVerificationByIdRequestData, + ReportPhoneCallVerificationByIdentityRequestData, + ReportPhoneCallVerificationByIdRequestData, ReportSmsVerificationByIdentityRequestData, ReportSmsVerificationByIdRequestData, StartCalloutVerificationRequestData, + StartDataVerificationRequestData, StartFlashCallVerificationRequestData, + StartPhoneCallVerificationRequestData, StartSeamlessVerificationRequestData, StartSmsVerificationRequestData, } from './requests'; import { SmsOptions } from './start-verification-request'; export const startVerificationHelper = { + /** + * Builds a request object for starting an SMS verification process. + * + * @param {string} phoneNumber - The phone number to which the verification SMS should be sent. + * @param {string} [reference] - An optional reference identifier used to pass your own reference in the request for tracking purposes. + * @param {SmsOptions} [smsOptions] - Optional parameters for configuring the SMS verification request, with default values assumed for all contained values if not provided. + * @return {StartSmsVerificationRequestData} The constructed SMS verification request data. + */ buildSmsRequest: ( phoneNumber: string, reference?: string, @@ -33,6 +45,45 @@ export const startVerificationHelper = { }, }; }, + /** + * Builds a request object for starting a phone call verification process. + * + * @param {string} phoneNumber - The phone number to which the verification call should be made. + * @param {string} [reference] - An optional reference identifier used to pass your own reference in the request for tracking purposes. + * @param {string} [locale] - An optional language-region identifier to use for the verification call. + * @return {StartPhoneCallVerificationRequestData} The request data object for initiating the phone call verification. + */ + buildPhoneCallRequest: ( + phoneNumber: string, + reference?: string, + locale?: string, + ): StartPhoneCallVerificationRequestData => { + return { + startVerificationWithPhoneCallRequestBody: { + identity: { + type: 'number', + endpoint: phoneNumber, + }, + reference, + ...(locale !== undefined) ? { + phoneCallOptions: { + speech: { + locale, + }, + }, + } : {}, + }, + }; + }, + /** + * Builds a callout request body with the provided phone number, reference, and locale. + * + * @param {string} phoneNumber - The phone number to which the callout will be made. + * @param {string} [reference] - An optional reference identifier for the callout. + * @param {string} [locale] - An optional locale string to specify the language or region for the callout. + * @return {StartCalloutVerificationRequestData} The constructed callout request object. + * @deprecated Use the method buildPhoneCallRequest() instead + */ buildCalloutRequest: ( phoneNumber: string, reference?: string, @@ -55,6 +106,14 @@ export const startVerificationHelper = { }, }; }, + /** + * Builds a request object for starting a flash call verification process. + * + * @param {string} phoneNumber - The phone number to which the flash call verification should be made. + * @param {string} [reference] - An optional reference identifier used to pass your own reference in the request for tracking purposes. + * @param {number} [dialTimeout] - An optional timeout value in seconds for how long to wait for the flash call to be answered. + * @return {StartFlashCallVerificationRequestData} The request data object for initiating the flash call verification. + */ buildFlashCallRequest: ( phoneNumber: string, reference?: string, @@ -75,6 +134,35 @@ export const startVerificationHelper = { }, }; }, + /** + * Builds a request object for initiating a data verification process. + * + * @param {string} phoneNumber - The phone number to be verified. + * @param {string} [reference] - An optional reference identifier used to pass your own reference in the request for tracking purposes. + * @return {StartDataVerificationRequestData} The request data object used to start the data verification. + */ + buildDataRequest: ( + phoneNumber: string, + reference?: string, + ): StartDataVerificationRequestData => { + return { + startDataVerificationRequestBody: { + identity: { + type: 'number', + endpoint: phoneNumber, + }, + reference, + }, + }; + }, + /** + * Builds a seamless verification request body with the provided phone number and optional reference. + * + * @param {string} phoneNumber - The phone number to verify. + * @param {string} [reference] - An optional reference identifier for the verification request. + * @return {StartSeamlessVerificationRequestData} The constructed seamless verification request data. + * @deprecated Use the method buildDataRequest() instead + */ buildSeamlessRequest: ( phoneNumber: string, reference?: string, @@ -91,6 +179,14 @@ export const startVerificationHelper = { }, }; export const reportVerificationByIdHelper = { + /** + * Builds a request object for reporting an SMS verification by its ID. + * + * @param {string} id - The unique identifier for the SMS verification request. + * @param {string} code - The verification code received via SMS. + * @param {string} [cli] - An optional CLI (Caller Line Identification) that can be included in the request. + * @return {ReportSmsVerificationByIdRequestData} The request data object used to report the SMS verification. + */ buildSmsRequest: ( id: string, code: string, @@ -106,6 +202,34 @@ export const reportVerificationByIdHelper = { }, }; }, + /** + * Builds a request object for reporting a phone call verification by its ID. + * + * @param {string} id - The unique identifier for the phone call verification request. + * @param {string} code - The verification code received during the phone call. + * @return {ReportPhoneCallVerificationByIdRequestData} The request data object used to report the phone call verification. + */ + buildPhoneCallRequest: ( + id: string, + code: string, + ): ReportPhoneCallVerificationByIdRequestData => { + return { + id, + reportPhoneCallVerificationByIdRequestBody: { + phoneCall: { + code, + }, + }, + }; + }, + /** + * Builds a request object for reporting a callout verification by its ID. + * + * @param {string} id - The unique identifier for the callout verification request. + * @param {string} code - The verification code received during the callout. + * @return {ReportCalloutVerificationByIdRequestData} The request data object used to report the callout verification. + * @deprecated Use the method buildPhoneCallRequest() instead + */ buildCalloutRequest: ( id: string, code: string, @@ -119,6 +243,13 @@ export const reportVerificationByIdHelper = { }, }; }, + /** + * Builds a request object for reporting a flash call verification by its ID. + * + * @param {string} id - The unique identifier for the flash call verification request. + * @param {string} cli - The CLI (Caller Line Identification) received during the flash call. + * @return {ReportFlashCallVerificationByIdRequestData} The request data object used to report the flash call verification. + */ buildFlashCallRequest: ( id: string, cli: string, @@ -134,6 +265,14 @@ export const reportVerificationByIdHelper = { }, }; export const reportVerificationByIdentityHelper = { + /** + * Builds a request object for reporting an SMS verification by the phone number identity. + * + * @param {string} identity - The phone number for which the verification process has been initiated. + * @param {string} code - The verification code received via SMS. + * @param {string} [cli] - The CLI (Caller Line Identification) that may be used during the verification. + * @return {ReportSmsVerificationByIdentityRequestData} The request data object used to report the SMS verification. + */ buildSmsRequest: ( identity: string, code: string, @@ -149,6 +288,34 @@ export const reportVerificationByIdentityHelper = { }, }; }, + /** + * Builds a request object for reporting a phone call verification by the phone number identity. + * + * @param {string} identity - The phone number for which the verification process has been initiated. + * @param {string} code - The verification code received via the phone call. + * @return {ReportPhoneCallVerificationByIdentityRequestData} The request data object used to report the phone call verification. + */ + buildPhoneCallRequest: ( + identity: string, + code: string, + ): ReportPhoneCallVerificationByIdentityRequestData => { + return { + endpoint: identity, + reportPhoneCallVerificationByIdentityRequestBody: { + phoneCall: { + code, + }, + }, + }; + }, + /** + * Builds a request object for reporting a callout verification by the phone number identity. + * + * @param {string} identity - The phone number for which the callout verification process has been initiated. + * @param {string} code - The verification code received during the callout. + * @return {ReportCalloutVerificationByIdentityRequestData} The request data object used to report the callout verification. + * @deprecated Use the method buildPhoneCallRequest() instead + */ buildCalloutRequest: ( identity: string, code: string, @@ -162,6 +329,13 @@ export const reportVerificationByIdentityHelper = { }, }; }, + /** + * Builds a request object for reporting a flash call verification by the phone number identity. + * + * @param {string} identity - The phone number for which the flash call verification process has been initiated. + * @param {string} cli - The CLI (Caller Line Identification) received during the flash call. + * @return {ReportFlashCallVerificationByIdentityRequestData} The request data object used to report the flash call verification. + */ buildFlashCallRequest: ( identity: string, cli: string, diff --git a/packages/verification/src/models/v1/identity/identity.ts b/packages/verification/src/models/v1/identity/identity.ts index f3f12c37..dd8f731e 100644 --- a/packages/verification/src/models/v1/identity/identity.ts +++ b/packages/verification/src/models/v1/identity/identity.ts @@ -4,12 +4,12 @@ export interface Identity { /** Currently only `number` type is supported. */ - type: TypeEnum; + type: IdentityType; /** For type `number` use an [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537)-compatible phone number. */ endpoint: string; - /** */ - verified?: boolean; } -export type TypeEnum = 'number'; +export type IdentityType = 'number'; +/** @deprecated Use IdentityType instead */ +export type TypeEnum = IdentityType; diff --git a/packages/verification/src/models/v1/identity/index.ts b/packages/verification/src/models/v1/identity/index.ts index 3474d3a3..74923347 100644 --- a/packages/verification/src/models/v1/identity/index.ts +++ b/packages/verification/src/models/v1/identity/index.ts @@ -1 +1 @@ -export type { Identity } from './identity'; +export type { Identity, IdentityType, TypeEnum } from './identity'; diff --git a/packages/verification/src/models/v1/index.ts b/packages/verification/src/models/v1/index.ts index 1892ec81..f2389bda 100644 --- a/packages/verification/src/models/v1/index.ts +++ b/packages/verification/src/models/v1/index.ts @@ -1,32 +1,34 @@ -export * from './callout-request-event-response'; -export * from './callout-request-event-response-callout'; -export * from './callout-request-event-response-callout-speech'; -export * from './callout-verification-report-response'; -export * from './callout-verification-status-response'; -export * from './flashcall-request-event-response'; +// Models associated to API requests +export * from './requests'; +export * from './start-verification-request'; +export * from './verification-report-request'; +// Models associated to SMS verification workflow +export * from './start-sms-verification-response'; +export * from './sms-verification-report-response'; +export * from './sms-verification-status-response'; +// Models associated to PhoneCall verification workflow +export * from './start-phonecall-verification-response'; +export * from './phonecall-verification-report-response'; +export * from './phonecall-verification-status-response'; +// Models associated to Flashcall verification workflow +export * from './start-flashcall-verification-response'; export * from './flashcall-verification-report-response'; export * from './flashcall-verification-status-response'; -export * from './verification-report-response-price'; +// Models associated to Data verification workflow +export * from './start-data-verification-response'; +// Wrapper for the various types of Verification Status Response +export * from './verification-status-response'; +// Models associated to callback events +export * from './mod-callbacks'; +// Common models export * from './identity'; -export * from './start-verification-request'; export * from './links-object'; export * from './price'; -export * from './start-flashcall-verification-response'; -export * from './start-callout-verification-response'; -export * from './start-seamless-verification-response'; -export * from './start-sms-verification-response'; -export * from './sms-request-event-response'; -export * from './sms-verification-report-response'; -export * from './sms-verification-status-response'; export * from './verification-price-call'; export * from './verification-price-sms'; -export * from './verification-report-request'; -export * from './verification-status-response'; -export * from './verification-request-event'; -export * from './verification-request-event-response'; -export * from './verification-result-event'; -export * from './verification-result-event-response'; +// Error model export * from './verification-error'; +// Enums export * from './enums'; +// Helper methods export * from './helper'; -export * from './requests'; diff --git a/packages/verification/src/models/v1/flashcall-request-event-response/flashcall-request-event-response.ts b/packages/verification/src/models/v1/mod-callbacks/flashcall-request-event-response/flashcall-request-event-response.ts similarity index 79% rename from packages/verification/src/models/v1/flashcall-request-event-response/flashcall-request-event-response.ts rename to packages/verification/src/models/v1/mod-callbacks/flashcall-request-event-response/flashcall-request-event-response.ts index 8d266be6..645d2f91 100644 --- a/packages/verification/src/models/v1/flashcall-request-event-response/flashcall-request-event-response.ts +++ b/packages/verification/src/models/v1/mod-callbacks/flashcall-request-event-response/flashcall-request-event-response.ts @@ -1,15 +1,16 @@ -import { ActionEnum } from '../enums'; +import { ActionEnum } from '../../enums'; export interface FlashCallRequestEventResponse { - /** Determines whether the verification can be executed. */ action?: ActionEnum; - /** @see FlashCallContent */ - flashCall?: FlashCallContent; + /** @see FlashCallProperties */ + flashCall?: FlashCallProperties; } -interface FlashCallContent { +/** @deprecated Use FlashCallProperties instead */ +export type FlashCallContent = FlashCallProperties; +export interface FlashCallProperties { /** The phone number that will be displayed to the user when the flashcall is received on the user\'s phone. By default, the Sinch dashboard will randomly select the CLI that will be displayed during a flashcall from a pool of numbers. If you want to set your own CLI, you can specify it in the response to the Verification Request Event. */ cli?: string; /** The maximum time that a flashcall verification will be active and can be completed. If the phone number hasn\'t been verified successfully during this time, then the verification request will fail. By default, the Sinch dashboard will automatically optimize dial time out during a flashcall. If you want to set your own dial time out for the flashcall, you can specify it in the response to the Verification Request Event. */ diff --git a/packages/verification/src/models/v1/mod-callbacks/flashcall-request-event-response/index.ts b/packages/verification/src/models/v1/mod-callbacks/flashcall-request-event-response/index.ts new file mode 100644 index 00000000..47bf2584 --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/flashcall-request-event-response/index.ts @@ -0,0 +1,5 @@ +export type { + FlashCallRequestEventResponse, + FlashCallContent, + FlashCallProperties, +} from './flashcall-request-event-response'; diff --git a/packages/verification/src/models/v1/mod-callbacks/index.ts b/packages/verification/src/models/v1/mod-callbacks/index.ts new file mode 100644 index 00000000..7a45a655 --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/index.ts @@ -0,0 +1,11 @@ +export * from './verification-callback-event'; +// 'Verification Request Event' received from Sinch server +export * from './verification-request-event'; +// Response to send to Sinch server for a 'Verification Request Event' +export * from './phonecall-request-event-response'; +export * from './flashcall-request-event-response'; +export * from './sms-request-event-response'; +export * from './verification-request-event-response'; +// 'Verification Result Event' received from Sinch server (no response data needed) +export * from './verification-result-event'; +export * from './verification-result-event-response'; diff --git a/packages/verification/src/models/v1/mod-callbacks/phonecall-request-event-response/index.ts b/packages/verification/src/models/v1/mod-callbacks/phonecall-request-event-response/index.ts new file mode 100644 index 00000000..c8b54dd0 --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/phonecall-request-event-response/index.ts @@ -0,0 +1,7 @@ +export type { + CalloutRequestEventResponse, + PhoneCallRequestEventResponse, + CalloutProperties, + PhoneCallProperties, + SpeechProperties, +} from './phonecall-request-event-response'; diff --git a/packages/verification/src/models/v1/mod-callbacks/phonecall-request-event-response/phonecall-request-event-response.ts b/packages/verification/src/models/v1/mod-callbacks/phonecall-request-event-response/phonecall-request-event-response.ts new file mode 100644 index 00000000..4d451644 --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/phonecall-request-event-response/phonecall-request-event-response.ts @@ -0,0 +1,29 @@ +import { ActionEnum } from '../../enums'; + +/** @deprecated Use PhoneCallRequestEventResponse instead */ +export type CalloutRequestEventResponse = PhoneCallRequestEventResponse; + +export interface PhoneCallRequestEventResponse { + /** Determines whether the verification can be executed. */ + action?: ActionEnum; + /** @see PhoneCallProperties */ + callout?: PhoneCallProperties; +} + +/** @deprecated Use PhoneCallProperties instead */ +export type CalloutProperties = PhoneCallProperties; + +export interface PhoneCallProperties { + /** The Phone Call PIN that should be entered by the user. Sinch servers automatically generate PIN codes for Phone Call verification. If you want to set your own code, you can specify it in the response to the Verification Request Event. */ + code?: string; + /** @see SpeechProperties */ + speech?: SpeechProperties; +} + +/** + * An object defining various properties for the text-to-speech message. + */ +export interface SpeechProperties { + /** Indicates the language that should be used for the text-to-speech message. Currently, only `en-US` is supported. */ + locale?: string; +} diff --git a/packages/verification/src/models/v1/mod-callbacks/sms-request-event-response/index.ts b/packages/verification/src/models/v1/mod-callbacks/sms-request-event-response/index.ts new file mode 100644 index 00000000..59f81aa6 --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/sms-request-event-response/index.ts @@ -0,0 +1,6 @@ +export type { + SMSRequestEventResponse, + SmsRequestEventResponse, + SmsContent, + SmsProperties, +} from './sms-request-event-response'; diff --git a/packages/verification/src/models/v1/sms-request-event-response/sms-request-event-response.ts b/packages/verification/src/models/v1/mod-callbacks/sms-request-event-response/sms-request-event-response.ts similarity index 51% rename from packages/verification/src/models/v1/sms-request-event-response/sms-request-event-response.ts rename to packages/verification/src/models/v1/mod-callbacks/sms-request-event-response/sms-request-event-response.ts index 0d1978bc..a6cc8294 100644 --- a/packages/verification/src/models/v1/sms-request-event-response/sms-request-event-response.ts +++ b/packages/verification/src/models/v1/mod-callbacks/sms-request-event-response/sms-request-event-response.ts @@ -1,17 +1,21 @@ -import { ActionEnum } from '../enums'; - -export interface SMSRequestEventResponse { +import { ActionEnum } from '../../enums'; +export interface SmsRequestEventResponse { /** Determines whether the verification can be executed. */ action?: ActionEnum; - /** @see SmsContent */ - sms?: SmsContent; + /** @see SmsProperties */ + sms?: SmsProperties; } -interface SmsContent { - +export interface SmsProperties { /** The SMS PIN that should be used. By default, the Sinch dashboard will automatically generate PIN codes for SMS verification. If you want to set your own PIN, you can specify it in the response to the Verification Request Event. */ code?: string; /** List of strings */ acceptLanguage?: string[]; } + +/** @deprecated Use SmsRequestEventResponse instead */ +export type SMSRequestEventResponse = SmsRequestEventResponse; + +/** @deprecated Use SmsProperties instead */ +export type SmsContent = SmsProperties; diff --git a/packages/verification/src/models/v1/mod-callbacks/verification-callback-event.ts b/packages/verification/src/models/v1/mod-callbacks/verification-callback-event.ts new file mode 100644 index 00000000..2ce27cad --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/verification-callback-event.ts @@ -0,0 +1,4 @@ +import { VerificationRequestEvent } from './verification-request-event'; +import { VerificationResultEvent } from './verification-result-event'; + +export type VerificationCallbackEvent = VerificationRequestEvent | VerificationResultEvent; diff --git a/packages/verification/src/models/v1/verification-request-event-response/index.ts b/packages/verification/src/models/v1/mod-callbacks/verification-request-event-response/index.ts similarity index 100% rename from packages/verification/src/models/v1/verification-request-event-response/index.ts rename to packages/verification/src/models/v1/mod-callbacks/verification-request-event-response/index.ts diff --git a/packages/verification/src/models/v1/mod-callbacks/verification-request-event-response/verification-request-event-response.ts b/packages/verification/src/models/v1/mod-callbacks/verification-request-event-response/verification-request-event-response.ts new file mode 100644 index 00000000..fe380dd7 --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/verification-request-event-response/verification-request-event-response.ts @@ -0,0 +1,7 @@ +import { SmsRequestEventResponse } from '../sms-request-event-response'; +import { FlashCallRequestEventResponse } from '../flashcall-request-event-response'; +import { CalloutRequestEventResponse } from '../phonecall-request-event-response'; + +export type VerificationRequestEventResponse = SmsRequestEventResponse + | FlashCallRequestEventResponse + | CalloutRequestEventResponse; diff --git a/packages/verification/src/models/v1/mod-callbacks/verification-request-event/index.ts b/packages/verification/src/models/v1/mod-callbacks/verification-request-event/index.ts new file mode 100644 index 00000000..7b8a4b6f --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/verification-request-event/index.ts @@ -0,0 +1 @@ +export type { VerificationRequestEvent, MethodEnum } from './verification-request-event'; diff --git a/packages/verification/src/models/v1/verification-request-event/verification-request-event.ts b/packages/verification/src/models/v1/mod-callbacks/verification-request-event/verification-request-event.ts similarity index 90% rename from packages/verification/src/models/v1/verification-request-event/verification-request-event.ts rename to packages/verification/src/models/v1/mod-callbacks/verification-request-event/verification-request-event.ts index da0dc1b0..198ec43f 100644 --- a/packages/verification/src/models/v1/verification-request-event/verification-request-event.ts +++ b/packages/verification/src/models/v1/mod-callbacks/verification-request-event/verification-request-event.ts @@ -1,5 +1,5 @@ -import { Identity } from '../identity'; -import { Price } from '../price'; +import { Identity } from '../../identity'; +import { Price } from '../../price'; export interface VerificationRequestEvent { diff --git a/packages/verification/src/models/v1/verification-result-event-response/index.ts b/packages/verification/src/models/v1/mod-callbacks/verification-result-event-response/index.ts similarity index 100% rename from packages/verification/src/models/v1/verification-result-event-response/index.ts rename to packages/verification/src/models/v1/mod-callbacks/verification-result-event-response/index.ts diff --git a/packages/verification/src/models/v1/mod-callbacks/verification-result-event-response/verification-result-event-response.ts b/packages/verification/src/models/v1/mod-callbacks/verification-result-event-response/verification-result-event-response.ts new file mode 100644 index 00000000..8c710ddb --- /dev/null +++ b/packages/verification/src/models/v1/mod-callbacks/verification-result-event-response/verification-result-event-response.ts @@ -0,0 +1,7 @@ +import { SmsRequestEventResponse } from '../sms-request-event-response'; +import { FlashCallRequestEventResponse } from '../flashcall-request-event-response'; +import { CalloutRequestEventResponse } from '../phonecall-request-event-response'; + +export type VerificationResultEventResponse = SmsRequestEventResponse + | FlashCallRequestEventResponse + | CalloutRequestEventResponse; diff --git a/packages/verification/src/models/v1/verification-result-event/index.ts b/packages/verification/src/models/v1/mod-callbacks/verification-result-event/index.ts similarity index 100% rename from packages/verification/src/models/v1/verification-result-event/index.ts rename to packages/verification/src/models/v1/mod-callbacks/verification-result-event/index.ts diff --git a/packages/verification/src/models/v1/verification-result-event/verification-result-event.ts b/packages/verification/src/models/v1/mod-callbacks/verification-result-event/verification-result-event.ts similarity index 94% rename from packages/verification/src/models/v1/verification-result-event/verification-result-event.ts rename to packages/verification/src/models/v1/mod-callbacks/verification-result-event/verification-result-event.ts index 156a7474..a3df263e 100644 --- a/packages/verification/src/models/v1/verification-result-event/verification-result-event.ts +++ b/packages/verification/src/models/v1/mod-callbacks/verification-result-event/verification-result-event.ts @@ -1,5 +1,5 @@ -import { Identity } from '../identity'; -import { ReasonEnum, SourceEnum, VerificationStatusEnum } from '../enums'; +import { Identity } from '../../identity'; +import { ReasonEnum, SourceEnum, VerificationStatusEnum } from '../../enums'; export interface VerificationResultEvent { diff --git a/packages/verification/src/models/v1/phonecall-verification-report-response/index.ts b/packages/verification/src/models/v1/phonecall-verification-report-response/index.ts new file mode 100644 index 00000000..d5f8c4e5 --- /dev/null +++ b/packages/verification/src/models/v1/phonecall-verification-report-response/index.ts @@ -0,0 +1,4 @@ +export type { + PhoneCallVerificationReportResponse, + CalloutVerificationReportResponse, +} from './phonecall-verification-report-response'; diff --git a/packages/verification/src/models/v1/callout-verification-report-response/callout-verification-report-response.ts b/packages/verification/src/models/v1/phonecall-verification-report-response/phonecall-verification-report-response.ts similarity index 80% rename from packages/verification/src/models/v1/callout-verification-report-response/callout-verification-report-response.ts rename to packages/verification/src/models/v1/phonecall-verification-report-response/phonecall-verification-report-response.ts index 7e1f6b8c..3594a542 100644 --- a/packages/verification/src/models/v1/callout-verification-report-response/callout-verification-report-response.ts +++ b/packages/verification/src/models/v1/phonecall-verification-report-response/phonecall-verification-report-response.ts @@ -1,8 +1,10 @@ import { ReasonEnum, SourceEnum, VerificationStatusEnum } from '../enums'; import { Identity } from '../identity'; -export interface CalloutVerificationReportResponse { +/** @deprecated Use PhoneCallVerificationReportResponse instead */ +export type CalloutVerificationReportResponse = PhoneCallVerificationReportResponse; +export interface PhoneCallVerificationReportResponse { /** The unique ID of the verification request. */ id?: string; /** The method of the verification request. This will always be `callout`. */ diff --git a/packages/verification/src/models/v1/phonecall-verification-status-response/index.ts b/packages/verification/src/models/v1/phonecall-verification-status-response/index.ts new file mode 100644 index 00000000..ba3cccd5 --- /dev/null +++ b/packages/verification/src/models/v1/phonecall-verification-status-response/index.ts @@ -0,0 +1,4 @@ +export type { + PhoneCallVerificationStatusResponse, + CalloutVerificationStatusResponse, +} from './phonecall-verification-status-response'; diff --git a/packages/verification/src/models/v1/callout-verification-status-response/callout-verification-status-response.ts b/packages/verification/src/models/v1/phonecall-verification-status-response/phonecall-verification-status-response.ts similarity index 87% rename from packages/verification/src/models/v1/callout-verification-status-response/callout-verification-status-response.ts rename to packages/verification/src/models/v1/phonecall-verification-status-response/phonecall-verification-status-response.ts index d7722f45..c36fa29e 100644 --- a/packages/verification/src/models/v1/callout-verification-status-response/callout-verification-status-response.ts +++ b/packages/verification/src/models/v1/phonecall-verification-status-response/phonecall-verification-status-response.ts @@ -2,7 +2,10 @@ import { CallResult, ReasonEnum, VerificationStatusEnum } from '../enums'; import { Identity } from '../identity'; import { VerificationPriceCall } from '../verification-price-call'; -export interface CalloutVerificationStatusResponse { +/** @deprecated Use PhoneCallVerificationStatusResponse instead */ +export type CalloutVerificationStatusResponse = PhoneCallVerificationStatusResponse; + +export interface PhoneCallVerificationStatusResponse { /** The unique ID of the verification request. */ id?: string; /** The method of the verification request. This will always be `callout`. */ diff --git a/packages/verification/src/models/v1/requests/verification-status/verification-status-request-data.ts b/packages/verification/src/models/v1/requests/verification-status/verification-status-request-data.ts index 69c09aed..b0f9df92 100644 --- a/packages/verification/src/models/v1/requests/verification-status/verification-status-request-data.ts +++ b/packages/verification/src/models/v1/requests/verification-status/verification-status-request-data.ts @@ -6,7 +6,8 @@ export interface VerificationStatusByIdentityRequestData { /** For type `number` use a [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537)-compatible phone number. */ 'endpoint': string; /** The method of the verification. */ - 'method': 'sms' | 'callout' | 'flashCall'; + // TODO v2.0 - Remove 'callout' and 'flashCall' options + 'method': 'sms' | 'callout' | 'phonecall' | 'flashCall' | 'flashcall'; } export interface VerificationStatusByReferenceRequestData { /** The custom reference of the verification. */ diff --git a/packages/verification/src/models/v1/requests/verifications/verifications-request-data.ts b/packages/verification/src/models/v1/requests/verifications/verifications-request-data.ts index 2c8e064a..21a79617 100644 --- a/packages/verification/src/models/v1/requests/verifications/verifications-request-data.ts +++ b/packages/verification/src/models/v1/requests/verifications/verifications-request-data.ts @@ -1,12 +1,15 @@ import { CalloutVerificationReportRequest, FlashCallVerificationReportRequest, + PhoneCallVerificationReportRequest, SmsVerificationReportRequest, } from '../../verification-report-request'; import { + StartDataVerification, StartSeamlessVerification, StartVerificationWithCallout, StartVerificationWithFlashCall, + StartVerificationWithPhoneCall, StartVerificationWithSms, } from '../../start-verification-request'; @@ -25,6 +28,12 @@ export interface ReportFlashCallVerificationByIdRequestData extends ReportVerifi 'reportFlashCallVerificationByIdRequestBody': FlashCallVerificationReportRequest; } +export interface ReportPhoneCallVerificationByIdRequestData extends ReportVerificationByIdRequestDataBase { + /** Request body to report a verification started with a phone call by its ID */ + 'reportPhoneCallVerificationByIdRequestBody': PhoneCallVerificationReportRequest; +} + +/** @deprecated Use ReportPhoneCallVerificationByIdRequestData instead */ export interface ReportCalloutVerificationByIdRequestData extends ReportVerificationByIdRequestDataBase { /** Request body to report a verification started with a callout by its ID */ 'reportCalloutVerificationByIdRequestBody': CalloutVerificationReportRequest; @@ -45,6 +54,12 @@ export interface ReportFlashCallVerificationByIdentityRequestData extends Report 'reportFlashCallVerificationByIdentityRequestBody': FlashCallVerificationReportRequest; } +export interface ReportPhoneCallVerificationByIdentityRequestData extends ReportVerificationByIdentityRequestDataBase { + /** Request body to report a verification started with a callout by its identity */ + 'reportPhoneCallVerificationByIdentityRequestBody': PhoneCallVerificationReportRequest; +} + +/** @deprecated Use ReportPhoneCallVerificationByIdentityRequestData instead */ export interface ReportCalloutVerificationByIdentityRequestData extends ReportVerificationByIdentityRequestDataBase { /** Request body to report a verification started with a callout by its identity */ 'reportCalloutVerificationByIdentityRequestBody': CalloutVerificationReportRequest; @@ -60,11 +75,23 @@ export interface StartFlashCallVerificationRequestData { 'startVerificationWithFlashCallRequestBody': StartVerificationWithFlashCall; } +export interface StartPhoneCallVerificationRequestData { + /** Request body to start a verification with a phone call */ + 'startVerificationWithPhoneCallRequestBody': StartVerificationWithPhoneCall; +} + +/** @deprecated Use StartPhoneCallVerificationRequestData instead */ export interface StartCalloutVerificationRequestData { /** Request body to start a verification with a callout */ 'startVerificationWithCalloutRequestBody': StartVerificationWithCallout; } +export interface StartDataVerificationRequestData { + /** Request body to start a data verification */ + 'startDataVerificationRequestBody': StartDataVerification; +} + +/** @deprecated Use StartDataVerificationRequestData instead */ export interface StartSeamlessVerificationRequestData { /** Request body to start a seamless verification */ 'startSeamlessVerificationRequestBody': StartSeamlessVerification; diff --git a/packages/verification/src/models/v1/sms-request-event-response/index.ts b/packages/verification/src/models/v1/sms-request-event-response/index.ts deleted file mode 100644 index 6e98db9a..00000000 --- a/packages/verification/src/models/v1/sms-request-event-response/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { SMSRequestEventResponse } from './sms-request-event-response'; diff --git a/packages/verification/src/models/v1/sms-verification-report-response/index.ts b/packages/verification/src/models/v1/sms-verification-report-response/index.ts index d213363f..c043cf9c 100644 --- a/packages/verification/src/models/v1/sms-verification-report-response/index.ts +++ b/packages/verification/src/models/v1/sms-verification-report-response/index.ts @@ -1 +1 @@ -export type { SMSVerificationReportResponse } from './sms-verification-report-response'; +export type { SMSVerificationReportResponse, SmsVerificationReportResponse } from './sms-verification-report-response'; diff --git a/packages/verification/src/models/v1/sms-verification-report-response/sms-verification-report-response.ts b/packages/verification/src/models/v1/sms-verification-report-response/sms-verification-report-response.ts index 420c4ccc..9cf18073 100644 --- a/packages/verification/src/models/v1/sms-verification-report-response/sms-verification-report-response.ts +++ b/packages/verification/src/models/v1/sms-verification-report-response/sms-verification-report-response.ts @@ -1,7 +1,7 @@ import { ReasonEnum, SourceEnum, VerificationStatusEnum } from '../enums'; import { Identity } from '../identity'; -export interface SMSVerificationReportResponse { +export interface SmsVerificationReportResponse { /** The unique ID of the verification request. */ id?: string; @@ -18,3 +18,6 @@ export interface SMSVerificationReportResponse { /** @see Identity */ identity?: Identity; } + +/** @deprecated Use SmsVerificationReportResponse instead */ +export type SMSVerificationReportResponse = SmsVerificationReportResponse; diff --git a/packages/verification/src/models/v1/sms-verification-status-response/index.ts b/packages/verification/src/models/v1/sms-verification-status-response/index.ts index 9d1dfb90..cd232656 100644 --- a/packages/verification/src/models/v1/sms-verification-status-response/index.ts +++ b/packages/verification/src/models/v1/sms-verification-status-response/index.ts @@ -1 +1 @@ -export * from './sms-verification-status-response'; +export type { SMSVerificationStatusResponse, SmsVerificationStatusResponse } from './sms-verification-status-response'; diff --git a/packages/verification/src/models/v1/sms-verification-status-response/sms-verification-status-response.ts b/packages/verification/src/models/v1/sms-verification-status-response/sms-verification-status-response.ts index 9718eda7..21c457d9 100644 --- a/packages/verification/src/models/v1/sms-verification-status-response/sms-verification-status-response.ts +++ b/packages/verification/src/models/v1/sms-verification-status-response/sms-verification-status-response.ts @@ -1,8 +1,8 @@ import { ReasonEnum, SourceEnum, VerificationStatusEnum } from '../enums'; import { Identity } from '../identity'; -import { VerificationPriceSMS } from '../verification-price-sms'; +import { VerificationPriceSms } from '../verification-price-sms'; -export interface SMSVerificationStatusResponse { +export interface SmsVerificationStatusResponse { /** The unique ID of the verification request. */ id?: string; @@ -15,7 +15,7 @@ export interface SMSVerificationStatusResponse { /** The reference ID that was optionally passed together with the verification request. */ reference?: string; /** Prices associated with this verification */ - price?: VerificationPriceSMS; + price?: VerificationPriceSms; /** @see Identity */ identity?: Identity; /** The ID of the country to which the verification was sent. */ @@ -25,3 +25,6 @@ export interface SMSVerificationStatusResponse { /** Free text that the client is sending, used to show if the call/SMS was intercepted or not. */ source?: SourceEnum; } + +/** @deprecated Use SmsVerificationStatusResponse instead */ +export type SMSVerificationStatusResponse = SmsVerificationStatusResponse; diff --git a/packages/verification/src/models/v1/start-callout-verification-response/index.ts b/packages/verification/src/models/v1/start-callout-verification-response/index.ts deleted file mode 100644 index 1700cf35..00000000 --- a/packages/verification/src/models/v1/start-callout-verification-response/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { StartCalloutVerificationResponse } from './start-callout-verification-response'; diff --git a/packages/verification/src/models/v1/start-data-verification-response/index.ts b/packages/verification/src/models/v1/start-data-verification-response/index.ts new file mode 100644 index 00000000..3d6f1c8f --- /dev/null +++ b/packages/verification/src/models/v1/start-data-verification-response/index.ts @@ -0,0 +1,4 @@ +export type { + StartSeamlessVerificationResponse, + StartDataVerificationResponse, +} from './start-data-verification-response'; diff --git a/packages/verification/src/models/v1/start-seamless-verification-response/start-seamless-verification-response.ts b/packages/verification/src/models/v1/start-data-verification-response/start-data-verification-response.ts similarity index 71% rename from packages/verification/src/models/v1/start-seamless-verification-response/start-seamless-verification-response.ts rename to packages/verification/src/models/v1/start-data-verification-response/start-data-verification-response.ts index 8adc5156..a3a220b6 100644 --- a/packages/verification/src/models/v1/start-seamless-verification-response/start-seamless-verification-response.ts +++ b/packages/verification/src/models/v1/start-data-verification-response/start-data-verification-response.ts @@ -1,7 +1,9 @@ import { LinksObject } from '../links-object'; -export interface StartSeamlessVerificationResponse { +/** @deprecated Use StartDataVerificationResponse instead */ +export type StartSeamlessVerificationResponse = StartDataVerificationResponse; +export interface StartDataVerificationResponse { /** Verification identifier used to query for status. */ id: string; /** The value of the method used for the Verification. For Data Verifications, this will always be `seamless`. */ @@ -13,7 +15,6 @@ export interface StartSeamlessVerificationResponse { } interface SeamlessContent { - /** The target URI. */ targetUri?: string; } diff --git a/packages/verification/src/models/v1/start-phonecall-verification-response/index.ts b/packages/verification/src/models/v1/start-phonecall-verification-response/index.ts new file mode 100644 index 00000000..b282e686 --- /dev/null +++ b/packages/verification/src/models/v1/start-phonecall-verification-response/index.ts @@ -0,0 +1,4 @@ +export type { + StartCalloutVerificationResponse, + StartPhoneCallVerificationResponse, +} from './start-phonecall-verification-response'; diff --git a/packages/verification/src/models/v1/start-callout-verification-response/start-callout-verification-response.ts b/packages/verification/src/models/v1/start-phonecall-verification-response/start-phonecall-verification-response.ts similarity index 61% rename from packages/verification/src/models/v1/start-callout-verification-response/start-callout-verification-response.ts rename to packages/verification/src/models/v1/start-phonecall-verification-response/start-phonecall-verification-response.ts index ced322ae..7ed1fec5 100644 --- a/packages/verification/src/models/v1/start-callout-verification-response/start-callout-verification-response.ts +++ b/packages/verification/src/models/v1/start-phonecall-verification-response/start-phonecall-verification-response.ts @@ -1,7 +1,9 @@ import { LinksObject } from '../links-object'; -export interface StartCalloutVerificationResponse { +/** @deprecated Use StartPhoneCallVerificationResponse instead */ +export type StartCalloutVerificationResponse = StartPhoneCallVerificationResponse; +export interface StartPhoneCallVerificationResponse { /** Verification identifier used to query for status. */ id?: string; /** The value of the method used for the Verification. For Phone Call Verifications, this will always be `callout`. */ diff --git a/packages/verification/src/models/v1/start-seamless-verification-response/index.ts b/packages/verification/src/models/v1/start-seamless-verification-response/index.ts deleted file mode 100644 index 45916e62..00000000 --- a/packages/verification/src/models/v1/start-seamless-verification-response/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { StartSeamlessVerificationResponse } from './start-seamless-verification-response'; diff --git a/packages/verification/src/models/v1/start-verification-request/index.ts b/packages/verification/src/models/v1/start-verification-request/index.ts index 8cd6484e..90fb96f1 100644 --- a/packages/verification/src/models/v1/start-verification-request/index.ts +++ b/packages/verification/src/models/v1/start-verification-request/index.ts @@ -2,11 +2,16 @@ export type { StartVerificationBase, StartVerificationWithSms, StartVerificationWithFlashCall, + StartVerificationWithPhoneCall, + StartVerificationWithPhoneCallServerModel, StartVerificationWithCallout, + StartDataVerification, StartSeamlessVerification, SmsOptions, CodeType, + PhoneCallOptions, CalloutOptions, + PhoneCallOptionsSpeech, CalloutOptionsSpeech, FlashCallOptions, } from './start-verification-request'; diff --git a/packages/verification/src/models/v1/start-verification-request/start-verification-request.ts b/packages/verification/src/models/v1/start-verification-request/start-verification-request.ts index 87a7b652..5f4d4346 100644 --- a/packages/verification/src/models/v1/start-verification-request/start-verification-request.ts +++ b/packages/verification/src/models/v1/start-verification-request/start-verification-request.ts @@ -10,12 +10,26 @@ export interface StartVerificationWithFlashCall extends StartVerificationBase { flashCallOptions?: FlashCallOptions; } +export interface StartVerificationWithPhoneCall extends StartVerificationBase { + /** @see PhoneCallOptions */ + phoneCallOptions?: PhoneCallOptions; +} + +export interface StartVerificationWithPhoneCallServerModel extends StartVerificationBase { + /** @see PhoneCallOptions */ + calloutOptions?: PhoneCallOptions; +} + +/** @deprecated Use StartVerificationWithPhoneCall instead */ export interface StartVerificationWithCallout extends StartVerificationBase { /** @see CalloutOptions */ calloutOptions?: CalloutOptions; } -export interface StartSeamlessVerification extends StartVerificationBase {} +export interface StartDataVerification extends StartVerificationBase {} + +/** @deprecated Use StartDataVerification instead */ +export type StartSeamlessVerification = StartDataVerification; export interface StartVerificationBase { /** @see Identity */ @@ -36,6 +50,8 @@ export interface SmsOptions { codeType?: CodeType; /** The SMS template must include a placeholder `{{CODE}}` where the verification code will be inserted, and it can otherwise be customized as desired. */ template?: string; + /** A `language-region` identifier according to [IANA](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry). Only a subset of those identifiers is accepted. */ + locale?: string; } export type CodeType = 'Numeric' | 'Alpha' | 'Alphanumeric'; @@ -48,18 +64,23 @@ export interface FlashCallOptions { dialTimeout?: number; } +/** @deprecated Use PhoneCallOptions instead */ +export type CalloutOptions = PhoneCallOptions; + /** * An optional object for Phone Call Verification, with default values assumed for all contained values if not provided. */ -export interface CalloutOptions { - /** @see CalloutOptionsSpeech */ - speech?: CalloutOptionsSpeech; +export interface PhoneCallOptions { + /** @see PhoneCallOptionsSpeech */ + speech?: PhoneCallOptionsSpeech; } +/** @deprecated Use PhoneCallOptionsSpeech instead */ +export type CalloutOptionsSpeech = PhoneCallOptionsSpeech; /** * Text-To-Speech engine settings */ -export interface CalloutOptionsSpeech { +export interface PhoneCallOptionsSpeech { /** A `language-region` identifier according to [IANA](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry). Only a subset of those identifiers is accepted. */ locale?: string; } diff --git a/packages/verification/src/models/v1/verification-price-sms/verification-price-sms.ts b/packages/verification/src/models/v1/verification-price-sms/verification-price-sms.ts index 8e75a175..1b601271 100644 --- a/packages/verification/src/models/v1/verification-price-sms/verification-price-sms.ts +++ b/packages/verification/src/models/v1/verification-price-sms/verification-price-sms.ts @@ -3,8 +3,11 @@ import { Price } from '../price'; /** * Prices associated with this verification */ -export interface VerificationPriceSMS { +export interface VerificationPriceSms { /** The maximum price charged for this verification process. This property will appear in the body of the response with a delay. It will become visible only when the verification status is other than PENDING. */ verificationPrice?: Price; } + +/** @deprecated Use VerificationPriceSms instead */ +export type VerificationPriceSMS = VerificationPriceSms; diff --git a/packages/verification/src/models/v1/verification-report-request/index.ts b/packages/verification/src/models/v1/verification-report-request/index.ts index adcddc04..d8974f5d 100644 --- a/packages/verification/src/models/v1/verification-report-request/index.ts +++ b/packages/verification/src/models/v1/verification-report-request/index.ts @@ -1,5 +1,7 @@ export type { SmsVerificationReportRequest, FlashCallVerificationReportRequest, + PhoneCallVerificationReportRequest, + PhoneCallVerificationReportRequestServerModel, CalloutVerificationReportRequest, } from './verification-report-request'; diff --git a/packages/verification/src/models/v1/verification-report-request/verification-report-request.ts b/packages/verification/src/models/v1/verification-report-request/verification-report-request.ts index e0b79ff0..0ed08e86 100644 --- a/packages/verification/src/models/v1/verification-report-request/verification-report-request.ts +++ b/packages/verification/src/models/v1/verification-report-request/verification-report-request.ts @@ -20,12 +20,26 @@ interface FlashCallContent { cli: string; } +export interface PhoneCallVerificationReportRequest { + /** A configuration object containing settings specific to Phone Call verifications */ + phoneCall: PhoneCallContent; +} + +export interface PhoneCallVerificationReportRequestServerModel { + /** A configuration object containing settings specific to Phone Call verifications */ + callout: PhoneCallContent; +} + +/** @deprecated Use PhoneCallVerificationReportRequest instead */ export interface CalloutVerificationReportRequest { /** A configuration object containing settings specific to Phone Call verifications */ callout: CalloutContent; } -interface CalloutContent { +interface PhoneCallContent { /** The code which was received by the user submitting the Phone Call verification. */ code?: string; } + +/** @deprecated Use PhoneCallContent instead */ +type CalloutContent = PhoneCallContent; diff --git a/packages/verification/src/models/v1/verification-report-response-price/index.ts b/packages/verification/src/models/v1/verification-report-response-price/index.ts deleted file mode 100644 index 5eff275a..00000000 --- a/packages/verification/src/models/v1/verification-report-response-price/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type { - VerificationReportResponsePrice, -} from './verification-report-response-price'; diff --git a/packages/verification/src/models/v1/verification-report-response-price/verification-report-response-price.ts b/packages/verification/src/models/v1/verification-report-response-price/verification-report-response-price.ts deleted file mode 100644 index b319daa6..00000000 --- a/packages/verification/src/models/v1/verification-report-response-price/verification-report-response-price.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Price } from '../price'; - -/** - * Prices associated with this verification - */ -export interface VerificationReportResponsePrice { - - /** The maximum price charged for this verification process. This property will appear in the body of the response with a delay. It will become visible only when the verification status is other than PENDING. */ - verificationPrice?: Price; - /** The maximum cost of the call made during this verification process. Present only when termination debiting is enabled (disabled by default). This property will appear in the body of the response with a delay. It will become visible only after the call is completed, when its cost is known to Sinch. */ - terminationPrice?: Price; - /** The time of the call for which the fee was charged. Present only when termination debiting is enabled (disabled by default). Depending on the type of rounding used, the value is the actual call time rounded to the nearest second, minute or other value. */ - billableDuration?: number; -} - -/** - * Prices associated with this verification - */ -export interface SMSVerificationReportResponsePrice { - - /** The maximum price charged for this verification process. This property will appear in the body of the response with a delay. It will become visible only when the verification status is other than PENDING. */ - verificationPrice?: Price; -} diff --git a/packages/verification/src/models/v1/verification-request-event-response/verification-request-event-response.ts b/packages/verification/src/models/v1/verification-request-event-response/verification-request-event-response.ts deleted file mode 100644 index f6aa5e43..00000000 --- a/packages/verification/src/models/v1/verification-request-event-response/verification-request-event-response.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SMSRequestEventResponse } from '../sms-request-event-response'; -import { FlashCallRequestEventResponse } from '../flashcall-request-event-response'; -import { CalloutRequestEventResponse } from '../callout-request-event-response'; - -export type VerificationRequestEventResponse = SMSRequestEventResponse - | FlashCallRequestEventResponse - | CalloutRequestEventResponse; diff --git a/packages/verification/src/models/v1/verification-request-event/index.ts b/packages/verification/src/models/v1/verification-request-event/index.ts deleted file mode 100644 index 1fb30a07..00000000 --- a/packages/verification/src/models/v1/verification-request-event/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type { VerificationRequestEvent } from './verification-request-event'; diff --git a/packages/verification/src/models/v1/verification-result-event-response/verification-result-event-response.ts b/packages/verification/src/models/v1/verification-result-event-response/verification-result-event-response.ts deleted file mode 100644 index 10f825df..00000000 --- a/packages/verification/src/models/v1/verification-result-event-response/verification-result-event-response.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SMSRequestEventResponse } from '../sms-request-event-response'; -import { FlashCallRequestEventResponse } from '../flashcall-request-event-response'; -import { CalloutRequestEventResponse } from '../callout-request-event-response'; - -export type VerificationResultEventResponse = SMSRequestEventResponse - | FlashCallRequestEventResponse - | CalloutRequestEventResponse; diff --git a/packages/verification/src/models/v1/verification-status-response/verification-status-response.ts b/packages/verification/src/models/v1/verification-status-response/verification-status-response.ts index 8f4fd76a..0ea38fc3 100644 --- a/packages/verification/src/models/v1/verification-status-response/verification-status-response.ts +++ b/packages/verification/src/models/v1/verification-status-response/verification-status-response.ts @@ -1,7 +1,11 @@ -import { SMSVerificationStatusResponse } from '../sms-verification-status-response'; +import { SmsVerificationStatusResponse } from '../sms-verification-status-response'; import { FlashCallVerificationStatusResponse } from '../flashcall-verification-status-response'; -import { CalloutVerificationStatusResponse } from '../callout-verification-status-response'; +import { + CalloutVerificationStatusResponse, + PhoneCallVerificationStatusResponse, +} from '../phonecall-verification-status-response'; -export type VerificationStatusResponse = SMSVerificationStatusResponse +export type VerificationStatusResponse = SmsVerificationStatusResponse | FlashCallVerificationStatusResponse - | CalloutVerificationStatusResponse; + | CalloutVerificationStatusResponse + | PhoneCallVerificationStatusResponse; diff --git a/packages/verification/src/rest/v1/callbacks/callbacks-webhook.ts b/packages/verification/src/rest/v1/callbacks/callbacks-webhook.ts index 5b8ff98a..4fec4b6e 100644 --- a/packages/verification/src/rest/v1/callbacks/callbacks-webhook.ts +++ b/packages/verification/src/rest/v1/callbacks/callbacks-webhook.ts @@ -1,10 +1,11 @@ -import { VerificationRequestEvent, VerificationResultEvent } from '../../../models'; +import { VerificationCallbackEvent, VerificationRequestEvent, VerificationResultEvent } from '../../../models'; import { CallbackProcessor, SinchClientParameters, validateAuthenticationHeader } from '@sinch/sdk-client'; import { IncomingHttpHeaders } from 'http'; +/** @deprecated Use Verification.VerificationCallback instead */ export type VerificationCallback = VerificationRequestEvent | VerificationResultEvent; -export class VerificationCallbackWebhooks implements CallbackProcessor{ +export class VerificationCallbackWebhooks implements CallbackProcessor{ private readonly sinchClientParameters: SinchClientParameters; @@ -39,9 +40,9 @@ export class VerificationCallbackWebhooks implements CallbackProcessor> { @@ -25,7 +32,7 @@ export class VerificationsApiFixture implements Partial, + SmsVerificationReportResponse>, [ReportSmsVerificationByIdRequestData]> = jest.fn(); /** * Fixture associated to function reportFlashCallById @@ -33,6 +40,12 @@ export class VerificationsApiFixture implements Partial, [ReportFlashCallVerificationByIdRequestData]> = jest.fn(); + /** + * Fixture associated to function reportPhoneCallById + */ + public reportPhoneCallById: jest.Mock, + [ReportPhoneCallVerificationByIdRequestData]> = jest.fn(); /** * Fixture associated to function reportCalloutById */ @@ -44,7 +57,7 @@ export class VerificationsApiFixture implements Partial, + SmsVerificationReportResponse>, [ReportSmsVerificationByIdentityRequestData]> = jest.fn(); /** * Fixture associated to function reportFlashCallByIdentity @@ -53,6 +66,13 @@ export class VerificationsApiFixture implements Partial, [ReportFlashCallVerificationByIdentityRequestData]> = jest.fn(); + /** + * Fixture associated to function reportPhoneCallByIdentity + */ + public reportPhoneCallByIdentity: + jest.Mock, + [ReportPhoneCallVerificationByIdentityRequestData]> = jest.fn(); /** * Fixture associated to function reportCalloutByIdentity */ @@ -72,6 +92,18 @@ export class VerificationsApiFixture implements Partial, [StartFlashCallVerificationRequestData]> = jest.fn(); + /** + * Fixture associated to function startPhoneCall + */ + public startPhoneCall: jest.Mock, + [StartPhoneCallVerificationRequestData]> = jest.fn(); + /** + * Fixture associated to function startData + */ + public startData: jest.Mock< + Promise, + [StartDataVerificationRequestData]> = jest.fn(); /** * Fixture associated to function startCallout */ diff --git a/packages/verification/src/rest/v1/verifications/verifications-api.ts b/packages/verification/src/rest/v1/verifications/verifications-api.ts index 36502254..a4b84877 100644 --- a/packages/verification/src/rest/v1/verifications/verifications-api.ts +++ b/packages/verification/src/rest/v1/verifications/verifications-api.ts @@ -5,7 +5,7 @@ import { StartFlashCallVerificationResponse, FlashCallVerificationReportResponse, StartSmsVerificationResponse, - SMSVerificationReportResponse, + SmsVerificationReportResponse, ReportSmsVerificationByIdRequestData, ReportFlashCallVerificationByIdRequestData, ReportCalloutVerificationByIdRequestData, @@ -17,6 +17,17 @@ import { StartCalloutVerificationRequestData, StartSeamlessVerificationRequestData, StartVerificationWithSms, + StartPhoneCallVerificationRequestData, + StartPhoneCallVerificationResponse, + StartVerificationWithPhoneCall, + StartDataVerificationRequestData, + StartDataVerificationResponse, + ReportPhoneCallVerificationByIdRequestData, + PhoneCallVerificationReportRequest, + StartVerificationWithPhoneCallServerModel, + PhoneCallVerificationReportRequestServerModel, + ReportPhoneCallVerificationByIdentityRequestData, + PhoneCallVerificationReportResponse, } from '../../../models'; import { RequestBody, @@ -40,12 +51,12 @@ export class VerificationsApi extends VerificationDomainApi { * Report the received verification code to verify it, using the Verification ID of the Verification request. * @param { ReportSmsVerificationByIdRequestData } data - The data to provide to the API call. */ - public async reportSmsById(data: ReportSmsVerificationByIdRequestData): Promise { + public async reportSmsById(data: ReportSmsVerificationByIdRequestData): Promise { this.client = this.getSinchClient(); (data.reportSmsVerificationByIdRequestBody as any).method = 'sms'; const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; @@ -59,7 +70,7 @@ export class VerificationsApi extends VerificationDomainApi { = await this.client.prepareOptions(basePathUrl, 'PUT', getParams, headers, body || undefined, path); const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); - return this.client.processCall({ + return this.client.processCall({ url, requestOptions, apiName: this.apiName, @@ -76,12 +87,12 @@ export class VerificationsApi extends VerificationDomainApi { data: ReportFlashCallVerificationByIdRequestData, ): Promise { this.client = this.getSinchClient(); - (data.reportFlashCallVerificationByIdRequestBody as any).method = 'flashCall'; + (data.reportFlashCallVerificationByIdRequestBody as any).method = 'flashcall'; const getParams = this.client.extractQueryParams( data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; @@ -103,10 +114,59 @@ export class VerificationsApi extends VerificationDomainApi { }); } + /** + * Report a Phone Call verification with ID + * Report the received verification code to verify it, using the Verification ID of the Verification request. + * @param { ReportCalloutVerificationByIdRequestData } data - The data to provide to the API call. + */ + public async reportPhoneCallById( + data: ReportPhoneCallVerificationByIdRequestData, + ): Promise { + this.client = this.getSinchClient(); + (data.reportPhoneCallVerificationByIdRequestBody as any).method = 'callout'; + const getParams = this.client.extractQueryParams(data, [] as never[]); + const headers: { [key: string]: string | undefined } = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; + + // Special fields handling: see method for details + const requestDataBody = this.performReportPhoneCallByIdRequestBodyTransformation( + data.reportPhoneCallVerificationByIdRequestBody); + + const body: RequestBody = requestDataBody + ? JSON.stringify(requestDataBody) + : '{}'; + + const path = `/verification/v1/verifications/id/${data['id']}`; + const basePathUrl = this.client.apiClientOptions.hostname + path; + + const requestOptions + = await this.client.prepareOptions(basePathUrl, 'PUT', getParams, headers, body || undefined, path); + const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); + + return this.client.processCall({ + url, + requestOptions, + apiName: this.apiName, + operationId: 'ReportPhoneCallVerificationById', + }); + } + + performReportPhoneCallByIdRequestBodyTransformation( + body: PhoneCallVerificationReportRequest, + ): PhoneCallVerificationReportRequestServerModel { + const requestDataBody: any = { ...body }; + (requestDataBody).callout = { ...requestDataBody.phoneCall }; + delete requestDataBody.phoneCall; + return requestDataBody; + } + /** * Report a Callout verification with ID * Report the received verification code to verify it, using the Verification ID of the Verification request. * @param { ReportCalloutVerificationByIdRequestData } data - The data to provide to the API call. + * @deprecated Use the method reportPhoneCallById() instead */ public async reportCalloutById( data: ReportCalloutVerificationByIdRequestData, @@ -115,7 +175,7 @@ export class VerificationsApi extends VerificationDomainApi { (data.reportCalloutVerificationByIdRequestBody as any).method = 'callout'; const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; @@ -144,12 +204,12 @@ export class VerificationsApi extends VerificationDomainApi { */ public async reportSmsByIdentity( data: ReportSmsVerificationByIdentityRequestData, - ): Promise { + ): Promise { this.client = this.getSinchClient(); (data.reportSmsVerificationByIdentityRequestBody as any).method = 'sms'; const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; @@ -163,7 +223,7 @@ export class VerificationsApi extends VerificationDomainApi { = await this.client.prepareOptions(basePathUrl, 'PUT', getParams, headers, body || undefined, path); const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); - return this.client.processCall({ + return this.client.processCall({ url, requestOptions, apiName: this.apiName, @@ -180,11 +240,11 @@ export class VerificationsApi extends VerificationDomainApi { data: ReportFlashCallVerificationByIdentityRequestData, ): Promise { this.client = this.getSinchClient(); - (data.reportFlashCallVerificationByIdentityRequestBody as any).method = 'flashCall'; + (data.reportFlashCallVerificationByIdentityRequestBody as any).method = 'flashcall'; const getParams = this.client.extractQueryParams( data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; @@ -206,10 +266,59 @@ export class VerificationsApi extends VerificationDomainApi { }); } + /** + * Report a Phone Call verification using Identity + * Report the received verification code (OTP) to verify it, using the identity of the user (in most cases, the phone number). + * @param { ReportPhoneCallVerificationByIdentityRequestData } data - The data to provide to the API call. + */ + public async reportPhoneCallByIdentity( + data: ReportPhoneCallVerificationByIdentityRequestData, + ): Promise { + this.client = this.getSinchClient(); + (data.reportPhoneCallVerificationByIdentityRequestBody as any).method = 'callout'; + const getParams = this.client.extractQueryParams( + data, [] as never[]); + const headers: { [key: string]: string | undefined } = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; + + // Special fields handling: see method for details + const requestDataBody = this.performReportPhoneCallByIdentityRequestBodyTransformation( + data.reportPhoneCallVerificationByIdentityRequestBody); + + const body: RequestBody = requestDataBody + ? JSON.stringify(requestDataBody) + : '{}'; + const path = `/verification/v1/verifications/number/${data['endpoint']}`; + const basePathUrl = this.client.apiClientOptions.hostname + path; + + const requestOptions + = await this.client.prepareOptions(basePathUrl, 'PUT', getParams, headers, body || undefined, path); + const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); + + return this.client.processCall({ + url, + requestOptions, + apiName: this.apiName, + operationId: 'ReportPhoneCallVerificationByIdentity', + }); + } + + performReportPhoneCallByIdentityRequestBodyTransformation( + body: PhoneCallVerificationReportRequest, + ): PhoneCallVerificationReportRequestServerModel { + const requestDataBody: any = { ...body }; + (requestDataBody).callout = { ...requestDataBody.phoneCall }; + delete requestDataBody.phoneCall; + return requestDataBody; + } + /** * Report a Callout verification using Identity * Report the received verification code (OTP) to verify it, using the identity of the user (in most cases, the phone number). * @param { ReportCalloutVerificationByIdentityRequestData } data - The data to provide to the API call. + * @deprecated Use the method reportPhoneCallByIdentity() instead */ public async reportCalloutByIdentity( data: ReportCalloutVerificationByIdentityRequestData, @@ -219,7 +328,7 @@ export class VerificationsApi extends VerificationDomainApi { const getParams = this.client.extractQueryParams( data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; @@ -251,9 +360,12 @@ export class VerificationsApi extends VerificationDomainApi { (data.startVerificationWithSmsRequestBody as any).method = 'sms'; const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; + if (data.startVerificationWithSmsRequestBody.smsOptions?.locale !== undefined) { + headers['Accept-Language'] = data.startVerificationWithSmsRequestBody.smsOptions.locale; + } // Special fields handling: see method for details const requestDataBody = this.performStartSmsRequestBodyTransformation(data.startVerificationWithSmsRequestBody); @@ -284,6 +396,8 @@ export class VerificationsApi extends VerificationDomainApi { requestDataBody.smsOptions.expiry = this.formatTime(expiry); } } + // Remove the `locale` property from the body as it is used as a header parameter for the API call + delete requestDataBody.smsOptions?.locale; return requestDataBody; } @@ -298,6 +412,7 @@ export class VerificationsApi extends VerificationDomainApi { return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`; } + /** * Start verification with a FlashCall * This method is used by the mobile and web Verification SDKs to start a verification. It can also be used to request a verification from your backend, by making a request. @@ -307,10 +422,10 @@ export class VerificationsApi extends VerificationDomainApi { data: StartFlashCallVerificationRequestData, ): Promise { this.client = this.getSinchClient(); - (data.startVerificationWithFlashCallRequestBody as any).method = 'flashCall'; + (data.startVerificationWithFlashCallRequestBody as any).method = 'flashcall'; const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; @@ -332,17 +447,100 @@ export class VerificationsApi extends VerificationDomainApi { }); } + /** + * Start verification with a phone call + * This method is used by the mobile and web Verification SDKs to start a verification. It can also be used to request a verification from your backend, by making a request. + * @param { StartPhoneCallVerificationRequestData } data - The data to provide to the API call. + */ + public async startPhoneCall( + data: StartPhoneCallVerificationRequestData, + ): Promise { + this.client = this.getSinchClient(); + (data.startVerificationWithPhoneCallRequestBody as any).method = 'callout'; + const getParams = this.client.extractQueryParams(data, [] as never[]); + const headers: { [key: string]: string | undefined } = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; + + // Special fields handling: see method for details + const requestDataBody = this.performStartPhoneCallRequestBodyTransformation( + data.startVerificationWithPhoneCallRequestBody); + + const body: RequestBody = requestDataBody + ? JSON.stringify(requestDataBody) + : '{}'; + + const path = '/verification/v1/verifications'; + const basePathUrl = this.client.apiClientOptions.hostname + path; + + const requestOptions + = await this.client.prepareOptions(basePathUrl, 'POST', getParams, headers, body || undefined, path); + const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); + + return this.client.processCall({ + url, + requestOptions, + apiName: this.apiName, + operationId: 'StartVerificationWithPhoneCall', + }); + } + + performStartPhoneCallRequestBodyTransformation( + body: StartVerificationWithPhoneCall, + ): StartVerificationWithPhoneCallServerModel { + const requestDataBody = { ...body }; + if (requestDataBody.phoneCallOptions !== undefined) { + (requestDataBody as any).calloutOptions = { ...requestDataBody.phoneCallOptions }; + delete requestDataBody.phoneCallOptions; + } + return requestDataBody; + } + + /** + * Start data verification + * This method is used by the mobile and web Verification SDKs to start a verification. It can also be used to request a verification from your backend, by making a request. + * @param { StartDataVerificationRequestData } data - The data to provide to the API call. + */ + public async startData(data: StartDataVerificationRequestData): Promise { + this.client = this.getSinchClient(); + (data.startDataVerificationRequestBody as any).method = 'seamless'; + const getParams = this.client.extractQueryParams(data, [] as never[]); + const headers: { [key: string]: string | undefined } = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; + + const body: RequestBody = data['startDataVerificationRequestBody'] + ? JSON.stringify(data['startDataVerificationRequestBody']) + : '{}'; + const path = '/verification/v1/verifications'; + const basePathUrl = this.client.apiClientOptions.hostname + path; + + const requestOptions + = await this.client.prepareOptions(basePathUrl, 'POST', getParams, headers, body || undefined, path); + const url = this.client.prepareUrl(requestOptions.hostname, requestOptions.queryParams); + + return this.client.processCall({ + url, + requestOptions, + apiName: this.apiName, + operationId: 'StartDataVerification', + }); + } + /** * Start verification with a callout * This method is used by the mobile and web Verification SDKs to start a verification. It can also be used to request a verification from your backend, by making a request. * @param { StartCalloutVerificationRequestData } data - The data to provide to the API call. + * @deprecated Use the method startPhoneCall() instead */ public async startCallout(data: StartCalloutVerificationRequestData): Promise { this.client = this.getSinchClient(); (data.startVerificationWithCalloutRequestBody as any).method = 'callout'; const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; @@ -368,13 +566,14 @@ export class VerificationsApi extends VerificationDomainApi { * Start seamless verification (= data verification) * This method is used by the mobile and web Verification SDKs to start a verification. It can also be used to request a verification from your backend, by making a request. * @param { StartSeamlessVerificationRequestData } data - The data to provide to the API call. + * @deprecated Use the method startData() instead */ public async startSeamless(data: StartSeamlessVerificationRequestData): Promise { this.client = this.getSinchClient(); (data.startSeamlessVerificationRequestBody as any).method = 'seamless'; const getParams = this.client.extractQueryParams(data, [] as never[]); const headers: { [key: string]: string | undefined } = { - 'Content-Type': 'application/json; charset=UTF-8', + 'Content-Type': 'application/json', 'Accept': 'application/json', }; diff --git a/packages/verification/tests/rest/v1/callbacks/webhooks-events.steps.ts b/packages/verification/tests/rest/v1/callbacks/webhooks-events.steps.ts new file mode 100644 index 00000000..26183495 --- /dev/null +++ b/packages/verification/tests/rest/v1/callbacks/webhooks-events.steps.ts @@ -0,0 +1,89 @@ +import { VerificationCallbackWebhooks, Verification } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import assert from 'assert'; +import { IncomingHttpHeaders } from 'http'; + +let verificationCallbackWebhook: VerificationCallbackWebhooks; +let rawEvent: any; +let event: Verification.VerificationCallbackEvent; +let formattedHeaders: IncomingHttpHeaders; + +const processEvent = async (response: Response) => { + formattedHeaders = {}; + response.headers.forEach((value, name) => { + formattedHeaders[name.toLowerCase()] = value; + }); + rawEvent = await response.text(); + event = verificationCallbackWebhook.parseEvent(JSON.parse(rawEvent)); +}; + +Given('the Verification Webhooks handler is available', () => { + verificationCallbackWebhook = new VerificationCallbackWebhooks({ + applicationKey: 'appKey', + applicationSecret: 'appSecret', + }); +}); + +When('I send a request to trigger a "Verification Request" event', async () => { + const response = await fetch('http://localhost:3018/webhooks/verification/verification-request-event'); + await processEvent(response); +}); + +Then('the header of the Verification event "Verification Request" contains a valid authorization', () => { + assert.ok(verificationCallbackWebhook.validateAuthenticationHeader( + formattedHeaders, + rawEvent, + '/webhooks/verification', + 'POST')); +}); + +Then('the Verification event describes a "Verification Request" event type', () => { + const verificationRequestEvent = event as Verification.VerificationRequestEvent; + assert.equal(verificationRequestEvent.id, '1ce0ffee-c0de-5eed-d00d-f00dfeed1337'); + assert.equal(verificationRequestEvent.event, 'VerificationRequestEvent'); + const smsVerificationMethod: Verification.MethodEnum = 'sms'; + assert.equal(verificationRequestEvent.method, smsVerificationMethod); + const identity: Verification.Identity = { + type: 'number', + endpoint: '+33612345678', + }; + assert.equal(verificationRequestEvent.identity.type, identity.type); + assert.equal(verificationRequestEvent.identity.endpoint, identity.endpoint); + const smsPrice: Verification.Price = { + currencyId: 'EUR', + amount: 0.0453, + }; + assert.deepEqual(verificationRequestEvent.price, smsPrice); + const smsRate: Verification.Price = { + currencyId: 'EUR', + amount: 0, + }; + assert.deepEqual(verificationRequestEvent.rate, smsRate); +}); + +When('I send a request to trigger a "Verification Result" event', async () => { + const response = await fetch('http://localhost:3018/webhooks/verification/verification-result-event'); + await processEvent(response); +}); + +Then('the header of the Verification event "Verification Result" contains a valid authorization', () => { + assert.ok(verificationCallbackWebhook.validateAuthenticationHeader( + formattedHeaders, + rawEvent, + '/webhooks/verification', + 'POST')); +}); + +Then('the Verification event describes a "Verification Result" event type', () => { + const verificationRequestEvent = event as Verification.VerificationResultEvent; + assert.equal(verificationRequestEvent.id, '1ce0ffee-c0de-5eed-d00d-f00dfeed1337'); + assert.equal(verificationRequestEvent.event, 'VerificationResultEvent'); + const smsVerificationMethod: Verification.MethodEnum = 'sms'; + assert.equal(verificationRequestEvent.method, smsVerificationMethod); + const identity: Verification.Identity = { + type: 'number', + endpoint: '+33612345678', + }; + assert.equal(verificationRequestEvent.identity.type, identity.type); + assert.equal(verificationRequestEvent.identity.endpoint, identity.endpoint); +}); diff --git a/packages/verification/tests/rest/v1/verification-status/verification-status-api.test.ts b/packages/verification/tests/rest/v1/verification-status/verification-status-api.test.ts index cfc574d5..9be2e6f7 100644 --- a/packages/verification/tests/rest/v1/verification-status/verification-status-api.test.ts +++ b/packages/verification/tests/rest/v1/verification-status/verification-status-api.test.ts @@ -25,7 +25,7 @@ describe('VerificationStatusApi', () => { const requestData: Verification.VerificationStatusByIdRequestData = { id: '', }; - const expectedResponse: Verification.SMSVerificationStatusResponse = { + const expectedResponse: Verification.SmsVerificationStatusResponse = { id: '018bec3e-6913-d36c-5102-ebda3fd6d30f', method: 'sms', status: 'SUCCESSFUL', @@ -61,7 +61,7 @@ describe('VerificationStatusApi', () => { endpoint: '+33444555666', method: 'callout', }; - const expectedResponse: Verification.CalloutVerificationStatusResponse = { + const expectedResponse: Verification.PhoneCallVerificationStatusResponse = { id: '018bec2b-d123-b7b3-833e-4b177e3420df', method: 'callout', status: 'FAIL', diff --git a/packages/verification/tests/rest/v1/verification-status/verification-status.steps.ts b/packages/verification/tests/rest/v1/verification-status/verification-status.steps.ts new file mode 100644 index 00000000..4af23ae5 --- /dev/null +++ b/packages/verification/tests/rest/v1/verification-status/verification-status.steps.ts @@ -0,0 +1,114 @@ +import { VerificationStatusApi, VerificationService, Verification } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let verificationStatusApi: VerificationStatusApi; +let smsVerificationStatus: Verification.SmsVerificationStatusResponse; +let phoneCallVerificationStatus: Verification.PhoneCallVerificationStatusResponse; +let flashCallVerificationStatus: Verification.FlashCallVerificationStatusResponse; + +Given('the Verification service "Status" is available', () => { + const verificationService = new VerificationService({ + applicationKey: 'appKey', + applicationSecret: 'appSecret', + verificationHostname: 'http://localhost:3018', + }); + verificationStatusApi = verificationService.verificationStatus; +}); + +When('I send a request to retrieve a SMS verification status by its verification ID', async () => { + smsVerificationStatus = await verificationStatusApi.getById({ + id: '1ce0ffee-c0de-5eed-d00d-f00dfeed1337', + }) as Verification.SmsVerificationStatusResponse; +}); + +Then('the response contains the details of the SMS verification status', () => { + assert.equal(smsVerificationStatus.id, '1ce0ffee-c0de-5eed-d00d-f00dfeed1337'); + assert.equal(smsVerificationStatus.method, 'sms'); + const successfulStatus: Verification.VerificationStatusEnum = 'SUCCESSFUL'; + assert.equal(smsVerificationStatus.status, successfulStatus); + const verificationPrice: Verification.VerificationPriceSms = { + verificationPrice: { + currencyId: 'EUR', + amount: 0.0453, + }, + }; + assert.deepEqual(smsVerificationStatus.price, verificationPrice); + const identity: Verification.Identity = { + type: 'number', + endpoint: '+33612345678', + }; + assert.deepEqual(smsVerificationStatus.identity, identity); + assert.equal(smsVerificationStatus.countryId, 'FR'); + assert.deepEqual(smsVerificationStatus.verificationTimestamp, new Date('2024-06-06T09:08:41.4784877Z')); +}); + +When('I send a request to retrieve a Phone Call verification status by the phone number to verify', async () => { + phoneCallVerificationStatus = await verificationStatusApi.getByIdentity({ + method: 'phonecall', + endpoint: '+33612345678', + }) as Verification.PhoneCallVerificationStatusResponse; +}); + +Then('the response contains the details of the Phone Call verification status', () => { + assert.equal(phoneCallVerificationStatus.id, '1ce0ffee-c0de-5eed-d11d-f00dfeed1337'); + assert.equal(phoneCallVerificationStatus.method, 'callout'); + const successfulStatus: Verification.VerificationStatusEnum = 'SUCCESSFUL'; + assert.equal(phoneCallVerificationStatus.status, successfulStatus); + const verificationPrice: Verification.VerificationPriceCall = { + verificationPrice: { + currencyId: 'EUR', + amount: 0.1852, + }, + terminationPrice: { + currencyId: 'EUR', + amount: 0, + }, + }; + assert.deepEqual(phoneCallVerificationStatus.price, verificationPrice); + const identity: Verification.Identity = { + type: 'number', + endpoint: '+33612345678', + }; + assert.deepEqual(phoneCallVerificationStatus.identity, identity); + assert.equal(phoneCallVerificationStatus.countryId, 'FR'); + assert.deepEqual(phoneCallVerificationStatus.verificationTimestamp, new Date('2024-06-06T09:10:27.7264837Z')); + assert.equal(phoneCallVerificationStatus.callComplete, true); + const answeredCallResult: Verification.CallResult = 'ANSWERED'; + assert.equal(phoneCallVerificationStatus.callResult, answeredCallResult); +}); + +When('I send a request to retrieve a Flash Call verification status by its reference', async () => { + flashCallVerificationStatus = await verificationStatusApi.getByReference({ + reference: 'flashcall-verification-test-e2e', + }) as Verification.FlashCallVerificationReportResponse; +}); + +Then('the response contains the details of the Flash Call verification status', () => { + assert.equal(flashCallVerificationStatus.id, '1ce0ffee-c0de-5eed-d22d-f00dfeed1337'); + assert.equal(flashCallVerificationStatus.method, 'flashcall'); + const successfulStatus: Verification.VerificationStatusEnum = 'SUCCESSFUL'; + assert.equal(flashCallVerificationStatus.status, successfulStatus); + assert.equal(flashCallVerificationStatus.reference, 'flashcall-verification-test-e2e'); + const verificationPrice: Verification.VerificationPriceCall = { + verificationPrice: { + currencyId: 'EUR', + amount: 0.0205, + }, + terminationPrice: { + currencyId: 'EUR', + amount: 0, + }, + }; + assert.deepEqual(flashCallVerificationStatus.price, verificationPrice); + const identity: Verification.Identity = { + type: 'number', + endpoint: '+33612345678', + }; + assert.deepEqual(flashCallVerificationStatus.identity, identity); + assert.equal(flashCallVerificationStatus.countryId, 'FR'); + assert.deepEqual(flashCallVerificationStatus.verificationTimestamp, new Date('2024-06-06T09:07:32.3554646Z')); + assert.equal(flashCallVerificationStatus.callComplete, true); + const answeredCallResult: Verification.CallResult = 'ANSWERED'; + assert.equal(flashCallVerificationStatus.callResult, answeredCallResult); +}); diff --git a/packages/verification/tests/rest/v1/verifications/report.steps.ts b/packages/verification/tests/rest/v1/verifications/report.steps.ts new file mode 100644 index 00000000..9d0055bf --- /dev/null +++ b/packages/verification/tests/rest/v1/verifications/report.steps.ts @@ -0,0 +1,117 @@ +import { VerificationsApi, VerificationService, Verification } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let reportVerificationApi: VerificationsApi; +let reportSmsResponse: Verification.SmsVerificationReportResponse; +let reportPhoneCallResponse: Verification.PhoneCallVerificationReportResponse; +let reportFlashCallResponse: Verification.FlashCallVerificationReportResponse; + +Given('the Verification service "Report" is available', () => { + const verificationService = new VerificationService({ + applicationKey: 'appKey', + applicationSecret: 'appSecret', + verificationHostname: 'http://localhost:3018', + }); + reportVerificationApi = verificationService.verifications; +}); + +When('I send a request to report an SMS verification with the verification ID', async () => { + reportSmsResponse = await reportVerificationApi.reportSmsById({ + id: '1ce0ffee-c0de-5eed-d00d-f00dfeed1337', + reportSmsVerificationByIdRequestBody: { + sms: { + code: 'OQP1', + }, + }, + }); +}); + +When('I send a request to report an SMS verification with the phone number', async () => { + reportSmsResponse = await reportVerificationApi.reportSmsByIdentity({ + endpoint: '+46123456789', + reportSmsVerificationByIdentityRequestBody: { + sms: { + code: 'OQP1', + }, + }, + }); +}); + +Then('the response contains the details of an SMS verification report', () => { + assert.equal(reportSmsResponse.id, '1ce0ffee-c0de-5eed-d00d-f00dfeed1337'); + assert.equal(reportSmsResponse.method, 'sms'); + const successfulStatus: Verification.VerificationStatusEnum = 'SUCCESSFUL'; + assert.equal(reportSmsResponse.status, successfulStatus); +}); + +When('I send a request to report a Phone Call verification with the verification ID', async () => { + reportPhoneCallResponse = await reportVerificationApi.reportPhoneCallById({ + id: '1ce0ffee-c0de-5eed-d11d-f00dfeed1337', + reportPhoneCallVerificationByIdRequestBody: { + phoneCall: { + code: '123456', + }, + }, + }); +}); + +When('I send a request to report a Phone Call verification with the phone number', async () => { + reportPhoneCallResponse = await reportVerificationApi.reportPhoneCallByIdentity({ + endpoint: '+33612345678', + reportPhoneCallVerificationByIdentityRequestBody: { + phoneCall: { + code: '123456', + }, + }, + }); +}); + +Then('the response contains the details of a Phone Call verification report', () => { + assert.equal(reportPhoneCallResponse.id, '1ce0ffee-c0de-5eed-d11d-f00dfeed1337'); + assert.equal(reportPhoneCallResponse.method, 'callout'); + const successfulStatus: Verification.VerificationStatusEnum = 'SUCCESSFUL'; + assert.equal(reportPhoneCallResponse.status, successfulStatus); + assert.equal(reportPhoneCallResponse.callComplete, true); +}); + +When('I send a request to report a Flash Call verification with the verification ID', async () => { + reportFlashCallResponse = await reportVerificationApi.reportFlashCallById({ + id: '1ce0ffee-c0de-5eed-d11d-f00dfeed1337', + reportFlashCallVerificationByIdRequestBody: { + flashCall: { + cli: '+18156540001', + }, + }, + }); +}); + +When('I send a request to report a Flash Call verification with the phone number', async () => { + reportFlashCallResponse = await reportVerificationApi.reportFlashCallByIdentity({ + endpoint: '+33612345678', + reportFlashCallVerificationByIdentityRequestBody: { + flashCall: { + cli: '+18156540001', + }, + }, + }); +}); + +Then('the response contains the details of a Flash Call verification report', () => { + assert.equal(reportFlashCallResponse.id, '1ce0ffee-c0de-5eed-d22d-f00dfeed1337'); + assert.equal(reportFlashCallResponse.method, 'flashcall'); + const successfulStatus: Verification.VerificationStatusEnum = 'SUCCESSFUL'; + assert.equal(reportFlashCallResponse.status, successfulStatus); + assert.equal(reportFlashCallResponse.reference, 'flashcall-verification-test-e2e'); + assert.equal(reportFlashCallResponse.callComplete, true); +}); + +Then('the response contains the details of a failed Flash Call verification report', () => { + assert.equal(reportFlashCallResponse.id, '1ce0ffee-c0de-5eed-d22d-f00dfeed1337'); + assert.equal(reportFlashCallResponse.method, 'flashcall'); + const failStatus: Verification.VerificationStatusEnum = 'FAIL'; + assert.equal(reportFlashCallResponse.status, failStatus); + const expiredReason: Verification.ReasonEnum = 'Expired'; + assert.equal(reportFlashCallResponse.reason, expiredReason); + assert.equal(reportFlashCallResponse.callComplete, true); +}); diff --git a/packages/verification/tests/rest/v1/verifications/start.steps.ts b/packages/verification/tests/rest/v1/verifications/start.steps.ts new file mode 100644 index 00000000..f811b864 --- /dev/null +++ b/packages/verification/tests/rest/v1/verifications/start.steps.ts @@ -0,0 +1,139 @@ +import { VerificationsApi, VerificationService, Verification } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; +import { RequestFailedError } from '@sinch/sdk-client/src'; + +let startVerificationApi: VerificationsApi; +let startSmsVerificationResponse: Verification.StartSmsVerificationResponse; +let startPhoneCallVerificationResponse: Verification.StartPhoneCallVerificationResponse; +let startFlashCallVerificationResponse: Verification.StartFlashCallVerificationResponse; +let startDataVerificationResponseError: RequestFailedError; + +Given('the Verification service "Start" is available', () => { + const verificationService = new VerificationService({ + applicationKey: 'appKey', + applicationSecret: 'appSecret', + verificationHostname: 'http://localhost:3018', + }); + startVerificationApi = verificationService.verifications; +}); + +When('I send a request to start a verification with a SMS', async () => { + startSmsVerificationResponse = await startVerificationApi.startSms({ + startVerificationWithSmsRequestBody: { + identity: { + type: 'number', + endpoint: '+46123456789', + }, + smsOptions: { + codeType: 'Alphanumeric', + locale: 'sv-SE', + }, + }, + }); +}); + +Then('the response contains the details of a verification started with a SMS', () => { + assert.equal(startSmsVerificationResponse.id, '1ce0ffee-c0de-5eed-d00d-f00dfeed1337'); + assert.equal(startSmsVerificationResponse.method, 'sms'); + assert.ok(startSmsVerificationResponse.sms); + assert.equal(startSmsVerificationResponse.sms.template, 'Din verifieringskod är {{CODE}}.'); + assert.equal(startSmsVerificationResponse.sms.interceptionTimeout, 198); + assert.ok(startSmsVerificationResponse._links); + const statusLink = startSmsVerificationResponse._links[0]; + assert.equal(statusLink.rel, 'status'); + assert.equal(statusLink.href, 'http://localhost:3018/verification/v1/verifications/id/1ce0ffee-c0de-5eed-d00d-f00dfeed1337'); + assert.equal(statusLink.method, 'GET'); + const reportLink = startSmsVerificationResponse._links[1]; + assert.equal(reportLink.rel, 'report'); + assert.equal(reportLink.href, 'http://localhost:3018/verification/v1/verifications/id/1ce0ffee-c0de-5eed-d00d-f00dfeed1337'); + assert.equal(reportLink.method, 'PUT'); +}); + +When('I send a request to start a verification with a Phone Call', async () => { + startPhoneCallVerificationResponse = await startVerificationApi.startPhoneCall({ + startVerificationWithPhoneCallRequestBody: { + identity: { + type: 'number', + endpoint: '+33612345678', + }, + phoneCallOptions: { + speech: { + locale: 'fr-FR', + }, + }, + }, + }); +}); + +Then('the response contains the details of a verification started with a Phone Call', () => { + assert.equal(startPhoneCallVerificationResponse.id, '1ce0ffee-c0de-5eed-d11d-f00dfeed1337'); + assert.equal(startPhoneCallVerificationResponse.method, 'callout'); + assert.ok(startPhoneCallVerificationResponse._links); + const statusLink = startPhoneCallVerificationResponse._links[0]; + assert.equal(statusLink.rel, 'status'); + assert.equal(statusLink.href, 'http://localhost:3018/verification/v1/verifications/id/1ce0ffee-c0de-5eed-d11d-f00dfeed1337'); + assert.equal(statusLink.method, 'GET'); + const reportLink = startPhoneCallVerificationResponse._links[1]; + assert.equal(reportLink.rel, 'report'); + assert.equal(reportLink.href, 'http://localhost:3018/verification/v1/verifications/id/1ce0ffee-c0de-5eed-d11d-f00dfeed1337'); + assert.equal(reportLink.method, 'PUT'); +}); + +When('I send a request to start a verification with a Flash Call', async () => { + startFlashCallVerificationResponse = await startVerificationApi.startFlashCall({ + startVerificationWithFlashCallRequestBody: { + identity: { + type: 'number', + endpoint: '+33612345678', + }, + flashCallOptions: { + dialTimeout: 10, + }, + reference: 'flashcall-verification-test-e2e', + }, + }); +}); + +Then('the response contains the details of a verification started with a Flash Call', () => { + assert.equal(startFlashCallVerificationResponse.id, '1ce0ffee-c0de-5eed-d22d-f00dfeed1337'); + assert.equal(startFlashCallVerificationResponse.method, 'flashcall'); + assert.ok(startFlashCallVerificationResponse.flashCall); + assert.equal(startFlashCallVerificationResponse.flashCall.cliFilter, '(.*)8156(.*)'); + assert.equal(startFlashCallVerificationResponse.flashCall.interceptionTimeout, 45); + assert.equal(startFlashCallVerificationResponse.flashCall.reportTimeout, 75); + assert.equal(startFlashCallVerificationResponse.flashCall.denyCallAfter, 0); + assert.ok(startFlashCallVerificationResponse._links); + const statusLink = startFlashCallVerificationResponse._links[0]; + assert.equal(statusLink.rel, 'status'); + assert.equal(statusLink.href, 'http://localhost:3018/verification/v1/verifications/id/1ce0ffee-c0de-5eed-d22d-f00dfeed1337'); + assert.equal(statusLink.method, 'GET'); + const reportLink = startFlashCallVerificationResponse._links[1]; + assert.equal(reportLink.rel, 'report'); + assert.equal(reportLink.href, 'http://localhost:3018/verification/v1/verifications/id/1ce0ffee-c0de-5eed-d22d-f00dfeed1337'); + assert.equal(reportLink.method, 'PUT'); +}); + +When('I send a request to start a Data verification for a not available destination', async () => { + try { + await startVerificationApi.startData({ + startDataVerificationRequestBody: { + identity: { + type: 'number', + endpoint: '+17818880008', + }, + }, + }); + } catch (e: any) { + startDataVerificationResponseError = e as RequestFailedError; + } +}); + +Then('the response contains the error details of a Data verification', () => { + assert.equal(startDataVerificationResponseError.statusCode, 403); + assert.ok(startDataVerificationResponseError.data); + const errorDetails = JSON.parse(startDataVerificationResponseError.data) as Verification.VerificationError; + assert.equal(errorDetails.errorCode, 40300); + assert.equal(errorDetails.message, 'Seamless verification not available for given destination.'); + assert.equal(errorDetails.reference, 'c01dc0de-c4db-44f1-5ca1-da9159d21c191'); +}); diff --git a/packages/verification/tests/rest/v1/verifications/verifications-api.test.ts b/packages/verification/tests/rest/v1/verifications/verifications-api.test.ts index e084c97b..055c96fc 100644 --- a/packages/verification/tests/rest/v1/verifications/verifications-api.test.ts +++ b/packages/verification/tests/rest/v1/verifications/verifications-api.test.ts @@ -35,12 +35,15 @@ describe('VerificationsApi', () => { describe ('startVerification', () => { it('should make a POST request to start a verification with an SMS', async () => { // Given - const requestData = Verification.startVerificationHelper.buildSmsRequest('+46700000000'); + const smsOptions: Verification.SmsOptions = { + locale: 'sv-SE', + }; + const requestData = Verification.startVerificationHelper.buildSmsRequest('+46700000000', undefined, smsOptions); const expectedResponse: Verification.StartSmsVerificationResponse = { id: 'some_verification_id', method: 'sms', sms: { - template: 'Your verification code is {{CODE}}. Verified by Sinch', + template: 'Din verifieringskod är {{CODE}}.', interceptionTimeout: 298, }, _links, @@ -56,67 +59,90 @@ describe('VerificationsApi', () => { expect(fixture.startSms).toHaveBeenCalledWith(requestData); }); - it('should format the expiry field', () => { - const requestData = Verification.startVerificationHelper.buildSmsRequest( - '+46700000000', - undefined, - { expiry: new Date('2024-02-10T13:22:34.685Z') }); - const expectedResult: Verification.StartVerificationWithSms = { - identity: { - endpoint: '+46700000000', - type: 'number', - }, - smsOptions: { - expiry: '13:22:34', + it('should make a POST request to start a verification with a FlashCall', async () => { + // Given + const requestData = Verification.startVerificationHelper.buildFlashCallRequest('+46700000000', undefined, 30); + const expectedResponse: Verification.StartFlashCallVerificationResponse = { + id: 'some_verification_id', + method: 'flashcall', + flashCall: { + cliFilter: '(.*)70123(.*)', + interceptionTimeout: 60, + reportTimeout: 120, + denyCallAfter: 120, }, + _links, }; - const formattedRequestData - = verificationsApi.performStartSmsRequestBodyTransformation(requestData.startVerificationWithSmsRequestBody); - expect(formattedRequestData).toEqual(expectedResult); + + // When + fixture.startFlashCall.mockResolvedValue(expectedResponse); + verificationsApi.startFlashCall = fixture.startFlashCall; + const response = await verificationsApi.startFlashCall(requestData); + + // Then + expect(response).toEqual(expectedResponse); + expect(fixture.startFlashCall).toHaveBeenCalledWith(requestData); + }); + + it('should make a POST request to start a verification with a PhoneCall', async () => { + // Given + const requestData = Verification.startVerificationHelper.buildPhoneCallRequest('+46700000000'); + const expectedResponse: Verification.StartPhoneCallVerificationResponse = { + id: 'some_verification_id', + method: 'callout', + _links, + }; + + // When + fixture.startPhoneCall.mockResolvedValue(expectedResponse); + verificationsApi.startPhoneCall = fixture.startPhoneCall; + const response = await verificationsApi.startPhoneCall(requestData); + + // Then + expect(response).toEqual(expectedResponse); + expect(fixture.startPhoneCall).toHaveBeenCalledWith(requestData); }); - it('should leave the expiry field unchanged', () => { - const requestData = Verification.startVerificationHelper.buildSmsRequest( + it('should format the startPhoneCall request body', () => { + const requestData = Verification.startVerificationHelper.buildPhoneCallRequest( '+46700000000', undefined, - { expiry: '15:15:15' }); - const expectedResult: Verification.StartVerificationWithSms = { + 'en-US'); + const expectedResult: Verification.StartVerificationWithPhoneCallServerModel = { identity: { endpoint: '+46700000000', type: 'number', }, - smsOptions: { - expiry: '15:15:15', + calloutOptions: { + speech: { + locale: 'en-US', + }, }, }; - const formattedRequestData - = verificationsApi.performStartSmsRequestBodyTransformation(requestData.startVerificationWithSmsRequestBody); + const formattedRequestData = verificationsApi.performStartPhoneCallRequestBodyTransformation( + requestData.startVerificationWithPhoneCallRequestBody); expect(formattedRequestData).toEqual(expectedResult); }); - it('should make a POST request to start a verification with a FlashCall', async () => { + it('should make a POST request to start a data verification (seamless)', async () => { // Given - const requestData = Verification.startVerificationHelper.buildFlashCallRequest('+46700000000', undefined, 30); - const expectedResponse: Verification.StartFlashCallVerificationResponse = { + const requestData = Verification.startVerificationHelper.buildDataRequest('+46700000000'); + const expectedResponse: Verification.StartDataVerificationResponse = { id: 'some_verification_id', - method: 'flashcall', - flashCall: { - cliFilter: '(.*)70123(.*)', - interceptionTimeout: 60, - reportTimeout: 120, - denyCallAfter: 120, + method: 'seamless', + seamless: { + targetUri: 'https://target-uri.com', }, - _links, }; // When - fixture.startFlashCall.mockResolvedValue(expectedResponse); - verificationsApi.startFlashCall = fixture.startFlashCall; - const response = await verificationsApi.startFlashCall(requestData); + fixture.startData.mockResolvedValue(expectedResponse); + verificationsApi.startData = fixture.startData; + const response = await verificationsApi.startData(requestData); // Then expect(response).toEqual(expectedResponse); - expect(fixture.startFlashCall).toHaveBeenCalledWith(requestData); + expect(fixture.startData).toHaveBeenCalledWith(requestData); }); it('should make a POST request to start a verification with a Callout', async () => { @@ -138,7 +164,7 @@ describe('VerificationsApi', () => { expect(fixture.startCallout).toHaveBeenCalledWith(requestData); }); - it('should make a POST request to start a data verification (seamless)', async () => { + it('should make a POST request to start a seamless verification', async () => { // Given const requestData = Verification.startVerificationHelper.buildSeamlessRequest('+46700000000'); const expectedResponse: Verification.StartSeamlessVerificationResponse = { @@ -168,7 +194,7 @@ describe('VerificationsApi', () => { const requestData = Verification.reportVerificationByIdHelper.buildSmsRequest( 'some_verification_id', '0000'); - const expectedResponse: Verification.SMSVerificationReportResponse = { + const expectedResponse: Verification.SmsVerificationReportResponse = { id: 'some_verification_id', method: 'sms', status: 'SUCCESSFUL', @@ -208,6 +234,43 @@ describe('VerificationsApi', () => { }); it('should make a PUT request to report the verification code (OTP) received by a phone call to verify it,' + + 'using the verification ID of the verification request', async () => { + // Given + const requestData = Verification.reportVerificationByIdHelper.buildPhoneCallRequest( + 'some_verification_id', + '0000'); + const expectedResponse: Verification.PhoneCallVerificationReportResponse = { + id: 'some_verification_id', + method: 'callout', + status: 'SUCCESSFUL', + callComplete: true, + }; + + // When + fixture.reportPhoneCallById.mockResolvedValue(expectedResponse); + verificationsApi.reportPhoneCallById = fixture.reportPhoneCallById; + const response = await verificationsApi.reportPhoneCallById(requestData); + + // Then + expect(response).toEqual(expectedResponse); + expect(fixture.reportPhoneCallById).toHaveBeenCalledWith(requestData); + }); + + it('should format the reportPhoneCallById request body', () => { + const requestData = Verification.reportVerificationByIdHelper.buildPhoneCallRequest( + 'some_verification_id', + '0000'); + const expectedResult: Verification.PhoneCallVerificationReportRequestServerModel = { + callout: { + code: '0000', + }, + }; + const formattedRequestData = verificationsApi.performReportPhoneCallByIdRequestBodyTransformation( + requestData.reportPhoneCallVerificationByIdRequestBody); + expect(formattedRequestData).toEqual(expectedResult); + }); + + it('should make a PUT request to report the verification code (OTP) received by a callout to verify it,' + 'using the verification ID of the verification request', async () => { // Given const requestData = Verification.reportVerificationByIdHelper.buildCalloutRequest( @@ -238,7 +301,7 @@ describe('VerificationsApi', () => { const requestData = Verification.reportVerificationByIdentityHelper.buildSmsRequest( '+33444555666', '0000'); - const expectedResponse: Verification.SMSVerificationReportResponse = { + const expectedResponse: Verification.SmsVerificationReportResponse = { id: '018beea3-a942-0094-4a3a-d6b2f2c65057', method: 'sms', status: 'FAIL', @@ -282,6 +345,44 @@ describe('VerificationsApi', () => { }); it('should make a PUT request to report the verification code (OTP) received by a phone call to verify it,' + + 'using the identity of the user', async () => { + // Given + const requestData = Verification.reportVerificationByIdentityHelper.buildPhoneCallRequest( + '+33444555666', + '0000'); + const expectedResponse: Verification.PhoneCallVerificationReportResponse = { + id: '018beea3-a942-0094-4a3a-d6b2f2c65057', + method: 'callout', + status: 'FAIL', + reason: 'Expired', + callComplete: true, + }; + + // When + fixture.reportPhoneCallByIdentity.mockResolvedValue(expectedResponse); + verificationsApi.reportPhoneCallByIdentity = fixture.reportPhoneCallByIdentity; + const response = await verificationsApi.reportPhoneCallByIdentity(requestData); + + // Then + expect(response).toEqual(expectedResponse); + expect(fixture.reportPhoneCallByIdentity).toHaveBeenCalledWith(requestData); + }); + + it('should format the reportPhoneCallByIdentity request body', () => { + const requestData = Verification.reportVerificationByIdentityHelper.buildPhoneCallRequest( + '+33444555666', + '0000'); + const expectedResult: Verification.PhoneCallVerificationReportRequestServerModel = { + callout: { + code: '0000', + }, + }; + const formattedRequestData = verificationsApi.performReportPhoneCallByIdentityRequestBodyTransformation( + requestData.reportPhoneCallVerificationByIdentityRequestBody); + expect(formattedRequestData).toEqual(expectedResult); + }); + + it('should make a PUT request to report the verification code (OTP) received by a callout to verify it,' + 'using the identity of the user', async () => { // Given const requestData = Verification.reportVerificationByIdentityHelper.buildCalloutRequest( diff --git a/packages/voice/CHANGELOG.md b/packages/voice/CHANGELOG.md index afb3d060..210944f6 100644 --- a/packages/voice/CHANGELOG.md +++ b/packages/voice/CHANGELOG.md @@ -1,3 +1,15 @@ +## Version 1.2.0 +- [Tech] Update dependency `@sinch/sdk-client` to `1.2.0`. +- [Feature] In the interface `Participant`, the property `type` defines a list of string values on top of a generic string. +- [Breaking] In the interface `ConferenceCalloutRequest`, the property `mohClass` was declared as a `string` and is now a `MusicOnHold` type; +- [Deprecation Notice] The type `VoiceCallback` becomes `VoiceCallbackEvent` and is accessible on the `Voice` namespace. + +| Deprecated | New | +|----------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------| +|
handleEvent(event: VoiceCallback, res: Response) {
  console.log(event);
}
|
handleEvent(event: Voice.VoiceCallbackEvent, res: Response) {
  console.log(event);
}
| + +- [E2E] Add Cucumber steps implementation. + ## Version 1.1.0 - [Tech] Update dependency `@sinch/sdk-client` to `1.1.0` diff --git a/packages/voice/cucumber.js b/packages/voice/cucumber.js new file mode 100644 index 00000000..691a9809 --- /dev/null +++ b/packages/voice/cucumber.js @@ -0,0 +1,8 @@ +module.exports = { + default: [ + 'tests/e2e/features/**/*.feature', + '--require-module ts-node/register', + '--require tests/rest/v1/**/*.steps.ts', + `--format-options '{"snippetInterface": "synchronous"}'`, + ].join(' '), +}; diff --git a/packages/voice/package.json b/packages/voice/package.json index 473628fc..a27aff2e 100644 --- a/packages/voice/package.json +++ b/packages/voice/package.json @@ -1,6 +1,6 @@ { "name": "@sinch/voice", - "version": "1.1.0", + "version": "1.2.0", "description": "Sinch Voice API", "homepage": "", "repository": { @@ -25,13 +25,15 @@ "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", - "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo" + "compile": "tsc -p tsconfig.build.json && tsc -p tsconfig.tests.json && rimraf dist/tests tsconfig.build.tsbuildinfo", + "test:e2e": "cucumber-js" }, "dependencies": { - "@sinch/sdk-client": "^1.1.0" + "@sinch/sdk-client": "^1.2.0" }, "devDependencies": {}, "publishConfig": { - "directory": "dist" + "directory": "dist", + "access": "public" } } diff --git a/packages/voice/src/models/v1/conference-callout-request/conference-callout-request.ts b/packages/voice/src/models/v1/conference-callout-request/conference-callout-request.ts index 6d7583da..54a96829 100644 --- a/packages/voice/src/models/v1/conference-callout-request/conference-callout-request.ts +++ b/packages/voice/src/models/v1/conference-callout-request/conference-callout-request.ts @@ -1,5 +1,6 @@ import { Destination } from '../destination'; import { ConferenceDtmfOptions } from '../conference-dtmf-options'; +import { MusicOnHold } from '../enums'; /** * The conference callout calls a phone number or a user. When the call is answered, it's connected to a conference room. @@ -28,7 +29,7 @@ export interface ConferenceCalloutRequest { /** The text that will be spoken as a greeting. */ greeting?: string; /** Means "music-on-hold." It's an optional parameter that specifies what the first participant should listen to while they're alone in the conference, waiting for other participants to join. It can take one of these pre-defined values:
  • `ring` (progress tone)
  • `music1` (music file)
  • `music2` (music file)
  • `music3` (music file)

If no “music-on-hold” is specified, the user will only hear silence. */ - mohClass?: string; + mohClass?: MusicOnHold; /** Used to input custom data. */ custom?: string; /** can be either “pstn” for PSTN endpoint or “mxp” for data (app or web) clients. */ diff --git a/packages/voice/src/models/v1/mod-callbacks/index.ts b/packages/voice/src/models/v1/mod-callbacks/index.ts index 62e23609..c47363b9 100644 --- a/packages/voice/src/models/v1/mod-callbacks/index.ts +++ b/packages/voice/src/models/v1/mod-callbacks/index.ts @@ -8,3 +8,4 @@ export * from './notify-request'; export * from './pie-request'; export * from './pie-response'; export * from './callback-response'; +export * from './voice-callback-event'; diff --git a/packages/voice/src/models/v1/mod-callbacks/pie-request/index.ts b/packages/voice/src/models/v1/mod-callbacks/pie-request/index.ts index 12e1bc6f..57d27616 100644 --- a/packages/voice/src/models/v1/mod-callbacks/pie-request/index.ts +++ b/packages/voice/src/models/v1/mod-callbacks/pie-request/index.ts @@ -1 +1 @@ -export type { PieRequest, MenuResult } from './pie-request'; +export type { PieRequest, MenuResult, PieInformationType } from './pie-request'; diff --git a/packages/voice/src/models/v1/mod-callbacks/pie-request/pie-request.ts b/packages/voice/src/models/v1/mod-callbacks/pie-request/pie-request.ts index 6e5e40b5..dff7c992 100644 --- a/packages/voice/src/models/v1/mod-callbacks/pie-request/pie-request.ts +++ b/packages/voice/src/models/v1/mod-callbacks/pie-request/pie-request.ts @@ -24,9 +24,11 @@ export interface MenuResult { /** The ID of the menu that triggered the prompt input event. */ menuId?: string; /** The type of information that's returned. */ - type?: string; + type?: PieInformationType; /** The value of the returned information. */ value?: string; /** The type of input received. */ inputMethod?: string; } + +export type PieInformationType = 'error' | 'return' | 'sequence' | 'timeout' | 'hangup' | 'invalidinput'; diff --git a/packages/voice/src/models/v1/mod-callbacks/voice-callback-event.ts b/packages/voice/src/models/v1/mod-callbacks/voice-callback-event.ts new file mode 100644 index 00000000..69178239 --- /dev/null +++ b/packages/voice/src/models/v1/mod-callbacks/voice-callback-event.ts @@ -0,0 +1,7 @@ +import { IceRequest } from './ice-request'; +import { AceRequest } from './ace-request'; +import { DiceRequest } from './dice-request'; +import { PieRequest } from './pie-request'; +import { NotifyRequest } from './notify-request'; + +export type VoiceCallbackEvent = IceRequest | AceRequest | DiceRequest | PieRequest | NotifyRequest; diff --git a/packages/voice/src/models/v1/participant/participant.ts b/packages/voice/src/models/v1/participant/participant.ts index 82124509..004b70b1 100644 --- a/packages/voice/src/models/v1/participant/participant.ts +++ b/packages/voice/src/models/v1/participant/participant.ts @@ -4,7 +4,7 @@ export interface Participant { /** The type of the participant (caller or callee). */ - type?: string; + type?: 'number' | 'Number' | 'username' | 'Username' | 'sip' | 'did' | string; /** The phone number, user name, or other identifier of the participant (caller or callee). */ endpoint?: string; } diff --git a/packages/voice/src/rest/v1/callbacks/callbacks-webhook.ts b/packages/voice/src/rest/v1/callbacks/callbacks-webhook.ts index b4293b7e..e23b73ec 100644 --- a/packages/voice/src/rest/v1/callbacks/callbacks-webhook.ts +++ b/packages/voice/src/rest/v1/callbacks/callbacks-webhook.ts @@ -1,10 +1,11 @@ -import { AceRequest, DiceRequest, IceRequest, NotifyRequest, PieRequest } from '../../../models'; +import { AceRequest, DiceRequest, IceRequest, NotifyRequest, PieRequest, VoiceCallbackEvent } from '../../../models'; import { CallbackProcessor, SinchClientParameters, validateAuthenticationHeader } from '@sinch/sdk-client'; import { IncomingHttpHeaders } from 'http'; +/** @deprecated Use Voice.VoiceCallbackEvent instead */ export type VoiceCallback = IceRequest | AceRequest | DiceRequest | PieRequest | NotifyRequest; -export class VoiceCallbackWebhooks implements CallbackProcessor{ +export class VoiceCallbackWebhooks implements CallbackProcessor{ private readonly sinchClientParameters: SinchClientParameters; constructor(sinchClientParameters: SinchClientParameters) { @@ -38,9 +39,9 @@ export class VoiceCallbackWebhooks implements CallbackProcessor{ * Reviver for a Voice Event. * This method ensures the object can be treated as a Voice Event and should be called before any action is taken to manipulate the object. * @param {any} eventBody - The event body containing the voice event notification. - * @return {VoiceCallback} - The parsed voice event object. + * @return {VoiceCallbackEvent} - The parsed voice event object. */ - public parseEvent(eventBody: any): VoiceCallback { + public parseEvent(eventBody: any): VoiceCallbackEvent { if (eventBody.timestamp) { eventBody.timestamp = new Date(eventBody.timestamp); } diff --git a/packages/voice/tests/rest/v1/applications/applications.steps.ts b/packages/voice/tests/rest/v1/applications/applications.steps.ts new file mode 100644 index 00000000..2a896c81 --- /dev/null +++ b/packages/voice/tests/rest/v1/applications/applications.steps.ts @@ -0,0 +1,112 @@ +import { ApplicationsApi, VoiceService, Voice } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let applicationsApi: ApplicationsApi; +let listNumbersResponse: Voice.ListNumbersResponse; +let assignNumbersResponse: void; +let unassignNumberResponse: void; +let queryNumberResponse: Voice.QueryNumberResponse; +let callbackURLs: Voice.GetCallbacks; +let updateCallbackURLsResponse: void; + +Given('the Voice service "Applications" is available', () => { + const voiceService = new VoiceService({ + applicationKey: 'appKey', + applicationSecret: 'appSecret', + voiceApplicationManagementHostname: 'http://localhost:3020', + }); + applicationsApi = voiceService.applications; +}); + +When('I send a request to get information about my owned numbers', async () => { + listNumbersResponse = await applicationsApi.listNumbers({}); +}); + +Then('the response contains details about the numbers that I own', () => { + assert.ok(listNumbersResponse.numbers); + assert.equal(listNumbersResponse.numbers.length, 4); + const number1 = listNumbersResponse.numbers[0]; + assert.equal(number1.number, '+12012222222'); + assert.equal(number1.applicationkey, undefined); + assert.equal(number1.capability, 'voice'); + const number2 = listNumbersResponse.numbers[1]; + assert.equal(number2.number, '+12013333333'); + assert.equal(number2.applicationkey, 'ba5eba11-1dea-1337-babe-5a1ad00d1eaf'); + assert.equal(number2.capability, 'voice'); +}); + +When('I send a request to assign some numbers to a Voice Application', async () => { + assignNumbersResponse = await applicationsApi.assignNumbers({ + assignNumbersRequestBody: { + numbers: [ + '+12012222222', + ], + applicationkey: 'f00dcafe-abba-c0de-1dea-dabb1ed4caf3', + capability: 'voice', + }, + }); +}); + +Then('the assign numbers response contains no data', () => { + assert.deepEqual(assignNumbersResponse, {}); +}); + +When('I send a request to unassign a number from a Voice Application', async () => { + unassignNumberResponse = await applicationsApi.unassignNumber({ + unassignNumbersRequestBody: { + number: '+12012222222', + }, + }); +}); + +Then('the unassign number response contains no data', () => { + assert.deepEqual(unassignNumberResponse, {}); +}); + +When('I send a request to get information about a specific number', async () => { + queryNumberResponse = await applicationsApi.queryNumber({ + number: '+12015555555', + }); +}); + +Then('the response contains details about the specific number', () => { + assert.ok(queryNumberResponse.number); + const number = queryNumberResponse.number; + assert.equal(number.countryId, 'US'); + assert.equal(number.numberType, 'Fixed'); + assert.equal(number.normalizedNumber, '+12015555555'); + assert.equal(number.restricted, true); + const rate: Voice.VoicePrice = { + currencyId: 'USD', + amount: 0.0100, + }; + assert.deepEqual(number.rate, rate); +}); + +When('I send a request to get the callback URLs associated to an application', async () => { + callbackURLs = await applicationsApi.getCallbackURLs({ + applicationkey: 'f00dcafe-abba-c0de-1dea-dabb1ed4caf3', + }); +}); + +Then('the response contains callback URLs details', () => { + assert.ok(callbackURLs.url); + assert.equal(callbackURLs.url.primary, 'https://my.callback-server.com/voice'); + assert.equal(callbackURLs.url.fallback, 'https://my.fallback-server.com/voice'); +}); + +When('I send a request to update the callback URLs associated to an application', async () => { + updateCallbackURLsResponse = await applicationsApi.updateCallbackURLs({ + applicationkey: 'f00dcafe-abba-c0de-1dea-dabb1ed4caf3', + updateCallbacksRequestBody: { + url: { + primary: 'https://my-new.callback-server.com/voice', + }, + }, + }); +}); + +Then('the update callback URLs response contains no data', () => { + assert.deepEqual(updateCallbackURLsResponse, {}); +}); diff --git a/packages/voice/tests/rest/v1/callbacks/webhooks-events.steps.ts b/packages/voice/tests/rest/v1/callbacks/webhooks-events.steps.ts new file mode 100644 index 00000000..88581467 --- /dev/null +++ b/packages/voice/tests/rest/v1/callbacks/webhooks-events.steps.ts @@ -0,0 +1,158 @@ +import { VoiceCallbackWebhooks, Voice } from '../../../../src'; +import { Given, Then, When } from '@cucumber/cucumber'; +import assert from 'assert'; +import { IncomingHttpHeaders } from 'http'; + +let voiceCallbackWebhooks: VoiceCallbackWebhooks; +let rawEvent: any; +let event: Voice.VoiceCallbackEvent; +let formattedHeaders: IncomingHttpHeaders; + +const processEvent = async (response: Response) => { + formattedHeaders = {}; + response.headers.forEach((value, name) => { + formattedHeaders[name.toLowerCase()] = value; + }); + rawEvent = await response.text(); + event = voiceCallbackWebhooks.parseEvent(JSON.parse(rawEvent)); +}; + + +Given('the Voice Webhooks handler is available', () => { + voiceCallbackWebhooks = new VoiceCallbackWebhooks({ + applicationKey: 'appKey', + applicationSecret: 'YXBwU2VjcmV0', // base64 value for 'appSecret' + }); +}); + +When('I send a request to trigger a "PIE" event with a "return" type', async () => { + const response = await fetch('http://localhost:3019/webhooks/voice/pie-return'); + await processEvent(response); +}); + +Then('the header of the "PIE" event with a "return" type contains a valid authorization', () => { + assert.ok(voiceCallbackWebhooks.validateAuthenticationHeader( + formattedHeaders, + rawEvent, + '/webhooks/voice', + 'POST')); +}); + +Then('the Voice event describes a "PIE" event with a "return" type', () => { + const pieEvent = event as Voice.PieRequest; + assert.equal(pieEvent.callid, '1ce0ffee-ca11-ca11-ca11-abcdef000013'); + assert.equal(pieEvent.event, 'pie'); + const menuResult: Voice.MenuResult = { + type: 'return', + value: 'cancel', + menuId: 'main', + inputMethod: 'dtmf', + }; + assert.ok(pieEvent.menuResult); + assert.equal(pieEvent.menuResult.type, menuResult.type); + assert.equal(pieEvent.menuResult.value, menuResult.value); + assert.equal(pieEvent.menuResult.menuId, menuResult.menuId); + assert.equal(pieEvent.menuResult.inputMethod, menuResult.inputMethod); + assert.equal(pieEvent.version, 1); + assert.equal(pieEvent.custom, 'Custom text'); + assert.equal(pieEvent.applicationKey, 'f00dcafe-abba-c0de-1dea-dabb1ed4caf3'); +}); + +When('I send a request to trigger a "PIE" event with a "sequence" type', async () => { + const response = await fetch('http://localhost:3019/webhooks/voice/pie-sequence'); + await processEvent(response); +}); + +Then('the header of the "PIE" event with a "sequence" type contains a valid authorization', () => { + assert.ok(voiceCallbackWebhooks.validateAuthenticationHeader( + formattedHeaders, + rawEvent, + '/webhooks/voice', + 'POST')); +}); + +Then('the Voice event describes a "PIE" event with a "sequence" type', () => { + const pieEvent = event as Voice.PieRequest; + assert.equal(pieEvent.callid, '1ce0ffee-ca11-ca11-ca11-abcdef000023'); + assert.equal(pieEvent.event, 'pie'); + const menuResult: Voice.MenuResult = { + type: 'sequence', + value: '1234', + menuId: 'confirm', + inputMethod: 'dtmf', + }; + assert.ok(pieEvent.menuResult); + assert.equal(pieEvent.menuResult.type, menuResult.type); + assert.equal(pieEvent.menuResult.value, menuResult.value); + assert.equal(pieEvent.menuResult.menuId, menuResult.menuId); + assert.equal(pieEvent.menuResult.inputMethod, menuResult.inputMethod); + assert.equal(pieEvent.version, 1); + assert.equal(pieEvent.custom, 'Custom text'); + assert.equal(pieEvent.applicationKey, 'f00dcafe-abba-c0de-1dea-dabb1ed4caf3'); +}); + +When('I send a request to trigger a "DICE" event', async () => { + const response = await fetch('http://localhost:3019/webhooks/voice/dice'); + await processEvent(response); +}); + +Then('the header of the "DICE" event contains a valid authorization', () => { + assert.ok(voiceCallbackWebhooks.validateAuthenticationHeader( + formattedHeaders, + rawEvent, + '/webhooks/voice', + 'POST')); +}); + +Then('the Voice event describes a "DICE" event', () => { + const diceEvent = event as Voice.DiceRequest; + assert.equal(diceEvent.callid, '1ce0ffee-ca11-ca11-ca11-abcdef000033'); + assert.equal(diceEvent.event, 'dice'); + const reason: Voice.ReasonEnum = 'MANAGERHANGUP'; + assert.equal(diceEvent.reason, reason); + const result: Voice.ResultEnum = 'ANSWERED'; + assert.equal(diceEvent.result, result); + assert.equal(diceEvent.version, 1); + assert.equal(diceEvent.custom, 'Custom text'); + const debit: Voice.VoicePrice = { + currencyId: 'EUR', + amount: 0.0095, + }; + assert.deepEqual(diceEvent.userRate, debit); + const userRate: Voice.VoicePrice = { + currencyId: 'EUR', + amount: 0.0095, + }; + assert.deepEqual(diceEvent.userRate, userRate); + const destinationParticipant: Voice.Participant = { + type: 'number', + endpoint: '12017777777', + }; + assert.deepEqual(diceEvent.to, destinationParticipant); + assert.equal(diceEvent.applicationKey, 'f00dcafe-abba-c0de-1dea-dabb1ed4caf3'); + assert.equal(diceEvent.duration, 12); + assert.equal(diceEvent.from, '12015555555'); +}); + +When('I send a request to trigger a "ACE" event', async () => { + const response = await fetch('http://localhost:3019/webhooks/voice/ace'); + await processEvent(response); +}); + +Then('the header of the "ACE" event contains a valid authorization', () => { + assert.ok(voiceCallbackWebhooks.validateAuthenticationHeader( + formattedHeaders, + rawEvent, + '/webhooks/voice', + 'POST')); +}); + +Then('the Voice event describes a "ACE" event', () => { + const aceEvent = event as Voice.AceRequest; + assert.equal(aceEvent.callid, '1ce0ffee-ca11-ca11-ca11-abcdef000043'); + assert.equal(aceEvent.event, 'ace'); + assert.deepEqual(aceEvent.timestamp, new Date('2024-06-06T17:10:34Z')); + assert.equal(aceEvent.version, 1); + assert.equal(aceEvent.custom, 'Custom text'); + assert.equal(aceEvent.applicationKey, 'f00dcafe-abba-c0de-1dea-dabb1ed4caf3'); +}); diff --git a/packages/voice/tests/rest/v1/callouts/callouts.steps.ts b/packages/voice/tests/rest/v1/callouts/callouts.steps.ts new file mode 100644 index 00000000..4f2662dd --- /dev/null +++ b/packages/voice/tests/rest/v1/callouts/callouts.steps.ts @@ -0,0 +1,121 @@ +import { CalloutsApi, VoiceService, Voice } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let calloutsApi: CalloutsApi; +let ttsCallResponse: Voice.CalloutResponse; + +Given('the Voice service "Callouts" is available', () => { + const voiceService = new VoiceService({ + applicationKey: 'appKey', + applicationSecret: 'appSecret', + voiceHostname: 'http://localhost:3019', + }); + calloutsApi = voiceService.callouts; +}); + +When('I send a request to make a TTS call', async () => { + ttsCallResponse = await calloutsApi.tts({ + ttsCalloutRequestBody: { + method: 'ttsCallout', + ttsCallout: { + cli: '+12015555555', + destination: { + type: 'number', + endpoint: '+12017777777', + }, + locale: 'en-US', + text: 'Hello, this is a call from Sinch.', + }, + }, + }); +}); + +Then('the callout response contains the TTS call ID', () => { + assert.equal(ttsCallResponse.callId, '1ce0ffee-ca11-ca11-ca11-abcdef000001'); +}); + +When('I send a request to make a Conference call with the "Callout" service', async () => { + ttsCallResponse = await calloutsApi.conference({ + conferenceCalloutRequestBody: { + method: 'conferenceCallout', + conferenceCallout: { + cli: '+12015555555', + destination: { + type: 'number', + endpoint: '+12017777777', + }, + conferenceId: 'myConferenceId-E2E', + locale: 'en-US', + greeting: 'Welcome to this conference call.', + mohClass: 'music1', + }, + }, + }); +}); + +Then('the callout response contains the Conference call ID', () => { + assert.equal(ttsCallResponse.callId, '1ce0ffee-ca11-ca11-ca11-abcdef000002'); +}); + +When('I send a request to make a Custom call', async () => { + ttsCallResponse = await calloutsApi.custom({ + customCalloutRequestBody: { + method: 'customCallout', + customCallout: { + cli: '+12015555555', + destination: { + type: 'number', + endpoint: '+12017777777', + }, + custom: 'Custom text', + ice: Voice.customCalloutHelper.formatIceResponse( + Voice.iceActionHelper.connectPstn({ + number: '+12017777777', + cli: '+12015555555', + }), + Voice.iceInstructionHelper.say('Welcome to Sinch.', 'en-US/male'), + Voice.iceInstructionHelper.startRecording({ + destinationUrl: 'To specify', + credentials: 'To specify', + }), + ), + ace: Voice.customCalloutHelper.formatAceResponse( + Voice.aceActionHelper.runMenu({ + locale: 'Kimberly', + enableVoice: true, + barge: true, + menus: [ + { + id: 'main', + mainPrompt: '#tts[Welcome to the main menu. Press 1 to confirm order or 2 to cancel]', + repeatPrompt: '#tts[We didn\'t get your input, please try again]', + timeoutMills: 5000, + options: [ + { + dtmf: '1', + action: 'menu(confirm)', + }, + { + dtmf: '2', + action: 'return(cancel)', + }, + ], + }, + { + id: 'confirm', + mainPrompt: '#tts[Thank you for confirming your order. Enter your 4-digit PIN.]', + maxDigits: 4, + }, + ], + }), + ), + pie: 'https://callback-server.com/voice', + }, + }, + }); +}); + +Then('the callout response contains the Custom call ID', () => { + assert.equal(ttsCallResponse.callId, '1ce0ffee-ca11-ca11-ca11-abcdef000003'); +}); diff --git a/packages/voice/tests/rest/v1/calls/calls.steps.ts b/packages/voice/tests/rest/v1/calls/calls.steps.ts new file mode 100644 index 00000000..d16d218f --- /dev/null +++ b/packages/voice/tests/rest/v1/calls/calls.steps.ts @@ -0,0 +1,118 @@ +import { CallsApi, VoiceService, Voice } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let callsApi: CallsApi; +let callInformation: Voice.GetCallInformation; +let updateCallResponse: void; +let error: any; +let manageWithCallLegResponse: void; + +Given('the Voice service "Calls" is available', () => { + const voiceService = new VoiceService({ + applicationKey: 'appKey', + applicationSecret: 'appSecret', + voiceHostname: 'http://localhost:3019', + }); + callsApi = voiceService.calls; +}); + +When('I send a request to get a call\'s information', async () => { + callInformation = await callsApi.get({ + callId: '1ce0ffee-ca11-ca11-ca11-abcdef000003', + }); +}); + +Then('the response contains the information about the call', () => { + assert.equal(callInformation.callId, '1ce0ffee-ca11-ca11-ca11-abcdef000003'); + const participant: Voice.Participant = { + type: 'Number', + endpoint: '+12017777777', + }; + assert.deepEqual(callInformation.to, participant); + assert.equal(callInformation.domain, 'pstn'); + assert.equal(callInformation.duration, 14); + assert.equal(callInformation.status, 'FINAL'); + const result: Voice.ResultEnum = 'ANSWERED'; + assert.equal(callInformation.result, result); + const reason: Voice.ReasonEnum = 'MANAGERHANGUP'; + assert.equal(callInformation.reason, reason); + assert.deepEqual(callInformation.timestamp, new Date('2024-06-06T17:36:00Z')); + assert.equal(callInformation.custom, 'Custom text'); + const userRate: Voice.VoicePrice = { + currencyId: 'EUR', + amount: 0.1758, + }; + assert.deepEqual(callInformation.userRate, userRate); + const debit: Voice.VoicePrice = { + currencyId: 'EUR', + amount: 0.1758, + }; + assert.deepEqual(callInformation.debit, debit); +}); + +When('I send a request to update a call', async () => { + updateCallResponse = await callsApi.update({ + callId: '1ce0ffee-ca11-ca11-ca11-abcdef000022', + updateCallRequestBody: { + instructions: [ + Voice.svamlInstructionHelper.buildSay( + 'Sorry, the conference has been cancelled. The call will end now.', + 'en-US', + ), + ], + action: Voice.svamlActionHelper.buildHangup(), + }, + }); +}); + +Then('the update call response contains no data', () => { + assert.deepEqual(updateCallResponse, {}); +}); + +When('I send a request to update a call that doesn\'t exist', async () => { + updateCallResponse = undefined; + try { + updateCallResponse = await callsApi.update({ + callId: 'not-existing-callId', + updateCallRequestBody: { + instructions: [ + Voice.svamlInstructionHelper.buildSay( + 'Sorry, the conference has been cancelled. The call will end now.', + 'en-US', + ), + ], + action: Voice.svamlActionHelper.buildHangup(), + }, + }); + } catch (e) { + error = e; + } +}); + +Then('the update call response contains a "not found" error', () => { + assert.equal(updateCallResponse, undefined); + const voiceError = JSON.parse(error.data) as Voice.VoiceError; + assert.equal(voiceError.errorCode, 40400); + assert.equal(voiceError.message, 'Call not found'); + assert.equal(voiceError.reference, '38188074-abcd-56ab-ab64-daf82fada8e8'); +}); + +When('I send a request to manage a call with callLeg', async () => { + manageWithCallLegResponse = await callsApi.manageWithCallLeg({ + callId: '1ce0ffee-ca11-ca11-ca11-abcdef000032', + callLeg: 'callee', + manageWithCallLegRequestBody: { + instructions: [ + Voice.svamlInstructionHelper.buildPlayFiles( + ['https://samples-files.com/samples/Audio/mp3/sample-file-4.mp3'], + ), + ], + action: Voice.svamlActionHelper.buildContinue(), + }, + }); +}); + +Then('the manage a call with callLeg response contains no data', () => { + assert.deepEqual(manageWithCallLegResponse, {}); +}); diff --git a/packages/voice/tests/rest/v1/conferences/conferences.steps.ts b/packages/voice/tests/rest/v1/conferences/conferences.steps.ts new file mode 100644 index 00000000..b897190b --- /dev/null +++ b/packages/voice/tests/rest/v1/conferences/conferences.steps.ts @@ -0,0 +1,100 @@ +import { ConferencesApi, VoiceService, Voice } from '../../../../src'; +import { Given, When, Then } from '@cucumber/cucumber'; +import * as assert from 'assert'; + +let conferencesApi: ConferencesApi; +let conferenceCallResponse: Voice.CalloutResponse; +let conferenceInformation: Voice.GetConferenceInfoResponse; +let manageParticipantResponse: void; +let kickParticipantResponse: void; +let kickAllParticipantsResponse: void; + +Given('the Voice service "Conferences" is available', () => { + const voiceService = new VoiceService({ + applicationKey: 'appKey', + applicationSecret: 'appSecret', + voiceHostname: 'http://localhost:3019', + }); + conferencesApi = voiceService.conferences; +}); + +When('I send a request to make a Conference call with the "Conferences" service', async () => { + conferenceCallResponse = await conferencesApi.call({ + conferenceCalloutRequestBody: { + method: 'conferenceCallout', + conferenceCallout: { + cli: '+12015555555', + destination: { + type: 'number', + endpoint: '+12017777777', + }, + conferenceId: 'myConferenceId-E2E', + locale: 'en-US', + greeting: 'Welcome to this conference call.', + mohClass: 'music1', + }, + }, + }); +}); + +Then('the callout response from the "Conferences" service contains the Conference call ID', () => { + assert.equal(conferenceCallResponse.callId, '1ce0ffee-ca11-ca11-ca11-abcdef000002'); +}); + +When('I send a request to get the conference information', async () => { + conferenceInformation = await conferencesApi.get({ + conferenceId: 'myConferenceId-E2E', + }); +}); + +Then('the response contains the information about the conference participants', () => { + assert.ok(conferenceInformation.participants); + const participant1 = conferenceInformation.participants[0]; + assert.equal(participant1.id, '1ce0ffee-ca11-ca11-ca11-abcdef000012'); + assert.equal(participant1.cli, '+12015555555'); + assert.equal(participant1.duration, 35); + assert.equal(participant1.muted, true); + assert.equal(participant1.onhold, true); + const participant2 = conferenceInformation.participants[1]; + assert.equal(participant2.id, '1ce0ffee-ca11-ca11-ca11-abcdef000022'); + assert.equal(participant2.cli, '+12015555555'); + assert.equal(participant2.duration, 6); + assert.equal(participant2.muted, false); + assert.equal(participant2.onhold, false); +}); + +When('I send a request to put a participant on hold', async () => { + manageParticipantResponse = await conferencesApi.manageParticipant({ + conferenceId: 'myConferenceId-E2E', + callId: '1ce0ffee-ca11-ca11-ca11-abcdef000012', + manageParticipantRequestBody: { + command: 'onhold', + moh: 'music2', + }, + }); +}); + +Then('the manage participant response contains no data', () => { + assert.deepEqual(manageParticipantResponse, {}); +}); + +When('I send a request to kick a participant from a conference', async () => { + kickParticipantResponse = await conferencesApi.kickParticipant({ + conferenceId: 'myConferenceId-E2E', + callId: '1ce0ffee-ca11-ca11-ca11-abcdef000012', + }); +}); + +Then('the kick participant response contains no data', () => { + assert.deepEqual(kickParticipantResponse, {}); +}); + +When('I send a request to kick all the participants from a conference', async () => { + kickAllParticipantsResponse = await conferencesApi.kickAll({ + conferenceId: 'myConferenceId-E2E', + }); +}); + +Then('the kick all participants response contains no data', () => { + assert.deepEqual(kickAllParticipantsResponse, {}); +}); diff --git a/yarn.lock b/yarn.lock index c3649952..07f593bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -58,12 +58,25 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5": +"@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5": version "7.23.5" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz" integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.3": +"@babel/compat-data@^7.25.2", "@babel/compat-data@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.4.tgz#7d2a80ce229890edcf4cc259d4d696cb4dae2fcb" + integrity sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.23.6" resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz" integrity sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw== @@ -84,6 +97,27 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/core@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.25.2.tgz#ed8eec275118d7613e77a352894cd12ded8eba77" + integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.0" + "@babel/helper-compilation-targets" "^7.25.2" + "@babel/helper-module-transforms" "^7.25.2" + "@babel/helpers" "^7.25.0" + "@babel/parser" "^7.25.0" + "@babel/template" "^7.25.0" + "@babel/traverse" "^7.25.2" + "@babel/types" "^7.25.2" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/generator@^7.23.6", "@babel/generator@^7.7.2": version "7.23.6" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz" @@ -94,6 +128,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.25.0", "@babel/generator@^7.25.6": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.6.tgz#0df1ad8cb32fe4d2b01d8bf437f153d19342a87c" + integrity sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw== + dependencies: + "@babel/types" "^7.25.6" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz" @@ -101,14 +145,22 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": - version "7.22.15" - resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz" - integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== +"@babel/helper-annotate-as-pure@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz#5373c7bc8366b12a033b4be1ac13a206c6656aab" + integrity sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg== dependencies: - "@babel/types" "^7.22.15" + "@babel/types" "^7.24.7" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz#37d66feb012024f2422b762b9b2a7cfe27c7fba3" + integrity sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" -"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": +"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": version "7.23.6" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz" integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== @@ -119,22 +171,31 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.15", "@babel/helper-create-class-features-plugin@^7.23.6": - version "7.23.6" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz" - integrity sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw== +"@babel/helper-compilation-targets@^7.24.7", "@babel/helper-compilation-targets@^7.24.8", "@babel/helper-compilation-targets@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz#e1d9410a90974a3a5a66e84ff55ef62e3c02d06c" + integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-member-expression-to-functions" "^7.23.0" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/compat-data" "^7.25.2" + "@babel/helper-validator-option" "^7.24.8" + browserslist "^4.23.1" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.24.7", "@babel/helper-create-class-features-plugin@^7.25.0", "@babel/helper-create-class-features-plugin@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz#57eaf1af38be4224a9d9dd01ddde05b741f50e14" + integrity sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-member-expression-to-functions" "^7.24.8" + "@babel/helper-optimise-call-expression" "^7.24.7" + "@babel/helper-replace-supers" "^7.25.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/traverse" "^7.25.4" semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": +"@babel/helper-create-regexp-features-plugin@^7.18.6": version "7.22.15" resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz" integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== @@ -143,10 +204,19 @@ regexpu-core "^5.3.1" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.4.4": - version "0.4.4" - resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz" - integrity sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA== +"@babel/helper-create-regexp-features-plugin@^7.24.7", "@babel/helper-create-regexp-features-plugin@^7.25.0", "@babel/helper-create-regexp-features-plugin@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz#24c75974ed74183797ffd5f134169316cd1808d9" + integrity sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" + integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== dependencies: "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-plugin-utils" "^7.22.5" @@ -159,7 +229,7 @@ resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": +"@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz" integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== @@ -174,12 +244,13 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": - version "7.23.0" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz" - integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== +"@babel/helper-member-expression-to-functions@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz#6155e079c913357d24a4c20480db7c712a5c3fb6" + integrity sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA== dependencies: - "@babel/types" "^7.23.0" + "@babel/traverse" "^7.24.8" + "@babel/types" "^7.24.8" "@babel/helper-module-imports@^7.22.15": version "7.22.15" @@ -188,6 +259,14 @@ dependencies: "@babel/types" "^7.22.15" +"@babel/helper-module-imports@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" + integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-module-transforms@^7.23.3": version "7.23.3" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz" @@ -199,35 +278,50 @@ "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.20" -"@babel/helper-optimise-call-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz" - integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== +"@babel/helper-module-transforms@^7.24.7", "@babel/helper-module-transforms@^7.24.8", "@babel/helper-module-transforms@^7.25.0", "@babel/helper-module-transforms@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz#ee713c29768100f2776edf04d4eb23b8d27a66e6" + integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ== dependencies: - "@babel/types" "^7.22.5" + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + "@babel/traverse" "^7.25.2" + +"@babel/helper-optimise-call-expression@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz#8b0a0456c92f6b323d27cfd00d1d664e76692a0f" + integrity sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A== + dependencies: + "@babel/types" "^7.24.7" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.22.5" resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@babel/helper-remap-async-to-generator@^7.22.20": - version "7.22.20" - resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz" - integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== +"@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz#94ee67e8ec0e5d44ea7baeb51e571bd26af07878" + integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg== + +"@babel/helper-remap-async-to-generator@^7.24.7", "@babel/helper-remap-async-to-generator@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz#d2f0fbba059a42d68e5e378feaf181ef6055365e" + integrity sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-wrap-function" "^7.22.20" + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-wrap-function" "^7.25.0" + "@babel/traverse" "^7.25.0" -"@babel/helper-replace-supers@^7.22.20": - version "7.22.20" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz" - integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== +"@babel/helper-replace-supers@^7.24.7", "@babel/helper-replace-supers@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz#ff44deac1c9f619523fe2ca1fd650773792000a9" + integrity sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-member-expression-to-functions" "^7.22.15" - "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.24.8" + "@babel/helper-optimise-call-expression" "^7.24.7" + "@babel/traverse" "^7.25.0" "@babel/helper-simple-access@^7.22.5": version "7.22.5" @@ -236,12 +330,21 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": - version "7.22.5" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz" - integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== +"@babel/helper-simple-access@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" + integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== dependencies: - "@babel/types" "^7.22.5" + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-skip-transparent-expression-wrappers@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9" + integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" "@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" @@ -255,24 +358,39 @@ resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz" integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== +"@babel/helper-string-parser@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" + integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== + "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5": +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== + +"@babel/helper-validator-option@^7.23.5": version "7.23.5" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz" integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== -"@babel/helper-wrap-function@^7.22.20": - version "7.22.20" - resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz" - integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== +"@babel/helper-validator-option@^7.24.7", "@babel/helper-validator-option@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" + integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== + +"@babel/helper-wrap-function@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz#dab12f0f593d6ca48c0062c28bcfb14ebe812f81" + integrity sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ== dependencies: - "@babel/helper-function-name" "^7.22.5" - "@babel/template" "^7.22.15" - "@babel/types" "^7.22.19" + "@babel/template" "^7.25.0" + "@babel/traverse" "^7.25.0" + "@babel/types" "^7.25.0" "@babel/helpers@^7.23.6": version "7.23.6" @@ -283,6 +401,14 @@ "@babel/traverse" "^7.23.6" "@babel/types" "^7.23.6" +"@babel/helpers@^7.25.0": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.25.6.tgz#57ee60141829ba2e102f30711ffe3afab357cc60" + integrity sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q== + dependencies: + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.6" + "@babel/highlight@^7.23.4": version "7.23.4" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz" @@ -292,34 +418,66 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6": version "7.23.6" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz" integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz" - integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== +"@babel/parser@^7.25.0", "@babel/parser@^7.25.6": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f" + integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/types" "^7.25.6" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz" - integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.3": + version "7.25.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz#dca427b45a6c0f5c095a1c639dfe2476a3daba7f" + integrity sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/traverse" "^7.25.3" -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz" - integrity sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w== +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz#cd0c583e01369ef51676bdb3d7b603e17d2b3f73" + integrity sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.8" + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz#749bde80356b295390954643de7635e0dffabe73" + integrity sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz#e4eabdd5109acc399b38d7999b2ef66fc2022f89" + integrity sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/plugin-transform-optional-chaining" "^7.24.7" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz#3a82a70e7cb7294ad2559465ebcb871dfbf078fb" + integrity sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/traverse" "^7.25.0" "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" @@ -368,19 +526,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-import-assertions@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz" - integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== +"@babel/plugin-syntax-import-assertions@^7.24.7": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz#bb918905c58711b86f9710d74a3744b6c56573b5" + integrity sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.8" -"@babel/plugin-syntax-import-attributes@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz" - integrity sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA== +"@babel/plugin-syntax-import-attributes@^7.24.7": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz#6d4c78f042db0e82fd6436cd65fec5dc78ad2bde" + integrity sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.8" "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -396,7 +554,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.23.3", "@babel/plugin-syntax-jsx@^7.7.2": +"@babel/plugin-syntax-jsx@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" + integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-syntax-jsx@^7.7.2": version "7.23.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz" integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== @@ -459,7 +624,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.23.3", "@babel/plugin-syntax-typescript@^7.7.2": +"@babel/plugin-syntax-typescript@^7.24.7": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz#04db9ce5a9043d9c635e75ae7969a2cd50ca97ff" + integrity sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + +"@babel/plugin-syntax-typescript@^7.7.2": version "7.23.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz" integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== @@ -474,425 +646,432 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz" - integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== +"@babel/plugin-transform-arrow-functions@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz#4f6886c11e423bd69f3ce51dbf42424a5f275514" + integrity sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-async-generator-functions@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz" - integrity sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw== +"@babel/plugin-transform-async-generator-functions@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz#2afd4e639e2d055776c9f091b6c0c180ed8cf083" + integrity sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-remap-async-to-generator" "^7.25.0" "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/traverse" "^7.25.4" -"@babel/plugin-transform-async-to-generator@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz" - integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== +"@babel/plugin-transform-async-to-generator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz#72a3af6c451d575842a7e9b5a02863414355bdcc" + integrity sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA== dependencies: - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-remap-async-to-generator" "^7.24.7" -"@babel/plugin-transform-block-scoped-functions@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz" - integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== +"@babel/plugin-transform-block-scoped-functions@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz#a4251d98ea0c0f399dafe1a35801eaba455bbf1f" + integrity sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-block-scoping@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz" - integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== +"@babel/plugin-transform-block-scoping@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz#23a6ed92e6b006d26b1869b1c91d1b917c2ea2ac" + integrity sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.8" -"@babel/plugin-transform-class-properties@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz" - integrity sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg== +"@babel/plugin-transform-class-properties@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz#bae7dbfcdcc2e8667355cd1fb5eda298f05189fd" + integrity sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.25.4" + "@babel/helper-plugin-utils" "^7.24.8" -"@babel/plugin-transform-class-static-block@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz" - integrity sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ== +"@babel/plugin-transform-class-static-block@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz#c82027ebb7010bc33c116d4b5044fbbf8c05484d" + integrity sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.23.5": - version "7.23.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz" - integrity sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg== +"@babel/plugin-transform-classes@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz#d29dbb6a72d79f359952ad0b66d88518d65ef89a" + integrity sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-compilation-targets" "^7.25.2" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-replace-supers" "^7.25.0" + "@babel/traverse" "^7.25.4" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz" - integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== +"@babel/plugin-transform-computed-properties@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz#4cab3214e80bc71fae3853238d13d097b004c707" + integrity sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/template" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/template" "^7.24.7" -"@babel/plugin-transform-destructuring@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz" - integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== +"@babel/plugin-transform-destructuring@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz#c828e814dbe42a2718a838c2a2e16a408e055550" + integrity sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.8" -"@babel/plugin-transform-dotall-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz" - integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ== +"@babel/plugin-transform-dotall-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz#5f8bf8a680f2116a7207e16288a5f974ad47a7a0" + integrity sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-duplicate-keys@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz" - integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== +"@babel/plugin-transform-duplicate-keys@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz#dd20102897c9a2324e5adfffb67ff3610359a8ee" + integrity sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-dynamic-import@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz" - integrity sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ== +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz#809af7e3339466b49c034c683964ee8afb3e2604" + integrity sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.25.0" + "@babel/helper-plugin-utils" "^7.24.8" + +"@babel/plugin-transform-dynamic-import@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz#4d8b95e3bae2b037673091aa09cd33fecd6419f4" + integrity sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-transform-exponentiation-operator@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz" - integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ== +"@babel/plugin-transform-exponentiation-operator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz#b629ee22645f412024297d5245bce425c31f9b0d" + integrity sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-export-namespace-from@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz" - integrity sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ== +"@babel/plugin-transform-export-namespace-from@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz#176d52d8d8ed516aeae7013ee9556d540c53f197" + integrity sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-for-of@^7.23.6": - version "7.23.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz" - integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw== +"@babel/plugin-transform-for-of@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz#f25b33f72df1d8be76399e1b8f3f9d366eb5bc70" + integrity sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" -"@babel/plugin-transform-function-name@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz" - integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== +"@babel/plugin-transform-function-name@^7.25.1": + version "7.25.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz#b85e773097526c1a4fc4ba27322748643f26fc37" + integrity sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA== dependencies: - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-compilation-targets" "^7.24.8" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/traverse" "^7.25.1" -"@babel/plugin-transform-json-strings@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz" - integrity sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg== +"@babel/plugin-transform-json-strings@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz#f3e9c37c0a373fee86e36880d45b3664cedaf73a" + integrity sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-transform-literals@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz" - integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== +"@babel/plugin-transform-literals@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz#deb1ad14fc5490b9a65ed830e025bca849d8b5f3" + integrity sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.8" -"@babel/plugin-transform-logical-assignment-operators@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz" - integrity sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg== +"@babel/plugin-transform-logical-assignment-operators@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz#a58fb6eda16c9dc8f9ff1c7b1ba6deb7f4694cb0" + integrity sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-transform-member-expression-literals@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz" - integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== +"@babel/plugin-transform-member-expression-literals@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz#3b4454fb0e302e18ba4945ba3246acb1248315df" + integrity sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-modules-amd@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz" - integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw== +"@babel/plugin-transform-modules-amd@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz#65090ed493c4a834976a3ca1cde776e6ccff32d7" + integrity sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg== dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-module-transforms" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-modules-commonjs@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz" - integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== +"@babel/plugin-transform-modules-commonjs@^7.24.7", "@babel/plugin-transform-modules-commonjs@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz#ab6421e564b717cb475d6fff70ae7f103536ea3c" + integrity sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA== dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-module-transforms" "^7.24.8" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-simple-access" "^7.24.7" -"@babel/plugin-transform-modules-systemjs@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz" - integrity sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ== +"@babel/plugin-transform-modules-systemjs@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz#8f46cdc5f9e5af74f3bd019485a6cbe59685ea33" + integrity sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw== dependencies: - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-module-transforms" "^7.25.0" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-validator-identifier" "^7.24.7" + "@babel/traverse" "^7.25.0" -"@babel/plugin-transform-modules-umd@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz" - integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg== +"@babel/plugin-transform-modules-umd@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz#edd9f43ec549099620df7df24e7ba13b5c76efc8" + integrity sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A== dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-module-transforms" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz" - integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== +"@babel/plugin-transform-named-capturing-groups-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz#9042e9b856bc6b3688c0c2e4060e9e10b1460923" + integrity sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-new-target@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz" - integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ== +"@babel/plugin-transform-new-target@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz#31ff54c4e0555cc549d5816e4ab39241dfb6ab00" + integrity sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-nullish-coalescing-operator@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz" - integrity sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA== +"@babel/plugin-transform-nullish-coalescing-operator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz#1de4534c590af9596f53d67f52a92f12db984120" + integrity sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-transform-numeric-separator@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz" - integrity sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q== +"@babel/plugin-transform-numeric-separator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz#bea62b538c80605d8a0fac9b40f48e97efa7de63" + integrity sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-transform-object-rest-spread@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz" - integrity sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g== +"@babel/plugin-transform-object-rest-spread@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz#d13a2b93435aeb8a197e115221cab266ba6e55d6" + integrity sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q== dependencies: - "@babel/compat-data" "^7.23.3" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-compilation-targets" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.23.3" + "@babel/plugin-transform-parameters" "^7.24.7" -"@babel/plugin-transform-object-super@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz" - integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== +"@babel/plugin-transform-object-super@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz#66eeaff7830bba945dd8989b632a40c04ed625be" + integrity sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-replace-supers" "^7.24.7" -"@babel/plugin-transform-optional-catch-binding@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz" - integrity sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A== +"@babel/plugin-transform-optional-catch-binding@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz#00eabd883d0dd6a60c1c557548785919b6e717b4" + integrity sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.23.3", "@babel/plugin-transform-optional-chaining@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz" - integrity sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA== +"@babel/plugin-transform-optional-chaining@^7.24.7", "@babel/plugin-transform-optional-chaining@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz#bb02a67b60ff0406085c13d104c99a835cdf365d" + integrity sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-transform-parameters@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz" - integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== +"@babel/plugin-transform-parameters@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz#5881f0ae21018400e320fc7eb817e529d1254b68" + integrity sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-private-methods@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz" - integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g== +"@babel/plugin-transform-private-methods@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz#9bbefbe3649f470d681997e0b64a4b254d877242" + integrity sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.25.4" + "@babel/helper-plugin-utils" "^7.24.8" -"@babel/plugin-transform-private-property-in-object@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz" - integrity sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A== +"@babel/plugin-transform-private-property-in-object@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz#4eec6bc701288c1fab5f72e6a4bbc9d67faca061" + integrity sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-create-class-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-transform-property-literals@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz" - integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== +"@babel/plugin-transform-property-literals@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz#f0d2ed8380dfbed949c42d4d790266525d63bbdc" + integrity sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-regenerator@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz" - integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ== +"@babel/plugin-transform-regenerator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz#021562de4534d8b4b1851759fd7af4e05d2c47f8" + integrity sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" regenerator-transform "^0.15.2" -"@babel/plugin-transform-reserved-words@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz" - integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-shorthand-properties@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz" - integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-spread@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz" - integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - -"@babel/plugin-transform-sticky-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz" - integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-template-literals@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz" - integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-typeof-symbol@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz" - integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-typescript@^7.23.3": - version "7.23.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz" - integrity sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.23.6" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-typescript" "^7.23.3" - -"@babel/plugin-transform-unicode-escapes@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz" - integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-property-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz" - integrity sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz" - integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-sets-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz" - integrity sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/preset-env@^7.23.3": - version "7.23.6" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.6.tgz" - integrity sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ== - dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.23.5" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.3" +"@babel/plugin-transform-reserved-words@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz#80037fe4fbf031fc1125022178ff3938bb3743a4" + integrity sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-shorthand-properties@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz#85448c6b996e122fa9e289746140aaa99da64e73" + integrity sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-spread@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz#e8a38c0fde7882e0fb8f160378f74bd885cc7bb3" + integrity sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + +"@babel/plugin-transform-sticky-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz#96ae80d7a7e5251f657b5cf18f1ea6bf926f5feb" + integrity sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-template-literals@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz#a05debb4a9072ae8f985bcf77f3f215434c8f8c8" + integrity sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-typeof-symbol@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz#383dab37fb073f5bfe6e60c654caac309f92ba1c" + integrity sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + +"@babel/plugin-transform-typescript@^7.24.7": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.2.tgz#237c5d10de6d493be31637c6b9fa30b6c5461add" + integrity sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-create-class-features-plugin" "^7.25.0" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/plugin-syntax-typescript" "^7.24.7" + +"@babel/plugin-transform-unicode-escapes@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz#2023a82ced1fb4971630a2e079764502c4148e0e" + integrity sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-unicode-property-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz#9073a4cd13b86ea71c3264659590ac086605bbcd" + integrity sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-unicode-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz#dfc3d4a51127108099b19817c0963be6a2adf19f" + integrity sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-unicode-sets-regex@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz#be664c2a0697ffacd3423595d5edef6049e8946c" + integrity sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.2" + "@babel/helper-plugin-utils" "^7.24.8" + +"@babel/preset-env@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.25.4.tgz#be23043d43a34a2721cd0f676c7ba6f1481f6af6" + integrity sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw== + dependencies: + "@babel/compat-data" "^7.25.4" + "@babel/helper-compilation-targets" "^7.25.2" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-validator-option" "^7.24.8" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.3" + "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.0" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.24.7" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.0" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.23.3" - "@babel/plugin-syntax-import-attributes" "^7.23.3" + "@babel/plugin-syntax-import-assertions" "^7.24.7" + "@babel/plugin-syntax-import-attributes" "^7.24.7" "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -904,59 +1083,60 @@ "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.23.3" - "@babel/plugin-transform-async-generator-functions" "^7.23.4" - "@babel/plugin-transform-async-to-generator" "^7.23.3" - "@babel/plugin-transform-block-scoped-functions" "^7.23.3" - "@babel/plugin-transform-block-scoping" "^7.23.4" - "@babel/plugin-transform-class-properties" "^7.23.3" - "@babel/plugin-transform-class-static-block" "^7.23.4" - "@babel/plugin-transform-classes" "^7.23.5" - "@babel/plugin-transform-computed-properties" "^7.23.3" - "@babel/plugin-transform-destructuring" "^7.23.3" - "@babel/plugin-transform-dotall-regex" "^7.23.3" - "@babel/plugin-transform-duplicate-keys" "^7.23.3" - "@babel/plugin-transform-dynamic-import" "^7.23.4" - "@babel/plugin-transform-exponentiation-operator" "^7.23.3" - "@babel/plugin-transform-export-namespace-from" "^7.23.4" - "@babel/plugin-transform-for-of" "^7.23.6" - "@babel/plugin-transform-function-name" "^7.23.3" - "@babel/plugin-transform-json-strings" "^7.23.4" - "@babel/plugin-transform-literals" "^7.23.3" - "@babel/plugin-transform-logical-assignment-operators" "^7.23.4" - "@babel/plugin-transform-member-expression-literals" "^7.23.3" - "@babel/plugin-transform-modules-amd" "^7.23.3" - "@babel/plugin-transform-modules-commonjs" "^7.23.3" - "@babel/plugin-transform-modules-systemjs" "^7.23.3" - "@babel/plugin-transform-modules-umd" "^7.23.3" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" - "@babel/plugin-transform-new-target" "^7.23.3" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4" - "@babel/plugin-transform-numeric-separator" "^7.23.4" - "@babel/plugin-transform-object-rest-spread" "^7.23.4" - "@babel/plugin-transform-object-super" "^7.23.3" - "@babel/plugin-transform-optional-catch-binding" "^7.23.4" - "@babel/plugin-transform-optional-chaining" "^7.23.4" - "@babel/plugin-transform-parameters" "^7.23.3" - "@babel/plugin-transform-private-methods" "^7.23.3" - "@babel/plugin-transform-private-property-in-object" "^7.23.4" - "@babel/plugin-transform-property-literals" "^7.23.3" - "@babel/plugin-transform-regenerator" "^7.23.3" - "@babel/plugin-transform-reserved-words" "^7.23.3" - "@babel/plugin-transform-shorthand-properties" "^7.23.3" - "@babel/plugin-transform-spread" "^7.23.3" - "@babel/plugin-transform-sticky-regex" "^7.23.3" - "@babel/plugin-transform-template-literals" "^7.23.3" - "@babel/plugin-transform-typeof-symbol" "^7.23.3" - "@babel/plugin-transform-unicode-escapes" "^7.23.3" - "@babel/plugin-transform-unicode-property-regex" "^7.23.3" - "@babel/plugin-transform-unicode-regex" "^7.23.3" - "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" + "@babel/plugin-transform-arrow-functions" "^7.24.7" + "@babel/plugin-transform-async-generator-functions" "^7.25.4" + "@babel/plugin-transform-async-to-generator" "^7.24.7" + "@babel/plugin-transform-block-scoped-functions" "^7.24.7" + "@babel/plugin-transform-block-scoping" "^7.25.0" + "@babel/plugin-transform-class-properties" "^7.25.4" + "@babel/plugin-transform-class-static-block" "^7.24.7" + "@babel/plugin-transform-classes" "^7.25.4" + "@babel/plugin-transform-computed-properties" "^7.24.7" + "@babel/plugin-transform-destructuring" "^7.24.8" + "@babel/plugin-transform-dotall-regex" "^7.24.7" + "@babel/plugin-transform-duplicate-keys" "^7.24.7" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.0" + "@babel/plugin-transform-dynamic-import" "^7.24.7" + "@babel/plugin-transform-exponentiation-operator" "^7.24.7" + "@babel/plugin-transform-export-namespace-from" "^7.24.7" + "@babel/plugin-transform-for-of" "^7.24.7" + "@babel/plugin-transform-function-name" "^7.25.1" + "@babel/plugin-transform-json-strings" "^7.24.7" + "@babel/plugin-transform-literals" "^7.25.2" + "@babel/plugin-transform-logical-assignment-operators" "^7.24.7" + "@babel/plugin-transform-member-expression-literals" "^7.24.7" + "@babel/plugin-transform-modules-amd" "^7.24.7" + "@babel/plugin-transform-modules-commonjs" "^7.24.8" + "@babel/plugin-transform-modules-systemjs" "^7.25.0" + "@babel/plugin-transform-modules-umd" "^7.24.7" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.24.7" + "@babel/plugin-transform-new-target" "^7.24.7" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.24.7" + "@babel/plugin-transform-numeric-separator" "^7.24.7" + "@babel/plugin-transform-object-rest-spread" "^7.24.7" + "@babel/plugin-transform-object-super" "^7.24.7" + "@babel/plugin-transform-optional-catch-binding" "^7.24.7" + "@babel/plugin-transform-optional-chaining" "^7.24.8" + "@babel/plugin-transform-parameters" "^7.24.7" + "@babel/plugin-transform-private-methods" "^7.25.4" + "@babel/plugin-transform-private-property-in-object" "^7.24.7" + "@babel/plugin-transform-property-literals" "^7.24.7" + "@babel/plugin-transform-regenerator" "^7.24.7" + "@babel/plugin-transform-reserved-words" "^7.24.7" + "@babel/plugin-transform-shorthand-properties" "^7.24.7" + "@babel/plugin-transform-spread" "^7.24.7" + "@babel/plugin-transform-sticky-regex" "^7.24.7" + "@babel/plugin-transform-template-literals" "^7.24.7" + "@babel/plugin-transform-typeof-symbol" "^7.24.8" + "@babel/plugin-transform-unicode-escapes" "^7.24.7" + "@babel/plugin-transform-unicode-property-regex" "^7.24.7" + "@babel/plugin-transform-unicode-regex" "^7.24.7" + "@babel/plugin-transform-unicode-sets-regex" "^7.25.4" "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.6" - babel-plugin-polyfill-corejs3 "^0.8.5" - babel-plugin-polyfill-regenerator "^0.5.3" - core-js-compat "^3.31.0" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.10.6" + babel-plugin-polyfill-regenerator "^0.6.1" + core-js-compat "^3.37.1" semver "^6.3.1" "@babel/preset-modules@0.1.6-no-external-plugins": @@ -968,16 +1148,16 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-typescript@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz" - integrity sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ== +"@babel/preset-typescript@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz#66cd86ea8f8c014855671d5ea9a737139cbbfef1" + integrity sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.15" - "@babel/plugin-syntax-jsx" "^7.23.3" - "@babel/plugin-transform-modules-commonjs" "^7.23.3" - "@babel/plugin-transform-typescript" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-validator-option" "^7.24.7" + "@babel/plugin-syntax-jsx" "^7.24.7" + "@babel/plugin-transform-modules-commonjs" "^7.24.7" + "@babel/plugin-transform-typescript" "^7.24.7" "@babel/regjsgen@^0.8.0": version "0.8.0" @@ -1000,6 +1180,15 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" +"@babel/template@^7.24.7", "@babel/template@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a" + integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.25.0" + "@babel/types" "^7.25.0" + "@babel/traverse@^7.23.6": version "7.23.6" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz" @@ -1016,7 +1205,20 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" + integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.6" + "@babel/parser" "^7.25.6" + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.6" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.23.6" resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz" integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== @@ -1025,6 +1227,15 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" + integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== + dependencies: + "@babel/helper-string-parser" "^7.24.8" + "@babel/helper-validator-identifier" "^7.24.7" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" @@ -1042,6 +1253,116 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@cucumber/ci-environment@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/ci-environment/-/ci-environment-10.0.1.tgz#c8584f1d4a619e4318cf60c01b838db096d72ccd" + integrity sha512-/+ooDMPtKSmvcPMDYnMZt4LuoipfFfHaYspStI4shqw8FyKcfQAmekz6G+QKWjQQrvM+7Hkljwx58MEwPCwwzg== + +"@cucumber/cucumber-expressions@17.1.0": + version "17.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber-expressions/-/cucumber-expressions-17.1.0.tgz#1a428548a2c98ef3224bd484fc5666b4f7153a72" + integrity sha512-PCv/ppsPynniKPWJr5v566daCVe+pbxQpHGrIu/Ev57cCH9Rv+X0F6lio4Id3Z64TaG7btCRLUGewIgLwmrwOA== + dependencies: + regexp-match-indices "1.0.2" + +"@cucumber/cucumber@^10.3.1": + version "10.8.0" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber/-/cucumber-10.8.0.tgz#d906b451a07a91c254f90b8e2dfed4cf8ade3fcf" + integrity sha512-o8SR6MRQVCKKw4tytWqCqOjfX4cASj9dqpdHKHMi09rZWBCNQHBwH1TO9wj7NKjOa6RfUOTcgvDlayTcjyCH4A== + dependencies: + "@cucumber/ci-environment" "10.0.1" + "@cucumber/cucumber-expressions" "17.1.0" + "@cucumber/gherkin" "28.0.0" + "@cucumber/gherkin-streams" "5.0.1" + "@cucumber/gherkin-utils" "9.0.0" + "@cucumber/html-formatter" "21.3.1" + "@cucumber/message-streams" "4.0.1" + "@cucumber/messages" "24.1.0" + "@cucumber/tag-expressions" "6.1.0" + assertion-error-formatter "^3.0.0" + capital-case "^1.0.4" + chalk "^4.1.2" + cli-table3 "0.6.3" + commander "^10.0.0" + debug "^4.3.4" + error-stack-parser "^2.1.4" + figures "^3.2.0" + glob "^10.3.10" + has-ansi "^4.0.1" + indent-string "^4.0.0" + is-installed-globally "^0.4.0" + is-stream "^2.0.0" + knuth-shuffle-seeded "^1.0.6" + lodash.merge "^4.6.2" + lodash.mergewith "^4.6.2" + luxon "3.2.1" + mkdirp "^2.1.5" + mz "^2.7.0" + progress "^2.0.3" + read-pkg-up "^7.0.1" + resolve-pkg "^2.0.0" + semver "7.5.3" + string-argv "0.3.1" + strip-ansi "6.0.1" + supports-color "^8.1.1" + tmp "0.2.3" + type-fest "^4.8.3" + util-arity "^1.1.0" + xmlbuilder "^15.1.1" + yaml "^2.2.2" + yup "1.2.0" + +"@cucumber/gherkin-streams@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin-streams/-/gherkin-streams-5.0.1.tgz#8c2142d295cd05644456be7282b4bd756c95c4cd" + integrity sha512-/7VkIE/ASxIP/jd4Crlp4JHXqdNFxPGQokqWqsaCCiqBiu5qHoKMxcWNlp9njVL/n9yN4S08OmY3ZR8uC5x74Q== + dependencies: + commander "9.1.0" + source-map-support "0.5.21" + +"@cucumber/gherkin-utils@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin-utils/-/gherkin-utils-9.0.0.tgz#944c64c458742d8e73b750e5dde2cf56b161d674" + integrity sha512-clk4q39uj7pztZuZtyI54V8lRsCUz0Y/p8XRjIeHh7ExeEztpWkp4ca9q1FjUOPfQQ8E7OgqFbqoQQXZ1Bx7fw== + dependencies: + "@cucumber/gherkin" "^28.0.0" + "@cucumber/messages" "^24.0.0" + "@teppeis/multimaps" "3.0.0" + commander "12.0.0" + source-map-support "^0.5.21" + +"@cucumber/gherkin@28.0.0", "@cucumber/gherkin@^28.0.0": + version "28.0.0" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-28.0.0.tgz#91246da622524807b21430c1692bedd319d3d4bb" + integrity sha512-Ee6zJQq0OmIUPdW0mSnsCsrWA2PZAELNDPICD2pLfs0Oz7RAPgj80UsD2UCtqyAhw2qAR62aqlktKUlai5zl/A== + dependencies: + "@cucumber/messages" ">=19.1.4 <=24" + +"@cucumber/html-formatter@21.3.1": + version "21.3.1" + resolved "https://registry.yarnpkg.com/@cucumber/html-formatter/-/html-formatter-21.3.1.tgz#1c832d95891c1e961d9e6a4da4664c09a1332768" + integrity sha512-M1zbre7e8MsecXheqNv62BKY5J06YJSv1LmsD7sJ3mu5t1jirLjj2It1HqPsX5CQAfg9n69xFRugPgLMSte9TA== + +"@cucumber/message-streams@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/message-streams/-/message-streams-4.0.1.tgz#a5339d3504594bb2edb5732aaae94dddb24d0970" + integrity sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA== + +"@cucumber/messages@24.1.0", "@cucumber/messages@>=19.1.4 <=24", "@cucumber/messages@^24.0.0": + version "24.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-24.1.0.tgz#a212c97b0548144c3ccfae021a96d6c56d3841d3" + integrity sha512-hxVHiBurORcobhVk80I9+JkaKaNXkW6YwGOEFIh/2aO+apAN+5XJgUUWjng9NwqaQrW1sCFuawLB1AuzmBaNdQ== + dependencies: + "@types/uuid" "9.0.8" + class-transformer "0.5.1" + reflect-metadata "0.2.1" + uuid "9.0.1" + +"@cucumber/tag-expressions@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/tag-expressions/-/tag-expressions-6.1.0.tgz#cb7af908bdb43669b7574c606f71fa707196e962" + integrity sha512-+3DwRumrCJG27AtzCIL37A/X+A/gSfxOPLg8pZaruh5SLumsTmpvilwroVWBT2fPzmno/tGXypeK5a7NHU4RzA== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" @@ -1081,7 +1402,7 @@ "@gar/promisify@^1.1.3": version "1.1.3" - resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== "@humanwhocodes/config-array@^0.11.13": @@ -1337,6 +1658,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz" @@ -1347,6 +1677,11 @@ resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/source-map@^0.3.3": version "0.3.5" resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz" @@ -1384,9 +1719,17 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@lerna/child-process@7.4.2": version "7.4.2" - resolved "https://registry.npmjs.org/@lerna/child-process/-/child-process-7.4.2.tgz" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.2.tgz#a2fd013ac2150dc288270d3e0d0b850c06bec511" integrity sha512-je+kkrfcvPcwL5Tg8JRENRqlbzjdlZXyaR88UcnCdNW0AJ1jX9IfHRys1X7AwSroU2ug8ESNC+suoBw1vX833Q== dependencies: chalk "^4.1.0" @@ -1395,7 +1738,7 @@ "@lerna/create@7.4.2": version "7.4.2" - resolved "https://registry.npmjs.org/@lerna/create/-/create-7.4.2.tgz" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.4.2.tgz#f845fad1480e46555af98bd39af29571605dddc9" integrity sha512-1wplFbQ52K8E/unnqB0Tq39Z4e+NEoNrpovEnl6GpsTUrC6WDp8+w0Le2uCBV0hXyemxChduCkLz4/y1H1wTeg== dependencies: "@lerna/child-process" "7.4.2" @@ -1504,37 +1847,37 @@ webpack "5.89.0" webpack-node-externals "3.0.0" -"@nestjs/common@^10.3.7": - version "10.3.7" - resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.3.7.tgz#38ab5ff92277cf1f26f4749c264524e76962cfff" - integrity sha512-gKFtFzcJznrwsRYjtNZoPAvSOPYdNgxbTYoAyLTpoy393cIKgLmJTHu6ReH8/qIB9AaZLdGaFLkx98W/tFWFUw== +"@nestjs/common@^10.4.4": + version "10.4.4" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.4.4.tgz#673d0eca273e1ab3a4d3ec9e212114b9f4fbf6e8" + integrity sha512-0j2/zqRw9nvHV1GKTktER8B/hIC/Z8CYFjN/ZqUuvwayCH+jZZBhCR2oRyuvLTXdnlSmtCAg2xvQ0ULqQvzqhA== dependencies: uid "2.0.2" iterare "1.2.1" - tslib "2.6.2" + tslib "2.7.0" -"@nestjs/core@^10.3.7": - version "10.3.7" - resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.3.7.tgz#736906ec27bc39b91519506babc231c8ab56ea21" - integrity sha512-hsdlnfiQ3kgqHL5k7js3CU0PV7hBJVi+LfFMgCkoagRxNMf67z0GFGeOV2jk5d65ssB19qdYsDa1MGVuEaoUpg== +"@nestjs/core@^10.4.4": + version "10.4.4" + resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.4.4.tgz#12cb1110da6d76e12ceccf0e92f6f5220fe27525" + integrity sha512-y9tjmAzU6LTh1cC/lWrRsCcOd80khSR0qAHAqwY2svbW+AhsR/XCzgpZrAAKJrm/dDfjLCZKyxJSayeirGcW5Q== dependencies: uid "2.0.2" "@nuxtjs/opencollective" "0.3.2" fast-safe-stringify "2.1.1" iterare "1.2.1" - path-to-regexp "3.2.0" - tslib "2.6.2" + path-to-regexp "3.3.0" + tslib "2.7.0" -"@nestjs/platform-express@^10.3.7": - version "10.3.7" - resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.3.7.tgz#ae3fc59609bdc0ffc5029a6e74d59a5d1e257eef" - integrity sha512-noNJ+PyIxQJLCKfuXz0tcQtlVAynfLIuKy62g70lEZ86UrIqSrZFqvWs/rFUgkbT6J8H7Rmv11hASOnX+7M2rA== +"@nestjs/platform-express@^10.4.4": + version "10.4.4" + resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.4.4.tgz#582d375272207f8d1528f77ff470ba815d9d9846" + integrity sha512-y52q1MxhbHaT3vAgWd08RgiYon0lJgtTa8U6g6gV0KI0IygwZhDQFJVxnrRDUdxQGIP5CKHmfQu3sk9gTNFoEA== dependencies: - body-parser "1.20.2" + body-parser "1.20.3" cors "2.8.5" - express "4.19.2" + express "4.21.0" multer "1.4.4-lts.1" - tslib "2.6.2" + tslib "2.7.0" "@nestjs/schematics@^10.0.0", "@nestjs/schematics@^10.0.1": version "10.1.0" @@ -1570,7 +1913,7 @@ "@npmcli/fs@^2.1.0": version "2.1.2" - resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== dependencies: "@gar/promisify" "^1.1.3" @@ -1585,7 +1928,7 @@ "@npmcli/git@^4.0.0": version "4.1.0" - resolved "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-4.1.0.tgz#ab0ad3fd82bc4d8c1351b6c62f0fa56e8fe6afa6" integrity sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ== dependencies: "@npmcli/promise-spawn" "^6.0.0" @@ -1607,7 +1950,7 @@ "@npmcli/move-file@^2.0.0": version "2.0.1" - resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== dependencies: mkdirp "^1.0.4" @@ -1620,14 +1963,14 @@ "@npmcli/promise-spawn@^6.0.0", "@npmcli/promise-spawn@^6.0.1": version "6.0.2" - resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz#c8bc4fa2bd0f01cb979d8798ba038f314cfa70f2" integrity sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg== dependencies: which "^3.0.0" "@npmcli/run-script@6.0.2", "@npmcli/run-script@^6.0.0": version "6.0.2" - resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-6.0.2.tgz#a25452d45ee7f7fb8c16dfaf9624423c0c0eb885" integrity sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA== dependencies: "@npmcli/node-gyp" "^3.0.0" @@ -1638,14 +1981,14 @@ "@nrwl/devkit@16.10.0": version "16.10.0" - resolved "https://registry.npmjs.org/@nrwl/devkit/-/devkit-16.10.0.tgz" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.10.0.tgz#ac8c5b4db00f12c4b817c937be2f7c4eb8f2593c" integrity sha512-fRloARtsDQoQgQ7HKEy0RJiusg/HSygnmg4gX/0n/Z+SUS+4KoZzvHjXc6T5ZdEiSjvLypJ+HBM8dQzIcVACPQ== dependencies: "@nx/devkit" "16.10.0" "@nrwl/tao@16.10.0": version "16.10.0" - resolved "https://registry.npmjs.org/@nrwl/tao/-/tao-16.10.0.tgz" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-16.10.0.tgz#94642a0380709b8e387e1e33705a5a9624933375" integrity sha512-QNAanpINbr+Pod6e1xNgFbzK1x5wmZl+jMocgiEFXZ67KHvmbD6MAQQr0MMz+GPhIu7EE4QCTLTyCEMlAG+K5Q== dependencies: nx "16.10.0" @@ -1662,7 +2005,7 @@ "@nx/devkit@16.10.0", "@nx/devkit@>=16.5.1 < 17": version "16.10.0" - resolved "https://registry.npmjs.org/@nx/devkit/-/devkit-16.10.0.tgz" + resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-16.10.0.tgz#7e466be2dee2dcb1ccaf286786ca2a0a639aa007" integrity sha512-IvKQqRJFDDiaj33SPfGd3ckNHhHi6ceEoqCbAP4UuMXOPPVOX6H0KVk+9tknkPb48B7jWIw6/AgOeWkBxPRO5w== dependencies: "@nrwl/devkit" "16.10.0" @@ -1675,7 +2018,7 @@ "@nx/nx-darwin-arm64@16.10.0": version "16.10.0" - resolved "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.10.0.tgz" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.10.0.tgz#0c73010cac7a502549483b12bad347da9014e6f1" integrity sha512-YF+MIpeuwFkyvM5OwgY/rTNRpgVAI/YiR0yTYCZR+X3AAvP775IVlusNgQ3oedTBRUzyRnI4Tknj1WniENFsvQ== "@nx/nx-darwin-x64@16.10.0": @@ -1841,7 +2184,7 @@ "@parcel/watcher@2.0.4": version "2.0.4" - resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" integrity sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg== dependencies: node-addon-api "^3.2.1" @@ -1871,19 +2214,19 @@ "@sigstore/bundle@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1" integrity sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog== dependencies: "@sigstore/protobuf-specs" "^0.2.0" "@sigstore/protobuf-specs@^0.2.0": version "0.2.1" - resolved "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz#be9ef4f3c38052c43bd399d3f792c97ff9e2277b" integrity sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A== "@sigstore/sign@^1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@sigstore/sign/-/sign-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/@sigstore/sign/-/sign-1.0.0.tgz#6b08ebc2f6c92aa5acb07a49784cb6738796f7b4" integrity sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA== dependencies: "@sigstore/bundle" "^1.1.0" @@ -1892,7 +2235,7 @@ "@sigstore/tuf@^1.0.3": version "1.0.3" - resolved "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/@sigstore/tuf/-/tuf-1.0.3.tgz#2a65986772ede996485728f027b0514c0b70b160" integrity sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg== dependencies: "@sigstore/protobuf-specs" "^0.2.0" @@ -1917,9 +2260,14 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@teppeis/multimaps@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@teppeis/multimaps/-/multimaps-3.0.0.tgz#bb9c3f8d569f589e548586fa0bbf423010ddfdc5" + integrity sha512-ID7fosbc50TbT0MK0EG12O+gAP3W3Aa/Pz4DaTtQtEvlc9Odaqi0de+xuZ7Li2GtK4HzEX7IuRWS/JmZLksR3Q== + "@tootallnate/once@2": version "2.0.0" - resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@tsconfig/node10@^1.0.7": @@ -1944,12 +2292,12 @@ "@tufjs/canonical-json@1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz#eade9fd1f537993bc1f0949f3aea276ecc4fab31" integrity sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ== "@tufjs/models@1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-1.0.4.tgz#5a689630f6b9dbda338d4b208019336562f176ef" integrity sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A== dependencies: "@tufjs/canonical-json" "1.0.0" @@ -2083,10 +2431,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.1": - version "29.5.11" - resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz" - integrity sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ== +"@types/jest@^29.5.13": + version "29.5.13" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc" + integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2187,6 +2535,11 @@ dependencies: "@types/node" "*" +"@types/uuid@9.0.8": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" @@ -2569,7 +2922,7 @@ "@zkochan/js-yaml@0.0.6": version "0.0.6" - resolved "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz" + resolved "https://registry.yarnpkg.com/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz#975f0b306e705e28b8068a07737fa46d3fc04826" integrity sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg== dependencies: argparse "^2.0.1" @@ -2584,7 +2937,7 @@ JSONStream@^1.3.5: abbrev@^1.0.0: version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== accepts@~1.3.8: @@ -2627,14 +2980,14 @@ add-stream@^1.0.0: agent-base@6, agent-base@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" agentkeepalive@^4.2.1: version "4.5.0" - resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== dependencies: humanize-ms "^1.2.1" @@ -2691,6 +3044,11 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: dependencies: type-fest "^0.21.3" +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" @@ -2725,6 +3083,11 @@ ansi-styles@^6.1.0: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" @@ -2740,12 +3103,12 @@ append-field@^1.0.0: "aproba@^1.0.3 || ^2.0.0": version "2.0.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== are-we-there-yet@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== dependencies: delegates "^1.0.0" @@ -2803,6 +3166,15 @@ arrify@^2.0.1: resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== +assertion-error-formatter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz#be9c8825dee6a8a6c72183d915912d9b57d5d265" + integrity sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ== + dependencies: + diff "^4.0.1" + pad-right "^0.2.2" + repeat-string "^1.6.1" + async@^3.2.3: version "3.2.5" resolved "https://registry.npmjs.org/async/-/async-3.2.5.tgz" @@ -2814,11 +3186,11 @@ asynckit@^0.4.0: integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== axios@^1.0.0: - version "1.6.2" - resolved "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -2856,29 +3228,29 @@ babel-plugin-jest-hoist@^29.6.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.4.6: - version "0.4.7" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz" - integrity sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ== +babel-plugin-polyfill-corejs2@^0.4.10: + version "0.4.11" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" + integrity sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== dependencies: "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.4.4" + "@babel/helper-define-polyfill-provider" "^0.6.2" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.8.5: - version "0.8.7" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz" - integrity sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA== +babel-plugin-polyfill-corejs3@^0.10.6: + version "0.10.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7" + integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.4" - core-js-compat "^3.33.1" + "@babel/helper-define-polyfill-provider" "^0.6.2" + core-js-compat "^3.38.0" -babel-plugin-polyfill-regenerator@^0.5.3: - version "0.5.4" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz" - integrity sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg== +babel-plugin-polyfill-regenerator@^0.6.1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz#addc47e240edd1da1058ebda03021f382bba785e" + integrity sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.4" + "@babel/helper-define-polyfill-provider" "^0.6.2" babel-preset-current-node-syntax@^1.0.0: version "1.0.1" @@ -2940,10 +3312,10 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -body-parser@1.20.2: - version "1.20.2" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz" - integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== dependencies: bytes "3.1.2" content-type "~1.0.5" @@ -2953,7 +3325,7 @@ body-parser@1.20.2: http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.11.0" + qs "6.13.0" raw-body "2.5.2" type-is "~1.6.18" unpipe "1.0.0" @@ -2997,6 +3369,16 @@ browserslist@^4.14.5, browserslist@^4.22.2: node-releases "^2.0.14" update-browserslist-db "^1.0.13" +browserslist@^4.23.1, browserslist@^4.23.3: + version "4.24.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.0.tgz#a1325fe4bc80b64fda169629fc01b3d6cecd38d4" + integrity sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A== + dependencies: + caniuse-lite "^1.0.30001663" + electron-to-chromium "^1.5.28" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" + bser@2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" @@ -3019,7 +3401,7 @@ buffer@^5.5.0: builtins@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== builtins@^5.0.0: @@ -3055,7 +3437,7 @@ bytes@3.1.2: cacache@^16.1.0: version "16.1.3" - resolved "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== dependencies: "@npmcli/fs" "^2.1.0" @@ -3079,7 +3461,7 @@ cacache@^16.1.0: cacache@^17.0.0: version "17.1.4" - resolved "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.4.tgz#b3ff381580b47e85c6e64f801101508e26604b35" integrity sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A== dependencies: "@npmcli/fs" "^3.1.0" @@ -3095,7 +3477,7 @@ cacache@^17.0.0: tar "^6.1.11" unique-filename "^3.0.0" -call-bind@^1.0.0, call-bind@^1.0.5: +call-bind@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz" integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== @@ -3104,6 +3486,17 @@ call-bind@^1.0.0, call-bind@^1.0.5: get-intrinsic "^1.2.1" set-function-length "^1.1.1" +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -3133,6 +3526,20 @@ caniuse-lite@^1.0.30001565: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz" integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw== +caniuse-lite@^1.0.30001663: + version "1.0.30001664" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz#d588d75c9682d3301956b05a3749652a80677df4" + integrity sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g== + +capital-case@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + chalk@4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" @@ -3208,6 +3615,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz" integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== +class-transformer@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" @@ -3283,7 +3695,7 @@ clone@^1.0.2: cmd-shim@6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" integrity sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q== co@^4.6.0: @@ -3322,7 +3734,7 @@ color-name@~1.1.4: color-support@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== columnify@1.6.0: @@ -3340,11 +3752,26 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.0.0.tgz#b929db6df8546080adfd004ab215ed48cf6f2592" + integrity sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA== + commander@4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commander@9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.1.0.tgz#a6b263b2327f2e188c6402c42623327909f2dbec" + integrity sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w== + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.20.0: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" @@ -3504,12 +3931,12 @@ cookie@0.6.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== -core-js-compat@^3.31.0, core-js-compat@^3.33.1: - version "3.34.0" - resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz" - integrity sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA== +core-js-compat@^3.37.1, core-js-compat@^3.38.0: + version "3.38.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" + integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw== dependencies: - browserslist "^4.22.2" + browserslist "^4.23.3" core-util-is@^1.0.3, core-util-is@~1.0.0: version "1.0.3" @@ -3578,13 +4005,20 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@^4.3.3: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz" @@ -3600,7 +4034,7 @@ decamelize@^1.1.0: dedent@0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== dedent@^1.0.0: @@ -3652,6 +4086,15 @@ define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" @@ -3669,7 +4112,7 @@ delayed-stream@~1.0.0: delegates@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== depd@2.0.0: @@ -3730,14 +4173,19 @@ dot-prop@^5.1.0: dotenv-expand@~10.0.0: version "10.0.0" - resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== -dotenv@^16.3.1, dotenv@~16.3.1: +dotenv@^16.3.1: version "16.3.1" resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== +dotenv@~16.3.1: + version "16.3.2" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.2.tgz#3cb611ce5a63002dbabf7c281bc331f69d28f03f" + integrity sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ== + duplexer@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" @@ -3765,6 +4213,11 @@ electron-to-chromium@^1.4.601: resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.614.tgz" integrity sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ== +electron-to-chromium@^1.5.28: + version "1.5.30" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.30.tgz#5b264b489cfe0c3dd71097c164d795444834e7c7" + integrity sha512-sXI35EBN4lYxzc/pIGorlymYNzDBOqkSlVRe6MkgBsW/hW1tpC/HDJ2fjG7XnjakzfLEuvdmux0Mjs6jHq4UOA== + emittery@^0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" @@ -3785,6 +4238,11 @@ encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encoding@^0.1.13: version "0.1.13" resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz" @@ -3821,7 +4279,7 @@ env-paths@^2.2.0: envinfo@7.8.1: version "7.8.1" - resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== err-code@^2.0.2: @@ -3836,6 +4294,25 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-module-lexer@^1.2.1: version "1.4.1" resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz" @@ -3846,6 +4323,11 @@ escalade@^3.1.1: resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" @@ -4158,37 +4640,37 @@ exponential-backoff@^3.1.1: resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -express@4.19.2: - version "4.19.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" - integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== +express@4.21.0: + version "4.21.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.0.tgz#d57cb706d49623d4ac27833f1cbc466b668eb915" + integrity sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.2" + body-parser "1.20.3" content-disposition "0.5.4" content-type "~1.0.4" cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" + finalhandler "1.3.1" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.1" + merge-descriptors "1.0.3" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "0.1.10" proxy-addr "~2.0.7" - qs "6.11.0" + qs "6.13.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + send "0.19.0" + serve-static "1.16.2" setprototypeof "1.2.0" statuses "2.0.1" type-is "~1.6.18" @@ -4290,13 +4772,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" on-finished "2.4.1" parseurl "~1.3.3" @@ -4345,10 +4827,10 @@ flatted@^3.2.9: resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.15.0: - version "1.15.3" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== foreground-child@^3.1.0: version "3.1.1" @@ -4411,7 +4893,7 @@ fs-extra@^10.0.0: fs-extra@^11.1.0, fs-extra@^11.1.1: version "11.2.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== dependencies: graceful-fs "^4.2.0" @@ -4454,7 +4936,7 @@ function-bind@^1.1.2: gauge@^4.0.3: version "4.0.4" - resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== dependencies: aproba "^1.0.3 || ^2.0.0" @@ -4476,7 +4958,7 @@ get-caller-file@^2.0.5: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz" integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== @@ -4486,6 +4968,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" @@ -4551,7 +5044,7 @@ git-up@^7.0.0: git-url-parse@13.1.0: version "13.1.0" - resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-13.1.0.tgz#07e136b5baa08d59fabdf0e33170de425adf07b4" integrity sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA== dependencies: git-up "^7.0.0" @@ -4595,7 +5088,7 @@ glob@10.3.10, glob@^10.2.2, glob@^10.3.7: glob@7.1.4: version "7.1.4" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== dependencies: fs.realpath "^1.0.0" @@ -4605,6 +5098,18 @@ glob@7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.3.10: + version "10.4.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5" + integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" @@ -4619,7 +5124,7 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: glob@^8.0.1: version "8.1.0" - resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" @@ -4638,6 +5143,13 @@ glob@^9.2.0: minipass "^4.2.4" path-scurry "^1.6.1" +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" @@ -4696,6 +5208,13 @@ hard-rejection@^2.1.0: resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== +has-ansi@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-4.0.1.tgz#f216a8c8d7b129e490dc15f4a62cc1cdb9603ce8" + integrity sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A== + dependencies: + ansi-regex "^4.1.0" + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" @@ -4718,6 +5237,13 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.2.2" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + has-proto@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" @@ -4740,6 +5266,13 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" @@ -4747,7 +5280,7 @@ hosted-git-info@^2.1.4: hosted-git-info@^3.0.6: version "3.0.8" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== dependencies: lru-cache "^6.0.0" @@ -4761,7 +5294,7 @@ hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: hosted-git-info@^6.0.0: version "6.1.1" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w== dependencies: lru-cache "^7.5.1" @@ -4789,7 +5322,7 @@ http-errors@2.0.0: http-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: "@tootallnate/once" "2" @@ -4798,7 +5331,7 @@ http-proxy-agent@^5.0.0: https-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" @@ -4816,7 +5349,7 @@ human-signals@^4.3.0: humanize-ms@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== dependencies: ms "^2.0.0" @@ -4842,15 +5375,15 @@ ieee754@^1.1.13: ignore-walk@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw== dependencies: minimatch "^5.0.1" ignore-walk@^6.0.0: - version "6.0.4" - resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz" - integrity sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw== + version "6.0.5" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.5.tgz#ef8d61eab7da169078723d1f82833b36e200b0dd" + integrity sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A== dependencies: minimatch "^9.0.0" @@ -4887,7 +5420,7 @@ indent-string@^4.0.0: infer-owner@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== inflight@^1.0.4: @@ -4903,6 +5436,11 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + ini@^1.3.2, ini@^1.3.8: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" @@ -4910,7 +5448,7 @@ ini@^1.3.2, ini@^1.3.8: init-package-json@5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/init-package-json/-/init-package-json-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-5.0.0.tgz#030cf0ea9c84cfc1b0dc2e898b45d171393e4b40" integrity sha512-kBhlSheBfYmq3e0L1ii+VKe3zBTLL5lDCDWR+f9dLmEGSB3MqLlMlsolubSsyI88Bg6EA+BIMlomAnQ1SwgQBw== dependencies: npm-package-arg "^10.0.0" @@ -4989,10 +5527,13 @@ interpret@^1.0.0: resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -ip@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz" - integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" ipaddr.js@1.9.1: version "1.9.1" @@ -5018,13 +5559,20 @@ is-ci@3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.13.0, is-core-module@^2.5.0, is-core-module@^2.8.1: +is-core-module@^2.13.0, is-core-module@^2.5.0: version "2.13.1" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz" integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: hasown "^2.0.0" +is-core-module@^2.8.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" @@ -5064,6 +5612,14 @@ is-inside-container@^1.0.0: dependencies: is-docker "^3.0.0" +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" @@ -5084,9 +5640,9 @@ is-obj@^2.0.0: resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.3: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: @@ -5234,6 +5790,15 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^3.1.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" + integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.7" resolved "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz" @@ -5601,9 +6166,9 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.5.0: +jest@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: "@jest/core" "^29.7.0" @@ -5631,6 +6196,11 @@ js-yaml@^3.10.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" @@ -5722,9 +6292,16 @@ kleur@^3.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -lerna@^7.4.1: +knuth-shuffle-seeded@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz#01f1b65733aa7540ee08d8b0174164d22081e4e1" + integrity sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg== + dependencies: + seed-random "~2.2.0" + +lerna@^7.4.2: version "7.4.2" - resolved "https://registry.npmjs.org/lerna/-/lerna-7.4.2.tgz" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.4.2.tgz#03497125d7b7c8d463eebfe17a701b16bde2ad09" integrity sha512-gxavfzHfJ4JL30OvMunmlm4Anw7d7Tq6tdVHzUukLdS9nWnxCN/QB21qR+VJYp5tcyXogHKbdUEGh6qmeyzxSA== dependencies: "@lerna/child-process" "7.4.2" @@ -5818,7 +6395,7 @@ levn@^0.4.1: libnpmaccess@7.0.2: version "7.0.2" - resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-7.0.2.tgz" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-7.0.2.tgz#7f056c8c933dd9c8ba771fa6493556b53c5aac52" integrity sha512-vHBVMw1JFMTgEk15zRsJuSAg7QtGGHpUSEfnbcRL1/gTBag9iEfJbyjpDmdJmwMhvpoLoNBtdAUCdGnaP32hhw== dependencies: npm-package-arg "^10.1.0" @@ -5826,7 +6403,7 @@ libnpmaccess@7.0.2: libnpmpublish@7.3.0: version "7.3.0" - resolved "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-7.3.0.tgz" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-7.3.0.tgz#2ceb2b36866d75a6cd7b4aa748808169f4d17e37" integrity sha512-fHUxw5VJhZCNSls0KLNEG0mCD2PN1i14gH5elGOgiVnU3VgTcRahagYP2LKI1m0tFCJ+XrAm0zVYyF5RCbXzcg== dependencies: ci-info "^3.6.1" @@ -5845,7 +6422,7 @@ lines-and-columns@^1.1.6: lines-and-columns@~2.0.3: version "2.0.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== load-json-file@6.2.0: @@ -5910,6 +6487,11 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash@^4.17.21: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" @@ -5923,6 +6505,18 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^10.2.0: + version "10.2.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" + integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" @@ -5939,7 +6533,7 @@ lru-cache@^6.0.0: lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: version "7.18.3" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== "lru-cache@^9.1.1 || ^10.0.0": @@ -5947,6 +6541,11 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz" integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== +luxon@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.2.1.tgz#14f1af209188ad61212578ea7e3d518d18cee45f" + integrity sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg== + magic-string@0.30.5: version "0.30.5" resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz" @@ -5976,7 +6575,7 @@ make-error@^1.1.1: make-fetch-happen@^10.0.3: version "10.2.1" - resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== dependencies: agentkeepalive "^4.2.1" @@ -5998,7 +6597,7 @@ make-fetch-happen@^10.0.3: make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.1.1: version "11.1.1" - resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== dependencies: agentkeepalive "^4.2.1" @@ -6063,10 +6662,10 @@ meow@^8.1.2: type-fest "^0.18.0" yargs-parser "^20.2.3" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: version "2.0.0" @@ -6158,6 +6757,13 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" @@ -6174,14 +6780,14 @@ minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: minipass-collect@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== dependencies: minipass "^3.0.0" minipass-fetch@^2.0.3: version "2.1.2" - resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== dependencies: minipass "^3.1.6" @@ -6209,9 +6815,9 @@ minipass-flush@^1.0.5: minipass "^3.0.0" minipass-json-stream@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz" - integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.2.tgz#5121616c77a11c406c3ffa77509e0b77bb267ec3" + integrity sha512-myxeeTm57lYs8pH2nxPzmEEg8DGIgW+9mv6D4JZD2pa81I/OBjeU7PtICXV6c9eRGTA5JMDsuIPUZRCyBMYNhg== dependencies: jsonparse "^1.3.1" minipass "^3.0.0" @@ -6252,6 +6858,11 @@ minipass@^5.0.0: resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" @@ -6272,6 +6883,11 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mkdirp@^2.1.5: + version "2.1.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" + integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== + modify-values@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz" @@ -6287,9 +6903,9 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0: +ms@2.1.3, ms@^2.0.0, ms@^2.1.3: version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multer@1.4.4-lts.1: @@ -6326,6 +6942,15 @@ mute-stream@1.0.0, mute-stream@~1.0.0: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" @@ -6341,6 +6966,14 @@ neo-async@^2.6.2: resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + node-abort-controller@^3.0.1: version "3.1.1" resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz" @@ -6348,7 +6981,7 @@ node-abort-controller@^3.0.1: node-addon-api@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== node-emoji@1.11.0: @@ -6373,13 +7006,13 @@ node-fetch@2.7.0, node-fetch@^2.6.1, node-fetch@^2.6.7: whatwg-url "^5.0.0" node-gyp-build@^4.3.0: - version "4.7.1" - resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz" - integrity sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg== + version "4.8.2" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.2.tgz#4f802b71c1ab2ca16af830e6c1ea7dd1ad9496fa" + integrity sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw== node-gyp@^9.0.0: version "9.4.1" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== dependencies: env-paths "^2.2.0" @@ -6409,9 +7042,14 @@ node-releases@^2.0.14: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + nopt@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== dependencies: abbrev "^1.0.0" @@ -6438,7 +7076,7 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.3: normalize-package-data@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== dependencies: hosted-git-info "^6.0.0" @@ -6453,7 +7091,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: npm-bundled@^1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" @@ -6474,7 +7112,7 @@ npm-install-checks@^6.0.0: npm-normalize-package-bin@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== npm-normalize-package-bin@^3.0.0: @@ -6484,7 +7122,7 @@ npm-normalize-package-bin@^3.0.0: npm-package-arg@8.1.1: version "8.1.1" - resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.1.tgz" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" integrity sha512-CsP95FhWQDwNqiYS+Q0mZ7FAEDytDZAkNxQqea6IaAFJTAY9Lhhqyl0irU/6PMc7BGfUmnsbHcqxJD7XuVM/rg== dependencies: hosted-git-info "^3.0.6" @@ -6493,7 +7131,7 @@ npm-package-arg@8.1.1: npm-package-arg@^10.0.0, npm-package-arg@^10.1.0: version "10.1.0" - resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-10.1.0.tgz#827d1260a683806685d17193073cc152d3c7e9b1" integrity sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA== dependencies: hosted-git-info "^6.0.0" @@ -6503,7 +7141,7 @@ npm-package-arg@^10.0.0, npm-package-arg@^10.1.0: npm-packlist@5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.1.tgz#79bcaf22a26b6c30aa4dd66b976d69cc286800e0" integrity sha512-UfpSvQ5YKwctmodvPPkK6Fwk603aoVsf8AEbmVKAEECrfvL8SSe1A2YIwrJ6xmTHAITKPwwZsWo7WwEbNk0kxw== dependencies: glob "^8.0.1" @@ -6513,14 +7151,14 @@ npm-packlist@5.1.1: npm-packlist@^7.0.0: version "7.0.4" - resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-7.0.4.tgz#033bf74110eb74daf2910dc75144411999c5ff32" integrity sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q== dependencies: ignore-walk "^6.0.0" npm-pick-manifest@^8.0.0: version "8.0.2" - resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz#2159778d9c7360420c925c1a2287b5a884c713aa" integrity sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg== dependencies: npm-install-checks "^6.0.0" @@ -6530,7 +7168,7 @@ npm-pick-manifest@^8.0.0: npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3, npm-registry-fetch@^14.0.5: version "14.0.5" - resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz#fe7169957ba4986a4853a650278ee02e568d115d" integrity sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA== dependencies: make-fetch-happen "^11.0.0" @@ -6557,7 +7195,7 @@ npm-run-path@^5.1.0: npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== dependencies: are-we-there-yet "^3.0.0" @@ -6567,7 +7205,7 @@ npmlog@^6.0.0, npmlog@^6.0.2: nx@16.10.0, "nx@>=16.5.1 < 17": version "16.10.0" - resolved "https://registry.npmjs.org/nx/-/nx-16.10.0.tgz" + resolved "https://registry.yarnpkg.com/nx/-/nx-16.10.0.tgz#b070461f7de0a3d7988bd78558ea84cda3543ace" integrity sha512-gZl4iCC0Hx0Qe1VWmO4Bkeul2nttuXdPpfnlcDKSACGu3ZIo+uySqwOF8yBAxSTIf8xe2JRhgzJN1aFkuezEBg== dependencies: "@nrwl/tao" "16.10.0" @@ -6618,15 +7256,15 @@ nx@16.10.0, "nx@>=16.5.1 < 17": "@nx/nx-win32-arm64-msvc" "16.10.0" "@nx/nx-win32-x64-msvc" "16.10.0" -object-assign@^4, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.9.0: - version "1.13.1" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== on-finished@2.4.1: version "2.4.1" @@ -6808,9 +7446,14 @@ p-waterfall@2.1.1: dependencies: p-reduce "^2.0.0" +package-json-from-dist@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== + pacote@^15.2.0: version "15.2.0" - resolved "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.2.0.tgz#0f0dfcc3e60c7b39121b2ac612bf8596e95344d3" integrity sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA== dependencies: "@npmcli/git" "^4.0.0" @@ -6832,6 +7475,13 @@ pacote@^15.2.0: ssri "^10.0.0" tar "^6.1.11" +pad-right@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/pad-right/-/pad-right-0.2.2.tgz#6fbc924045d244f2a2a244503060d3bfc6009774" + integrity sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g== + dependencies: + repeat-string "^1.5.2" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" @@ -6914,15 +7564,23 @@ path-scurry@^1.10.1, path-scurry@^1.6.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz" - integrity sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA== +path-to-regexp@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== + +path-to-regexp@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.3.0.tgz#f7f31d32e8518c2660862b644414b6d5c63a611b" + integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw== path-type@^3.0.0: version "3.0.0" @@ -6941,6 +7599,11 @@ picocolors@^1.0.0: resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" + integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== + picomatch@3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz" @@ -7016,7 +7679,7 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: proc-log@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== process-nextick-args@~2.0.0: @@ -7024,6 +7687,11 @@ process-nextick-args@~2.0.0: resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" @@ -7052,6 +7720,11 @@ promzard@^1.0.0: dependencies: read "^2.0.0" +property-expr@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.6.tgz#f77bc00d5928a6c748414ad12882e83f24aec1e8" + integrity sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA== + protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz" @@ -7080,12 +7753,12 @@ pure-rand@^6.0.0: resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz" integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== -qs@6.11.0: - version "6.11.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: - side-channel "^1.0.4" + side-channel "^1.0.6" queue-microtask@^1.2.2: version "1.2.3" @@ -7131,7 +7804,7 @@ read-cmd-shim@4.0.0: read-package-json-fast@^3.0.0: version "3.0.2" - resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz#394908a9725dc7a5f14e70c8e7556dff1d2b1049" integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== dependencies: json-parse-even-better-errors "^3.0.0" @@ -7139,7 +7812,7 @@ read-package-json-fast@^3.0.0: read-package-json@6.0.4, read-package-json@^6.0.0: version "6.0.4" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.4.tgz#90318824ec456c287437ea79595f4c2854708836" integrity sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw== dependencies: glob "^10.2.2" @@ -7234,6 +7907,11 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +reflect-metadata@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.1.tgz#8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74" + integrity sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw== + reflect-metadata@^0.1.13: version "0.1.14" resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz" @@ -7263,6 +7941,18 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" +regexp-match-indices@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz#cf20054a6f7d5b3e116a701a7b00f82889d10da6" + integrity sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ== + dependencies: + regexp-tree "^0.1.11" + +regexp-tree@^0.1.11: + version "0.1.27" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" + integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== + regexpu-core@^5.3.1: version "5.3.2" resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz" @@ -7282,7 +7972,7 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" -repeat-string@^1.6.1: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== @@ -7314,6 +8004,13 @@ resolve-from@^4.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-2.0.0.tgz#ac06991418a7623edc119084edc98b0e6bf05a41" + integrity sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ== + dependencies: + resolve-from "^5.0.0" + resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz" @@ -7422,6 +8119,11 @@ schema-utils@^3.1.1, schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" +seed-random@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" + integrity sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ== + "semver@2 || 3 || 4 || 5", semver@^5.6.0: version "5.7.2" resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" @@ -7446,10 +8148,10 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semve dependencies: lru-cache "^6.0.0" -send@0.18.0: - version "0.18.0" - resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== dependencies: debug "2.6.9" depd "2.0.0" @@ -7472,15 +8174,15 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== dependencies: - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.18.0" + send "0.19.0" set-blocking@^2.0.0: version "2.0.0" @@ -7497,6 +8199,18 @@ set-function-length@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" @@ -7530,14 +8244,15 @@ shelljs@0.8.5: interpret "^1.0.0" rechoir "^0.6.2" -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" signal-exit@3.0.7, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" @@ -7551,7 +8266,7 @@ signal-exit@^4.0.1: sigstore@^1.3.0, sigstore@^1.4.0: version "1.9.0" - resolved "https://registry.npmjs.org/sigstore/-/sigstore-1.9.0.tgz" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.9.0.tgz#1e7ad8933aa99b75c6898ddd0eeebc3eb0d59875" integrity sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A== dependencies: "@sigstore/bundle" "^1.1.0" @@ -7577,7 +8292,7 @@ smart-buffer@^4.2.0: socks-proxy-agent@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== dependencies: agent-base "^6.0.2" @@ -7585,11 +8300,11 @@ socks-proxy-agent@^7.0.0: socks "^2.6.2" socks@^2.6.2: - version "2.7.1" - resolved "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== dependencies: - ip "^2.0.0" + ip-address "^9.0.5" smart-buffer "^4.2.0" sort-keys@^2.0.0: @@ -7607,7 +8322,7 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@0.5.21, source-map-support@~0.5.20: +source-map-support@0.5.21, source-map-support@^0.5.21, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -7665,21 +8380,33 @@ split@^1.0.1: dependencies: through "2" +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -ssri@^10.0.0, ssri@^10.0.1: +ssri@^10.0.0: version "10.0.5" resolved "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz" integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A== dependencies: minipass "^7.0.3" +ssri@^10.0.1: + version "10.0.6" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5" + integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== + dependencies: + minipass "^7.0.3" + ssri@^9.0.0, ssri@^9.0.1: version "9.0.1" - resolved "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== dependencies: minipass "^3.1.1" @@ -7691,6 +8418,11 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + statuses@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" @@ -7701,6 +8433,11 @@ streamsearch@^1.1.0: resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== +string-argv@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -7709,7 +8446,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7741,7 +8487,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -7810,9 +8563,9 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" @@ -7861,7 +8614,7 @@ tar-stream@~2.2.0: tar@6.1.11: version "6.1.11" - resolved "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: chownr "^2.0.0" @@ -7871,7 +8624,7 @@ tar@6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.1.11, tar@^6.1.2: +tar@^6.1.11: version "6.2.0" resolved "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz" integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== @@ -7883,6 +8636,18 @@ tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + temp-dir@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" @@ -7928,6 +8693,20 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + through2@^2.0.0: version "2.0.5" resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" @@ -7941,11 +8720,21 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tiny-case@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03" + integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q== + titleize@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz" integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== +tmp@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" @@ -7982,6 +8771,11 @@ toidentifier@1.0.1: resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" @@ -8039,16 +8833,26 @@ tsconfig-paths@4.2.0, tsconfig-paths@^4.1.2: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@2.6.2, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.0, tslib@^2.6.2: - version "2.6.2" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== tslib@^1.8.1: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.0, tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" @@ -8058,7 +8862,7 @@ tsutils@^3.21.0: tuf-js@^1.1.7: version "1.1.7" - resolved "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz" + resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" integrity sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg== dependencies: "@tufjs/models" "1.0.4" @@ -8107,6 +8911,16 @@ type-fest@^0.8.1: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +type-fest@^4.8.3: + version "4.20.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.20.1.tgz#d97bb1e923bf524e5b4b43421d586760fb2ee8be" + integrity sha512-R6wDsVsoS9xYOpy8vgeBlqpdOyzJ12HNfQhC/aAKWM3YoCV9TtunJzh/QpkMgeDhkoynDcw5f1y+qF9yc/HHyg== + type-is@^1.6.4, type-is@~1.6.18: version "1.6.18" resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" @@ -8167,7 +8981,7 @@ unicode-property-aliases-ecmascript@^2.0.0: unique-filename@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== dependencies: unique-slug "^3.0.0" @@ -8181,7 +8995,7 @@ unique-filename@^3.0.0: unique-slug@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== dependencies: imurmurhash "^0.1.4" @@ -8226,6 +9040,21 @@ update-browserslist-db@^1.0.13: escalade "^3.1.1" picocolors "^1.0.0" +update-browserslist-db@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.0" + +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== + dependencies: + tslib "^2.0.3" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" @@ -8233,6 +9062,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +util-arity@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/util-arity/-/util-arity-1.1.0.tgz#59d01af1fdb3fede0ac4e632b0ab5f6ce97c9330" + integrity sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA== + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -8243,9 +9077,9 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^9.0.0: +uuid@9.0.1, uuid@^9.0.0: version "9.0.1" - resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== v8-compile-cache-lib@^3.0.1: @@ -8255,7 +9089,7 @@ v8-compile-cache-lib@^3.0.1: v8-compile-cache@2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^9.0.1: @@ -8284,7 +9118,7 @@ validate-npm-package-name@5.0.0, validate-npm-package-name@^5.0.0: validate-npm-package-name@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" integrity sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw== dependencies: builtins "^1.0.3" @@ -8378,14 +9212,14 @@ which@^2.0.1, which@^2.0.2: which@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/which/-/which-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== dependencies: isexe "^2.0.0" wide-align@^1.1.5: version "1.1.5" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: string-width "^1.0.2 || 2 || 3 || 4" @@ -8395,7 +9229,7 @@ wordwrap@^1.0.0: resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -8413,6 +9247,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" @@ -8429,7 +9272,7 @@ wrappy@1: write-file-atomic@5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: imurmurhash "^0.1.4" @@ -8473,6 +9316,11 @@ write-pkg@4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" @@ -8493,9 +9341,14 @@ yallist@^4.0.0: resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^2.2.2: + version "2.4.5" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e" + integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== + yargs-parser@20.2.4: version "20.2.4" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs-parser@21.1.1, yargs-parser@^21.1.1: @@ -8543,3 +9396,13 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yup@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/yup/-/yup-1.2.0.tgz#9e51af0c63bdfc9be0fdc6c10aa0710899d8aff6" + integrity sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ== + dependencies: + property-expr "^2.0.5" + tiny-case "^1.0.3" + toposort "^2.0.2" + type-fest "^2.19.0"