diff --git a/.eslintrc.json b/.eslintrc.json index 7258c5c536..556470697d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -26,6 +26,7 @@ "off", { "cases": { "camelCase": true, "pascalCase": true, "kebabCase": true } } ], + "import/no-import-module-exports": "off", "unicorn/no-instanceof-array": "error", "unicorn/no-static-only-class": "error", "unicorn/consistent-destructuring": "error", diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b18fd29357..040e07f81d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,11 @@ updates: directory: '/' schedule: interval: 'weekly' + - package-ecosystem: 'docker' + directory: '/' + schedule: + interval: 'daily' + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'daily' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cfcb1fc0d8..c52962467d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -46,7 +46,7 @@ N/A - [ ] Is the PR limited to one linear task? -- [ ] Are relevant unit and component test-cases added? +- [ ] Are relevant unit and component test-cases added in **new readability format**? ### Reviewer checklist diff --git a/.github/workflows/build-push-docker-image.yml b/.github/workflows/build-push-docker-image.yml index 5531bcacea..aec24864a4 100644 --- a/.github/workflows/build-push-docker-image.yml +++ b/.github/workflows/build-push-docker-image.yml @@ -43,6 +43,7 @@ jobs: - name: Checkout uses: actions/checkout@v4.1.1 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 1 - name: Setup Docker Buildx @@ -94,6 +95,7 @@ jobs: - name: Checkout uses: actions/checkout@v4.1.1 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 1 - name: Setup Docker Buildx diff --git a/.github/workflows/dt-test-and-report-code-coverage.yml b/.github/workflows/dt-test-and-report-code-coverage.yml index 51a9f8c9ee..4375b3383e 100644 --- a/.github/workflows/dt-test-and-report-code-coverage.yml +++ b/.github/workflows/dt-test-and-report-code-coverage.yml @@ -41,6 +41,8 @@ jobs: - name: Upload Coverage Reports to Codecov uses: codecov/codecov-action@v4.0.1 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: directory: ./reports/coverage diff --git a/.github/workflows/prepare-for-prod-dt-deploy.yml b/.github/workflows/prepare-for-prod-dt-deploy.yml index d796116f10..7d024c0337 100644 --- a/.github/workflows/prepare-for-prod-dt-deploy.yml +++ b/.github/workflows/prepare-for-prod-dt-deploy.yml @@ -120,10 +120,9 @@ jobs: yq eval -i ".user-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" multi-tenant/multi-tenant.yaml git add multi-tenant/multi-tenant.yaml - cd ../../../../config-be-rudder-transformer - yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" values.prod.yaml - yq eval -i ".config-be-rudder-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" values.prod.yaml - git add values.prod.yaml + cd ../../../../config-be-rudder-transformer/environment/prod + yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml + git add base.yaml git commit -m "chore: upgrade shared transformers to $TAG_NAME" git push -u origin shared-transformer-$TAG_NAME @@ -146,3 +145,39 @@ jobs: git push -u origin hosted-transformer-$TAG_NAME gh pr create --fill + + - name: Update helm charts and raise pull request for enterprise customers on dedicated transformers + env: + GITHUB_TOKEN: ${{ secrets.PAT }} + run: | + cd rudder-devops + git checkout -b dedicated-transformer-$TAG_NAME + + cd customer-objects + + declare -a enabled_ut_customers=() + declare -a sub_directories=('enterprise-us' 'enterprise-eu') + + # identify the customers enabled in sub-directories + for directory in "${sub_directories[@]}"; do + for f in "./$directory"/*; do + [[ -f $f ]] || continue + + enabled="$(yq e '.spec.user_transformer.enabled' $f)" + if [ $enabled == "true" ]; then + enabled_ut_customers+=( $f ) + fi + done + done + + # bump up the customers version and repository information + for customer in "${enabled_ut_customers[@]}"; do + yq eval -i ".spec.user_transformer.image.version=\"$TAG_NAME\"" $customer + yq eval -i ".spec.user_transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" $customer + git add $customer + done + + git commit -m "chore: upgrade dedicated transformers to $TAG_NAME" + git push -u origin dedicated-transformer-$TAG_NAME + + gh pr create --fill diff --git a/.github/workflows/prepare-for-prod-rollback.yml b/.github/workflows/prepare-for-prod-rollback.yml index 9ac144a21e..825720efe1 100644 --- a/.github/workflows/prepare-for-prod-rollback.yml +++ b/.github/workflows/prepare-for-prod-rollback.yml @@ -27,11 +27,14 @@ jobs: git config --global user.name "GitHub Actions" git config --global user.email "noreply@github.com" + - name: Clone Devops Repo + run: | + git clone https://${{secrets.PAT}}@github.com/rudderlabs/rudder-devops.git + - name: Update Helm Charts and Raise Pull Request env: GITHUB_TOKEN: ${{ secrets.PAT }} run: | - git clone https://${{secrets.PAT}}@github.com/rudderlabs/rudder-devops.git cd rudder-devops git checkout -b shared-transformer-rollback-${{ steps.target-version.outputs.tag_name }} @@ -57,3 +60,38 @@ jobs: git push -u origin shared-transformer-rollback-${{ steps.target-version.outputs.tag_name }} gh pr create --fill + + - name: Update helm charts and raise pull request for enterprise customers on dedicated transformers + env: + GITHUB_TOKEN: ${{ secrets.PAT }} + run: | + cd rudder-devops + git checkout -b dedicated-transformer-rollback-${{ steps.target-version.outputs.tag_name }} + + cd customer-objects + + declare -a enabled_ut_customers=() + declare -a sub_directories=('enterprise-us' 'enterprise-eu') + + # identify the customers enabled in sub-directories + for directory in "${sub_directories[@]}"; do + for f in "./$directory"/*; do + [[ -f $f ]] || continue + + enabled="$(yq e '.spec.user_transformer.enabled' $f)" + if [ $enabled == "true" ]; then + enabled_ut_customers+=( $f ) + fi + done + done + + # bump up the customers version and repository information + for customer in "${enabled_ut_customers[@]}"; do + yq eval -i ".spec.user_transformer.image.version=\"${{ steps.target-version.outputs.tag_name }}\"" $customer + git add $customer + done + + git commit -m "chore: rollback dedicated transformers to ${{ steps.target-version.outputs.tag_name }}" + git push -u origin dedicated-transformer-rollback-${{ steps.target-version.outputs.tag_name }} + + gh pr create --fill diff --git a/.github/workflows/prepare-for-prod-ut-deploy.yml b/.github/workflows/prepare-for-prod-ut-deploy.yml index ea6a9bf5f2..3d74ce6943 100644 --- a/.github/workflows/prepare-for-prod-ut-deploy.yml +++ b/.github/workflows/prepare-for-prod-ut-deploy.yml @@ -104,11 +104,10 @@ jobs: cd rudder-devops git checkout -b shared-user-transformer-$UT_TAG_NAME - cd helm-charts/config-be-rudder-transformer + cd helm-charts/config-be-rudder-transformer/environment/prod - yq eval -i ".config-be-user-transformer.image.tag=\"$UT_TAG_NAME\"" values.prod.yaml - yq eval -i ".config-be-user-transformer.image.repository=\"$TF_IMAGE_REPOSITORY\"" values.prod.yaml - git add values.prod.yaml + yq eval -i ".config-be-user-transformer.image.tag=\"$UT_TAG_NAME\"" base.yaml + git add base.yaml git commit -m "chore: upgrade shared user-transformers to $UT_TAG_NAME" git push -u origin shared-user-transformer-$UT_TAG_NAME diff --git a/.github/workflows/prepare-for-staging-deploy.yml b/.github/workflows/prepare-for-staging-deploy.yml index f1f90a75df..1e9e9a102e 100644 --- a/.github/workflows/prepare-for-staging-deploy.yml +++ b/.github/workflows/prepare-for-staging-deploy.yml @@ -105,7 +105,7 @@ jobs: cd rudder-devops BRANCH_NAME="shared-transformer-$TAG_NAME" echo $BRANCH_NAME - if [ -n `git ls-remote --heads origin $BRANCH_NAME 2>/dev/null` ] + if [ -n "$(git ls-remote --heads origin $BRANCH_NAME 2>/dev/null)" ] then echo "Staging deployment branch already exists!" else @@ -116,10 +116,10 @@ jobs: yq eval -i ".user-transformer.image.tag=\"$TAG_NAME\"" staging.yaml git add staging.yaml - cd ../../../../config-be-rudder-transformer - yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" values.staging.yaml - yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" values.staging.yaml - git add values.staging.yaml + cd ../../../../config-be-rudder-transformer/environment/staging + yq eval -i ".config-be-rudder-transformer.image.tag=\"$TAG_NAME\"" base.yaml + yq eval -i ".config-be-user-transformer.image.tag=\"$TAG_NAME\"" base.yaml + git add base.yaml git commit -m "chore: upgrade staging env transformers to \"$TAG_NAME\"" git push -u origin $BRANCH_NAME diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 233e99577d..15d7b20fd1 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -99,7 +99,7 @@ jobs: channel-id: ${{ secrets.SLACK_RELEASE_CHANNEL_ID }} payload: | { - "text": "*<${{env.RELEASES_URL}}v${{ steps.extract-version.outputs.release_version }}|v${{ steps.extract-version.outputs.release_version }}>*\nCC: <@U03KG4BK1L1> <@U02AE5GMMHV> <@U01LVJ30QEB>", + "text": "*<${{env.RELEASES_URL}}v${{ steps.extract-version.outputs.release_version }}|v${{ steps.extract-version.outputs.release_version }}>*\nCC: <@U03KG4BK1L1> <@U024YF8CR53> <@U01LVJ30QEB>", "blocks": [ { "type": "header", @@ -115,7 +115,7 @@ jobs: "type": "section", "text": { "type": "mrkdwn", - "text": "*<${{env.RELEASES_URL}}v${{ steps.extract-version.outputs.release_version }}|v${{ steps.extract-version.outputs.release_version }}>*\nCC: <@U03KG4BK1L1> <@U02AE5GMMHV> <@U01LVJ30QEB>" + "text": "*<${{env.RELEASES_URL}}v${{ steps.extract-version.outputs.release_version }}|v${{ steps.extract-version.outputs.release_version }}>*\nCC: <@U03KG4BK1L1> <@U024YF8CR53> <@U01LVJ30QEB>" } } ] diff --git a/.github/workflows/verify-server-start.yml b/.github/workflows/verify-server-start.yml new file mode 100644 index 0000000000..6ac5b7be05 --- /dev/null +++ b/.github/workflows/verify-server-start.yml @@ -0,0 +1,34 @@ +name: Verify Server start + +on: + pull_request: + types: ['opened', 'reopened', 'synchronize'] + +jobs: + check-health: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4.1.1 + with: + fetch-depth: 1 + + - name: Setup Node + uses: actions/setup-node@v4.0.2 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install Dependencies + run: npm ci + + - name: Start server + run: npm run build:start & + + - name: Wait for server to start + run: sleep 10 # Adjust the time as necessary for your server to start + + - name: Check server health + run: | + curl --fail http://localhost:9090/health || exit 1 diff --git a/.gitignore b/.gitignore index 956605f139..09c536ebb8 100644 --- a/.gitignore +++ b/.gitignore @@ -137,4 +137,5 @@ dist .idea # component test report -test_reports/ \ No newline at end of file +test_reports/ +temp/ diff --git a/.nvmrc b/.nvmrc index a9d087399d..99c98cdd6a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.19.0 +18.20.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index a043cfb6e3..dbe8ae98fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,261 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.69.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.69.0...v1.69.1) (2024-06-25) + + +### Bug Fixes + +* enhancement: introduce user model for one signal ([#3499](https://github.com/rudderlabs/rudder-transformer/issues/3499)) ([1c8e950](https://github.com/rudderlabs/rudder-transformer/commit/1c8e950f3d8789b33bba69a30c9eb21c40ce3d04)) + +## [1.69.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.2...v1.69.0) (2024-06-10) + + +### Features + +* add request_ip as fallback for mixpanel group call ([#3421](https://github.com/rudderlabs/rudder-transformer/issues/3421)) ([a73ab75](https://github.com/rudderlabs/rudder-transformer/commit/a73ab75032d753b35cb0e18234dcd7289dd1e644)) +* add v3 api support to appsflyer ([#3412](https://github.com/rudderlabs/rudder-transformer/issues/3412)) ([e124470](https://github.com/rudderlabs/rudder-transformer/commit/e124470e82b6aa9934094146d4050af02bb62fff)), closes [#3395](https://github.com/rudderlabs/rudder-transformer/issues/3395) [#3402](https://github.com/rudderlabs/rudder-transformer/issues/3402) +* changes for supporting record event in FB audience ([#3351](https://github.com/rudderlabs/rudder-transformer/issues/3351)) ([ac4a32a](https://github.com/rudderlabs/rudder-transformer/commit/ac4a32ab5e0c7e02a149e81d455666ed24fa01a3)) + + +### Bug Fixes + +* allowing traffic type dynamically for split.io ([#3425](https://github.com/rudderlabs/rudder-transformer/issues/3425)) ([3bea186](https://github.com/rudderlabs/rudder-transformer/commit/3bea186e725ec473ad757760355d6cc9670c4f8c)) +* bugsnag issue fix for zendesk ([#3439](https://github.com/rudderlabs/rudder-transformer/issues/3439)) ([775e8ee](https://github.com/rudderlabs/rudder-transformer/commit/775e8ee55a62ecddb58ff505302e4aabb8bffe24)) + +### [1.68.2](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.1...v1.68.2) (2024-06-06) + + +### Bug Fixes + +* **user-transformation:** pass tf id in common metadata ([d2b0779](https://github.com/rudderlabs/rudder-transformer/commit/d2b0779d3a0145c0088903edfe328c1c7554cbd2)) + +### [1.68.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.68.0...v1.68.1) (2024-05-29) + + +### Bug Fixes + +* tiktok_v2 assigning value to undefined properties ([#3426](https://github.com/rudderlabs/rudder-transformer/issues/3426)) ([323396b](https://github.com/rudderlabs/rudder-transformer/commit/323396b09fd6b7fda3cce53cc4f1cc443d7a78c1)) + +## [1.68.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.67.0...v1.68.0) (2024-05-27) + + +### Features + +* add json-data type support in redis ([#3336](https://github.com/rudderlabs/rudder-transformer/issues/3336)) ([0196f20](https://github.com/rudderlabs/rudder-transformer/commit/0196f20cc79e1f470d96a649dd9404c3c9284329)) +* facebook custom audience app secret support ([#3357](https://github.com/rudderlabs/rudder-transformer/issues/3357)) ([fce4ef9](https://github.com/rudderlabs/rudder-transformer/commit/fce4ef973500411c7ad812e7949bb1b73dabc3ba)) +* filtering unknown events in awin ([#3392](https://github.com/rudderlabs/rudder-transformer/issues/3392)) ([d842da8](https://github.com/rudderlabs/rudder-transformer/commit/d842da87a34cb63023eba288e0c5258e29997dcf)) +* **ga4:** component test refactor ([#3220](https://github.com/rudderlabs/rudder-transformer/issues/3220)) ([3ff9a5e](https://github.com/rudderlabs/rudder-transformer/commit/3ff9a5e8e955b929a1b04a89dcf0ccbc49e18648)) +* **integrations/auth0:** include Auth0 event type in Rudderstack message ([#3370](https://github.com/rudderlabs/rudder-transformer/issues/3370)) ([e9409fd](https://github.com/rudderlabs/rudder-transformer/commit/e9409fde6063d7eaa8558396b85b5fdf99f964e1)) +* onboard koddi destination ([#3359](https://github.com/rudderlabs/rudder-transformer/issues/3359)) ([f74c4a0](https://github.com/rudderlabs/rudder-transformer/commit/f74c4a0bc92ae6ccb0c00ac5b21745e496a015bc)) +* onboarding adjust source ([#3395](https://github.com/rudderlabs/rudder-transformer/issues/3395)) ([668d331](https://github.com/rudderlabs/rudder-transformer/commit/668d3311aadacbb92b1873bf43919db7d341afbb)) + + +### Bug Fixes + +* fb custom audience html response ([#3402](https://github.com/rudderlabs/rudder-transformer/issues/3402)) ([d1a2bd6](https://github.com/rudder +* standardise hashing for all CAPI integrations ([#3379](https://github.com/rudderlabs/rudder-transformer/issues/3379)) ([c249a69](https://github.com/rudderlabs/rudder-transformer/commit/c249a694d735f6d241a35b6e21f493c54890ac84)) +* tiktok_v2 remove default value for content-type for custom events ([#3383](https://github.com/rudderlabs/rudder-transformer/issues/3383)) ([6e7b5a0](https://github.com/rudderlabs/rudder-transformer/commit/6e7b5a0d8bf2c859dfb15b9cad7ed6070bd0892b)) +* added step for reconciling openfaas functions for python transformations ([#3420](https://github.com/rudderlabs/rudder-transformer/issues/3420)) ([7a2ab63](https://github.com/rudderlabs/rudder-transformer/commit/7a2ab63674d40870af4d16f0673a2a2594c899e9)) + +## [1.67.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.66.1...v1.67.0) (2024-05-23) + + +### Features + +* sre 456 ut move high cardinality histogram metrics to summaries cp ([#3409](https://github.com/rudderlabs/rudder-transformer/issues/3409)) ([be20dc2](https://github.com/rudderlabs/rudder-transformer/commit/be20dc26ade2fa0212dc91126cf42087a84a07c9)) + +### [1.66.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.66.0...v1.66.1) (2024-05-20) + + +### Bug Fixes + +* add validation for null/undefined traits in slack ([#3382](https://github.com/rudderlabs/rudder-transformer/issues/3382)) ([755073c](https://github.com/rudderlabs/rudder-transformer/commit/755073c4341a454785050d835021d9f17e0b9d3f)) +* gaoc store sales batching transform contract ([#3384](https://github.com/rudderlabs/rudder-transformer/issues/3384)) ([e7678cb](https://github.com/rudderlabs/rudder-transformer/commit/e7678cbdae4c06449ea9352ce3db390d2a29da14)) +* move af_currency outside properties in eventValue ([#3316](https://github.com/rudderlabs/rudder-transformer/issues/3316)) ([71c3d46](https://github.com/rudderlabs/rudder-transformer/commit/71c3d46236fff9209625cfb0737c21db2d275345)) +* remove default traits from ortto ([#3389](https://github.com/rudderlabs/rudder-transformer/issues/3389)) ([fbb0811](https://github.com/rudderlabs/rudder-transformer/commit/fbb0811aa0e417b0cffcea4ecc103979afccfe74)) +* update validation of event duration ([#3376](https://github.com/rudderlabs/rudder-transformer/issues/3376)) ([3ad7850](https://github.com/rudderlabs/rudder-transformer/commit/3ad78506446915ada8bdc5f5594dc2710e6b0646)) + +## [1.66.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.65.1...v1.66.0) (2024-05-13) + + +### Features + +* add slack source ([#3148](https://github.com/rudderlabs/rudder-transformer/issues/3148)) +* onboard monday to proxy ([#3347](https://github.com/rudderlabs/rudder-transformer/issues/3347)) +* onboard emarsys destination ([#3369](https://github.com/rudderlabs/rudder-transformer/issues/3369)) + + +### Bug Fixes + +* ninetailed: modify parameter requirements and add default values ([#3364](https://github.com/rudderlabs/rudder-transformer/issues/3364)) + +### [1.65.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.65.0...v1.65.1) (2024-05-10) + + +### Bug Fixes + +* update regex which was discarding firstname and lastname ([#3360](https://github.com/rudderlabs/rudder-transformer/issues/3360)) ([cb10aa7](https://github.com/rudderlabs/rudder-transformer/commit/cb10aa7707518b52edcf7fb1081c6969bcb5f8f8)) + +## [1.65.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.64.0...v1.65.0) (2024-05-06) + + +### Features + +* generate anonymousId and move to component testcases ([447f85f](https://github.com/rudderlabs/rudder-transformer/commit/447f85faf6ccca2179ab33b7fe43e281fc4f5897)) + + +### Bug Fixes + +* auth0 error handling ([2e22075](https://github.com/rudderlabs/rudder-transformer/commit/2e22075ddf792c573642fd09c5f9d31d8132525b)) +* auth0 error handling for missing userId ([#3334](https://github.com/rudderlabs/rudder-transformer/issues/3334)) ([d2cce77](https://github.com/rudderlabs/rudder-transformer/commit/d2cce772d6f0485ff8ee51af261006eb2066c3a2)) +* braze dedup for non-billable attributes ([#3320](https://github.com/rudderlabs/rudder-transformer/issues/3320)) ([ac59fdc](https://github.com/rudderlabs/rudder-transformer/commit/ac59fdc37d6fe08847d79d249d166de7cc358fd6)) +* multiple event mappings in ortto ([#3341](https://github.com/rudderlabs/rudder-transformer/issues/3341)) ([0a9a2e9](https://github.com/rudderlabs/rudder-transformer/commit/0a9a2e9e2a3029ad7b4834820ba2132d3f57ce98)) + +## [1.64.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.63.0...v1.64.0) (2024-04-29) + + +### Features + +* adding custom properties support to bluecore ([#3282](https://github.com/rudderlabs/rudder-transformer/issues/3282)) ([8592e66](https://github.com/rudderlabs/rudder-transformer/commit/8592e664eb568e70a00261e275ab2faed8f6f618)) +* onboard Yandex Metrica Offline Events Destination ([#3232](https://github.com/rudderlabs/rudder-transformer/issues/3232)) ([8f79f53](https://github.com/rudderlabs/rudder-transformer/commit/8f79f53d30326e07fc92dd624e799015ff9f87c2)) +* transactional custom property support for awin ([#3325](https://github.com/rudderlabs/rudder-transformer/issues/3325)) ([fdecaf3](https://github.com/rudderlabs/rudder-transformer/commit/fdecaf36d91db7540d6f68a013e4f7fb2a36ebaa)) + + +### Bug Fixes + +* algolia enhancement ( adding currency, price, subType and objectData support ) ([#3290](https://github.com/rudderlabs/rudder-transformer/issues/3290)) ([f06ebde](https://github.com/rudderlabs/rudder-transformer/commit/f06ebde110693fe32f8e450dc395f1f4019defab)) +* **delighted:** replace myAxios utility with handleHttpRequest utility ([#3237](https://github.com/rudderlabs/rudder-transformer/issues/3237)) ([bac3cc5](https://github.com/rudderlabs/rudder-transformer/commit/bac3cc5670c149454a6063a55a4b901043b0ff02)) +* handle empty userId ([5402b21](https://github.com/rudderlabs/rudder-transformer/commit/5402b219ccdeaafb710c8c2828e983e9864a415f)) +* handle empty userId (movable ink, bloomreach) ([#3300](https://github.com/rudderlabs/rudder-transformer/issues/3300)) ([038c1aa](https://github.com/rudderlabs/rudder-transformer/commit/038c1aa04049aaa1caa1bf82cf6c69611b5d3fd9)) +* send content_ids as a string if there is only one value ([#3317](https://github.com/rudderlabs/rudder-transformer/issues/3317)) ([54eca32](https://github.com/rudderlabs/rudder-transformer/commit/54eca3220ea48fae64c655813fe4430dd704639e)) + +## [1.63.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.2...v1.63.0) (2024-04-25) + + +### Features + +* remove redundant data from traits in hubspot ([#3310](https://github.com/rudderlabs/rudder-transformer/issues/3310)) ([4b21f13](https://github.com/rudderlabs/rudder-transformer/commit/4b21f1353d3d9a431a0d5446d019f66a543b977b)) + +### [1.62.2](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.1...v1.62.2) (2024-04-18) + + +### Bug Fixes + +* twitter_ads logger ([#3295](https://github.com/rudderlabs/rudder-transformer/issues/3295)) ([e92b052](https://github.com/rudderlabs/rudder-transformer/commit/e92b052e03182deb41b20b3ec3741306afa50380)) + +### [1.62.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.62.0...v1.62.1) (2024-04-18) + + +### Bug Fixes + +* revert mixpanel deprecate /track ([#3291](https://github.com/rudderlabs/rudder-transformer/issues/3291)) ([ec068b4](https://github.com/rudderlabs/rudder-transformer/commit/ec068b49bd4a5652a762c60a8257c883e4709d1a)) + +## [1.62.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.61.1...v1.62.0) (2024-04-15) + + +### Features + +* do away myaxios ([#3222](https://github.com/rudderlabs/rudder-transformer/issues/3222)) ([9214594](https://github.com/rudderlabs/rudder-transformer/commit/9214594bab2c86a4ae6f75e12531f778490cf127)) +* for reddit adding currency and value for addToCart, viewConent event as well ([#3239](https://github.com/rudderlabs/rudder-transformer/issues/3239)) ([ad235e7](https://github.com/rudderlabs/rudder-transformer/commit/ad235e785bf6039e11231a915be098130b25ec3b)) +* logger upgrade in services, dest, source ([#3228](https://github.com/rudderlabs/rudder-transformer/issues/3228)) ([c204113](https://github.com/rudderlabs/rudder-transformer/commit/c204113eab37a782f217488d0d626a8d6df345d3)) +* rakuten: adding a default value for tr ([#3240](https://github.com/rudderlabs/rudder-transformer/issues/3240)) ([3748f24](https://github.com/rudderlabs/rudder-transformer/commit/3748f24e21634fc74c5e5b3761551c64c8e69942)) + + +### Bug Fixes + +* adding check for reserved key words in extract custom fields ([#3264](https://github.com/rudderlabs/rudder-transformer/issues/3264)) ([3399c47](https://github.com/rudderlabs/rudder-transformer/commit/3399c47fdce1b3d19e29306ca3c5692a2fbc30fb)) +* deployment file paths ([#3216](https://github.com/rudderlabs/rudder-transformer/issues/3216)) ([808727d](https://github.com/rudderlabs/rudder-transformer/commit/808727de17e400ed102a843ab3b30f81f8900f24)) +* email mappings ([#3247](https://github.com/rudderlabs/rudder-transformer/issues/3247)) ([791cbf5](https://github.com/rudderlabs/rudder-transformer/commit/791cbf55fc6940af4e3208212b82c891c6618fc3)) +* fixed userId mapping, now mapping to uid instead of id ([#3262](https://github.com/rudderlabs/rudder-transformer/issues/3262)) ([9c6b251](https://github.com/rudderlabs/rudder-transformer/commit/9c6b251a6c784cc391f27e846a008fbe2901e2c8)) +* hs bugsnag error ([#3252](https://github.com/rudderlabs/rudder-transformer/issues/3252)) ([9daf1c9](https://github.com/rudderlabs/rudder-transformer/commit/9daf1c989258bd410d5780c1b11c4f6df9654af5)) +* hubspot: search for contact using secondary prop ([#3258](https://github.com/rudderlabs/rudder-transformer/issues/3258)) ([0b57204](https://github.com/rudderlabs/rudder-transformer/commit/0b5720446693efe1fd0ccdfc141bd7f21b2c32ae)) +* impact: support custom product mapping ([#3249](https://github.com/rudderlabs/rudder-transformer/issues/3249)) ([cb8ff2f](https://github.com/rudderlabs/rudder-transformer/commit/cb8ff2fb943c49df4ac083bd179d9674b40eb602)) +* marketo bulk ignore null while checking data type mismatch ([#3263](https://github.com/rudderlabs/rudder-transformer/issues/3263)) ([6e3274b](https://github.com/rudderlabs/rudder-transformer/commit/6e3274bfba9e7838d1f81d845a070427b67e75f5)) +* shopify: send 500 for identifier call in case of failure ([#3235](https://github.com/rudderlabs/rudder-transformer/issues/3235)) ([8eb4c4e](https://github.com/rudderlabs/rudder-transformer/commit/8eb4c4e9b8daebbaeb1d12ff0c17915fe19c2b50)) + +### [1.61.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.61.0...v1.61.1) (2024-04-03) + +## [1.61.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.60.0...v1.61.0) (2024-04-02) + + +### Features + +* consent field support for ga4 ([#3213](https://github.com/rudderlabs/rudder-transformer/issues/3213)) ([92515a5](https://github.com/rudderlabs/rudder-transformer/commit/92515a5fd8a2798c48010078f62b360ec6a49979)) +* consent field support for gaoc and upgrade the api version from v14 to v16 ([#3121](https://github.com/rudderlabs/rudder-transformer/issues/3121)) ([2aac2a6](https://github.com/rudderlabs/rudder-transformer/commit/2aac2a62547b7a7c617735fc3d6e88e0a1bed76e)), closes [#3190](https://github.com/rudderlabs/rudder-transformer/issues/3190) +* onboard new destination bloomreach ([#3185](https://github.com/rudderlabs/rudder-transformer/issues/3185)) ([d9b7e1f](https://github.com/rudderlabs/rudder-transformer/commit/d9b7e1f70565d59979aee3e62f60e39edb9a23c7)) +* onboarding linkedin conversion api ([#3194](https://github.com/rudderlabs/rudder-transformer/issues/3194)) ([eb7b197](https://github.com/rudderlabs/rudder-transformer/commit/eb7b197322c617b14c2579de8cb4d4dacf8e1df3)) +* update movable ink batch size ([#3223](https://github.com/rudderlabs/rudder-transformer/issues/3223)) ([667095f](https://github.com/rudderlabs/rudder-transformer/commit/667095fa8316cd95a066f15b848ad503c6b4af80)) + + +### Bug Fixes + +* fixed userId mapping, now mapping to uid instead of id ([#3192](https://github.com/rudderlabs/rudder-transformer/issues/3192)) ([70a468b](https://github.com/rudderlabs/rudder-transformer/commit/70a468bf16ecd5ee0b6fecee4b837895d19c525f)) +* ninetailed: remove page support ([#3218](https://github.com/rudderlabs/rudder-transformer/issues/3218)) ([2f30c56](https://github.com/rudderlabs/rudder-transformer/commit/2f30c56af62e983d09b5d4f2da9a0ba22f5c1612)) +* shopify invalid_event metric prometheus label ([#3200](https://github.com/rudderlabs/rudder-transformer/issues/3200)) ([345c87d](https://github.com/rudderlabs/rudder-transformer/commit/345c87d7c530c621ae3fd6c504d64e5a14e31f22)) +* fix: snapchat conversion: add event level_complete ([#3231](https://github.com/rudderlabs/rudder-transformer/issues/3231)) ([39368a0](https://github.com/rudderlabs/rudder-transformer/commit/39368a09e48acc324faa855186bc623e5c347881)) + +## [1.60.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.60.0) (2024-03-20) + + +### Features + +* ninetailed: add default value for context.location as {} ([#3197](https://github.com/rudderlabs/rudder-transformer/issues/3197)) ([91fc0fb](https://github.com/rudderlabs/rudder-transformer/commit/91fc0fb3e9eeb127298a0ce305ef6d1d7b72a39f)) + + +### Bug Fixes + +* heap: make userId as required for track and identify call ([#3198](https://github.com/rudderlabs/rudder-transformer/issues/3198)) ([6a7c534](https://github.com/rudderlabs/rudder-transformer/commit/6a7c534a7df812bb7e39c1905eadcc29d7cd1329)) +* tiktok_ads: validate message.event type ([#3203](https://github.com/rudderlabs/rudder-transformer/issues/3203)) ([a86c277](https://github.com/rudderlabs/rudder-transformer/commit/a86c2771034877cef4161cda61bcda5fdda2d89f)) + +## [1.59.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.59.0) (2024-03-18) + + +### Features + +* add Koala destination ([#3122](https://github.com/rudderlabs/rudder-transformer/issues/3122)) ([1ca039d](https://github.com/rudderlabs/rudder-transformer/commit/1ca039d64ebb1a18a0fc6b78ed5ee08528ad6b48)) +* add support of skip_user_properties_sync on Amplitude ([#3181](https://github.com/rudderlabs/rudder-transformer/issues/3181)) ([5e4ddbd](https://github.com/rudderlabs/rudder-transformer/commit/5e4ddbd8a591341a581a5721505d6dcb010f2eec)) +* adding zod validations ([#3066](https://github.com/rudderlabs/rudder-transformer/issues/3066)) ([325433b](https://github.com/rudderlabs/rudder-transformer/commit/325433b9188c8d1dbe740c7e193cdc2e58fdd751)) +* onboard destination movable ink ([#3167](https://github.com/rudderlabs/rudder-transformer/issues/3167)) ([7018b1e](https://github.com/rudderlabs/rudder-transformer/commit/7018b1e5e7f37ae177191c5ecf3a71cfe2f3d147)) +* update proxy tests for cm360 ([#3039](https://github.com/rudderlabs/rudder-transformer/issues/3039)) ([0504ffa](https://github.com/rudderlabs/rudder-transformer/commit/0504ffa898956f5b61771fb32ecfd0e0bf15248f)) +* use dontBatch directive in algolia ([#3169](https://github.com/rudderlabs/rudder-transformer/issues/3169)) ([916aaec](https://github.com/rudderlabs/rudder-transformer/commit/916aaecb1939160620d5fd3c4c0c0e33f2a371b2)) + + +### Bug Fixes + +* api contract for v1 proxy ([#3049](https://github.com/rudderlabs/rudder-transformer/issues/3049)) ([93947db](https://github.com/rudderlabs/rudder-transformer/commit/93947db35cdaf1ca7ed87ec5f73567754af312ab)) +* email mapping for clevertap ([#3173](https://github.com/rudderlabs/rudder-transformer/issues/3173)) ([04eab92](https://github.com/rudderlabs/rudder-transformer/commit/04eab92e1c383f9e8cdd5c845530a42a0af2932a)) +* fb pixel test case refactor ([#3075](https://github.com/rudderlabs/rudder-transformer/issues/3075)) ([cff7d1c](https://github.com/rudderlabs/rudder-transformer/commit/cff7d1c4578087a37614c0ef4529058481873479)) +* fixed 500 status for algolia dontBatch ([#3178](https://github.com/rudderlabs/rudder-transformer/issues/3178)) ([6330888](https://github.com/rudderlabs/rudder-transformer/commit/6330888ad5c67e3a800037b56501fc08da09e4d1)) +* label not present in prometheus metrics ([#3176](https://github.com/rudderlabs/rudder-transformer/issues/3176)) ([01d460c](https://github.com/rudderlabs/rudder-transformer/commit/01d460c3edaf39b35c4686516c9e9140be46aa5e)) +* send proper status to server in cm360 ([#3127](https://github.com/rudderlabs/rudder-transformer/issues/3127)) ([229ce47](https://github.com/rudderlabs/rudder-transformer/commit/229ce473af1ddd62d946bea1b018c882b142a5ef)) + +## [1.58.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.1...v1.58.0) (2024-03-04) + + +### Features + +* add support for interaction events in sfmc ([#3109](https://github.com/rudderlabs/rudder-transformer/issues/3109)) ([0486049](https://github.com/rudderlabs/rudder-transformer/commit/0486049ba2ad96b50d8f29e96b46b96a8a5c9f76)) +* add support of custom page/screen event name in mixpanel ([#3098](https://github.com/rudderlabs/rudder-transformer/issues/3098)) ([0eb2393](https://github.com/rudderlabs/rudder-transformer/commit/0eb2393939fba2452ef7f07a1d149d87f18290c3)) +* consent mode support for google adwords remarketing list ([#3143](https://github.com/rudderlabs/rudder-transformer/issues/3143)) ([7532c90](https://github.com/rudderlabs/rudder-transformer/commit/7532c90d7e1feac00f12961c56da18757010f44a)) +* **facebook:** update content_type mapping logic for fb pixel and fb conversions ([#3113](https://github.com/rudderlabs/rudder-transformer/issues/3113)) ([aea417c](https://github.com/rudderlabs/rudder-transformer/commit/aea417cd2691547399010c034cadbc5db6b0c6ee)) +* klaviyo profile mapping ([#3105](https://github.com/rudderlabs/rudder-transformer/issues/3105)) ([2761786](https://github.com/rudderlabs/rudder-transformer/commit/2761786ff3fc99ed6d4d3b7a6c2400226b1cfb12)) +* onboard new destination ninetailed ([#3106](https://github.com/rudderlabs/rudder-transformer/issues/3106)) ([0e2588e](https://github.com/rudderlabs/rudder-transformer/commit/0e2588ecd87f3b2c6877a099aa1cbf2d5325966c)) + + +### Bug Fixes + +* add error handling for tiktok ads ([#3144](https://github.com/rudderlabs/rudder-transformer/issues/3144)) ([e93e47f](https://github.com/rudderlabs/rudder-transformer/commit/e93e47f33e098104fb532916932fe38bbfeaa4a1)) +* **algolia:** added check for objectIds or filters to be non empty ([#3126](https://github.com/rudderlabs/rudder-transformer/issues/3126)) ([d619c97](https://github.com/rudderlabs/rudder-transformer/commit/d619c9769cd270cb2d16dad0865683ff4beb2d19)) +* clevertap remove stringification of array object properties ([#3048](https://github.com/rudderlabs/rudder-transformer/issues/3048)) ([69e43b6](https://github.com/rudderlabs/rudder-transformer/commit/69e43b6ffadeaec87b7440da34a341890ceba252)) +* convert to string from null in hs ([#3136](https://github.com/rudderlabs/rudder-transformer/issues/3136)) ([75e9f46](https://github.com/rudderlabs/rudder-transformer/commit/75e9f462b0ff9b9a8abab3c78dc7d147926e9e5e)) +* event fix and added utility ([#3142](https://github.com/rudderlabs/rudder-transformer/issues/3142)) ([9b705b7](https://github.com/rudderlabs/rudder-transformer/commit/9b705b71a9d3a595ea0fbf532602c3941b0a18db)) +* metadata structure correction ([#3119](https://github.com/rudderlabs/rudder-transformer/issues/3119)) ([8351b5c](https://github.com/rudderlabs/rudder-transformer/commit/8351b5cbbf81bbc14b2f884feaae4ad3ca59a39a)) +* one_signal: Encode external_id in endpoint ([#3140](https://github.com/rudderlabs/rudder-transformer/issues/3140)) ([8a20886](https://github.com/rudderlabs/rudder-transformer/commit/8a2088608d6da4b35bbb506db2fc3df1e4d41f3b)) +* rakuten: sync property mapping sourcekeys to rudderstack standard spec ([#3129](https://github.com/rudderlabs/rudder-transformer/issues/3129)) ([2ebff95](https://github.com/rudderlabs/rudder-transformer/commit/2ebff956ff2aa74b008a8de832a31d8774d2d47e)) +* reddit revenue mapping for floating point values ([#3118](https://github.com/rudderlabs/rudder-transformer/issues/3118)) ([41f4078](https://github.com/rudderlabs/rudder-transformer/commit/41f4078011ef54334bb9ecc11a7b2ccc8831a4aa)) +* version deprecation failure false positive ([#3104](https://github.com/rudderlabs/rudder-transformer/issues/3104)) ([657b780](https://github.com/rudderlabs/rudder-transformer/commit/657b7805eb01da25a007d978198d5debf03917fd)) + ### [1.57.1](https://github.com/rudderlabs/rudder-transformer/compare/v1.57.0...v1.57.1) (2024-03-04) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bdd76d916c..48055816e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,5 +49,5 @@ We look forward to your feedback on improving this project. [slack]: https://resources.rudderstack.com/join-rudderstack-slack [issue]: https://github.com/rudderlabs/rudder-transformer/issues/new -[cla]: https://rudderlabs.wufoo.com/forms/rudderlabs-contributor-license-agreement +[cla]: https://forms.gle/845JRGVZaC6kPZy68 [config-generator]: https://github.com/rudderlabs/config-generator diff --git a/Dockerfile b/Dockerfile index 6bd03c9515..9fe3c1cdb2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.4 -FROM node:18.19.0-alpine3.18 AS base +FROM node:18.20.1-alpine3.18 AS base ENV HUSKY 0 RUN apk update diff --git a/benchmark/metaLogger.js b/benchmark/metaLogger.js index 2af1f599c9..b89ad71066 100644 --- a/benchmark/metaLogger.js +++ b/benchmark/metaLogger.js @@ -2,30 +2,30 @@ const logger = require('../src/logger'); -logger.setLogLevel(Number.POSITIVE_INFINITY); +logger.setLogLevel('random'); const debug = (...args) => { - logger.setLogLevel(logger.levelDebug); + logger.setLogLevel('debug'); logger.debug(...args); - logger.setLogLevel(Number.POSITIVE_INFINITY); + logger.setLogLevel('random'); }; const info = (...args) => { - logger.setLogLevel(logger.levelInfo); + logger.setLogLevel('info'); logger.info(...args); - logger.setLogLevel(Number.POSITIVE_INFINITY); + logger.setLogLevel('random'); }; const warn = (...args) => { - logger.setLogLevel(logger.levelWarn); + logger.setLogLevel('warn'); logger.warn(...args); - logger.setLogLevel(Number.POSITIVE_INFINITY); + logger.setLogLevel('random'); }; const error = (...args) => { - logger.setLogLevel(logger.levelError); + logger.setLogLevel('error'); logger.error(...args); - logger.setLogLevel(Number.POSITIVE_INFINITY); + logger.setLogLevel('random'); }; module.exports = { diff --git a/github-release.config.js b/github-release.config.js new file mode 100644 index 0000000000..df269d8a02 --- /dev/null +++ b/github-release.config.js @@ -0,0 +1,5 @@ +module.exports = { + gitRawCommitsOpts: { + merges: null, + }, +}; diff --git a/package-lock.json b/package-lock.json index e21ae0bbbf..cb607996e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rudder-transformer", - "version": "1.57.1", + "version": "1.69.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rudder-transformer", - "version": "1.57.1", + "version": "1.69.1", "license": "ISC", "dependencies": { "@amplitude/ua-parser-js": "0.7.24", @@ -18,9 +18,10 @@ "@datadog/pprof": "^3.1.0", "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", - "@pyroscope/nodejs": "^0.2.6", - "@rudderstack/integrations-lib": "^0.2.4", - "@rudderstack/workflow-engine": "^0.7.2", + "@pyroscope/nodejs": "^0.2.9", + "@rudderstack/integrations-lib": "^0.2.10", + "@rudderstack/json-template-engine": "^0.15.0", + "@rudderstack/workflow-engine": "^0.8.9", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -47,6 +48,7 @@ "koa": "^2.14.1", "koa-bodyparser": "^4.4.0", "koa2-swagger-ui": "^5.7.0", + "libphonenumber-js": "^1.11.1", "lodash": "^4.17.21", "match-json": "^1.3.5", "md5": "^2.3.0", @@ -60,9 +62,11 @@ "parse-static-imports": "^1.1.0", "prom-client": "^14.2.0", "qs": "^6.11.1", + "rs-jsonpath": "^1.1.2", "rudder-transformer-cdk": "^1.4.11", "set-value": "^4.1.0", "sha256": "^0.2.0", + "sqlstring": "^2.3.3", "stacktrace-parser": "^0.1.10", "statsd-client": "^0.4.7", "truncate-utf8-bytes": "^1.0.2", @@ -77,6 +81,7 @@ "@digitalroute/cz-conventional-changelog-for-jira": "^8.0.1", "@types/fast-json-stable-stringify": "^2.1.0", "@types/jest": "^29.5.1", + "@types/jsonpath": "^0.2.4", "@types/koa": "^2.13.6", "@types/koa-bodyparser": "^4.3.10", "@types/lodash": "^4.14.197", @@ -117,16 +122,38 @@ "typescript": "^5.0.4" } }, + "../jsonpath": { + "name": "rs-jsonpath", + "version": "1.1.2", + "extraneous": true, + "license": "MIT", + "dependencies": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + }, + "devDependencies": { + "grunt": "0.4.5", + "grunt-browserify": "3.8.0", + "grunt-cli": "0.1.13", + "grunt-contrib-uglify": "0.9.1", + "jison": "0.4.13", + "jscs": "1.10.0", + "jshint": "2.6.0", + "mocha": "2.1.0" + } + }, "../rudder-integrations-lib": { "name": "@rudderstack/integrations-lib", - "version": "0.1.10", + "version": "0.2.9", "extraneous": true, "license": "MIT", "dependencies": { - "@rudderstack/workflow-engine": "^0.5.7", "axios": "^1.4.0", "axios-mock-adapter": "^1.22.0", "crypto": "^1.0.1", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^17.1.0", "get-value": "^3.0.1", "handlebars": "^4.7.8", "lodash": "^4.17.21", @@ -138,12 +165,19 @@ "winston": "^3.11.0" }, "devDependencies": { + "@commitlint/config-conventional": "^18.5.0", "@types/get-value": "^3.0.3", "@types/jest": "^29.5.4", "@types/lodash": "^4.14.195", "@types/node": "^20.3.3", "@types/set-value": "^4.0.1", "@types/sha256": "^0.2.0", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", + "commitlint": "^18.6.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "husky": "^8.0.0", "jest": "^29.4.3", "pre-commit": "^1.2.2", "prettier": "^2.8.4", @@ -4471,33 +4505,35 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@pyroscope/nodejs": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@pyroscope/nodejs/-/nodejs-0.2.6.tgz", - "integrity": "sha512-F37ROH//HzO7zKm2S7CtNG8OAp+i4ADg4erQR9D57BrSgi8+3Jjp5s5PWqyJABC6IzsABgGrentPobBDr8QdsA==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pyroscope/nodejs/-/nodejs-0.2.9.tgz", + "integrity": "sha512-pIw4pIqcNZTZxTUuV0OUI18UZEmx9lT2GaT75ny6FKVe2L1gxAwTCf5TKk8VsnUGY66buUkyaTHcTm7fy0BP/Q==", "dependencies": { - "axios": "^0.26.1", + "axios": "^0.28.0", "debug": "^4.3.3", "form-data": "^4.0.0", - "pprof": "^3.2.0", + "pprof": "^4.0.0", "regenerator-runtime": "^0.13.11", "source-map": "^0.7.3" }, "engines": { - "node": "^12.20.0 || >=14.13.1" + "node": ">=v18" } }, "node_modules/@pyroscope/nodejs/node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.28.1.tgz", + "integrity": "sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ==", "dependencies": { - "follow-redirects": "^1.14.8" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/@rudderstack/integrations-lib": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.4.tgz", - "integrity": "sha512-32Zose9aOPNWd4EyUNuS5YY+Vq4LYMuDcabJ+s3t1ZfHHMfISlDNF02b60MWgOrU8PARYC+siDs5wgA6xfZpzQ==", + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.10.tgz", + "integrity": "sha512-PVlRIxO9PVYpR+UNm1qQt85wo0wO9oX0PvoC9XqzYO+C0PfRvkMqac8ghA5ytqeCYNfSIye7DtidaII5ZoCQCA==", "dependencies": { "axios": "^1.4.0", "axios-mock-adapter": "^1.22.0", @@ -4516,21 +4552,21 @@ } }, "node_modules/@rudderstack/json-template-engine": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.8.5.tgz", - "integrity": "sha512-+iH40g+ZA2ANgwjOITdEdZJLZV+ljR28Akn/dRoDia591tMu7PptyvDaAvl+m1DijWXddpLQ8SX9xaEcIdmqlw==" + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@rudderstack/json-template-engine/-/json-template-engine-0.15.0.tgz", + "integrity": "sha512-RDBCn4oYvDjWhIEuV3d8QHmdTcFUyVbpBFxtRwOb1U7zSpTZ8H74/wxBG661ayN2psFh7UJYzZENju7cLWpFTw==" }, "node_modules/@rudderstack/workflow-engine": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.7.2.tgz", - "integrity": "sha512-aXQvoXMekvXxxDG6Yc5P5l3PJIwqVA+EmJ2w4SnQ94BUHhbsybPjgGvyzD17MUTAdWEOtqS38SuzLflBs/5T4g==", + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@rudderstack/workflow-engine/-/workflow-engine-0.8.9.tgz", + "integrity": "sha512-4PxiXUeJ6ulhdlS7MHB6zPV6fdRVZZ0EDnl5fRbu7gDq1h4h32t5T44RwXjLbyupuu/vsPshkBNoEpo+heUBqg==", "dependencies": { - "@aws-crypto/sha256-js": "^5.0.0", - "@rudderstack/json-template-engine": "^0.8.4", - "jsonata": "^2.0.3", + "@aws-crypto/sha256-js": "^5.2.0", + "@rudderstack/json-template-engine": "^0.15.0", + "jsonata": "^2.0.5", "lodash": "^4.17.21", - "object-sizeof": "^2.6.3", - "yaml": "^2.3.2" + "object-sizeof": "^2.6.4", + "yaml": "^2.4.3" } }, "node_modules/@rudderstack/workflow-engine/node_modules/@aws-crypto/sha256-js": { @@ -5465,6 +5501,12 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/jsonpath": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@types/jsonpath/-/jsonpath-0.2.4.tgz", + "integrity": "sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA==", + "dev": true + }, "node_modules/@types/keygrip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", @@ -6587,11 +6629,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -8932,9 +8974,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { "node": ">=8" } @@ -10074,7 +10116,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -10420,9 +10461,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -10523,9 +10564,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -14538,9 +14579,9 @@ } }, "node_modules/jsonata": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.3.tgz", - "integrity": "sha512-Up2H81MUtjqI/dWwWX7p4+bUMfMrQJVMN/jW6clFMTiYP528fBOBNtRu944QhKTs3+IsVWbgMeUTny5fw2VMUA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.5.tgz", + "integrity": "sha512-wEse9+QLIIU5IaCgtJCPsFi/H4F3qcikWzF4bAELZiRz08ohfx3Q6CjDRf4ZPF5P/92RI3KIHtb7u3jqPaHXdQ==", "engines": { "node": ">= 8" } @@ -14868,6 +14909,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz", + "integrity": "sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw==" + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -15299,6 +15345,11 @@ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", "dev": true }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, "node_modules/lodash.startcase": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", @@ -16547,9 +16598,9 @@ "dev": true }, "node_modules/nan": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", - "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" }, "node_modules/nanoid": { "version": "3.3.7", @@ -16846,9 +16897,9 @@ } }, "node_modules/object-sizeof": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/object-sizeof/-/object-sizeof-2.6.3.tgz", - "integrity": "sha512-GNkVRrLh11Qr5BGr73dwwPE200/78QG2rbx30cnXPnMvt7UuttH4Dup5t+LtcQhARkg8Hbr0c8Kiz52+CFxYmw==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/object-sizeof/-/object-sizeof-2.6.4.tgz", + "integrity": "sha512-YuJAf7Bi61KROcYmXm8RCeBrBw8UOaJDzTm1gp0eU7RjYi1xEte3/Nmg/VyPaHcJZ3sNojs1Y0xvSrgwkLmcFw==", "dependencies": { "buffer": "^6.0.3" } @@ -17548,24 +17599,23 @@ "dev": true }, "node_modules/pprof": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/pprof/-/pprof-3.2.1.tgz", - "integrity": "sha512-KnextTM3EHQ2zqN8fUjB0VpE+njcVR7cOfo7DjJSLKzIbKTPelDtokI04ScR/Vd8CLDj+M99tsaKV+K6FHzpzA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pprof/-/pprof-4.0.0.tgz", + "integrity": "sha512-Yhfk7Y0G1MYsy97oXxmSG5nvbM1sCz9EALiNhW/isAv5Xf7svzP+1RfGeBlS6mLSgRJvgSLh6Mi5DaisQuPttw==", "hasInstallScript": true, "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", + "@mapbox/node-pre-gyp": "^1.0.9", "bindings": "^1.2.1", "delay": "^5.0.0", "findit2": "^2.2.3", - "nan": "^2.14.0", + "nan": "^2.17.0", "p-limit": "^3.0.0", - "pify": "^5.0.0", "protobufjs": "~7.2.4", - "source-map": "^0.7.3", + "source-map": "~0.8.0-beta.0", "split": "^1.0.1" }, "engines": { - "node": ">=10.4.1" + "node": ">=14.0.0" } }, "node_modules/pprof-format": { @@ -17573,15 +17623,38 @@ "resolved": "https://registry.npmjs.org/pprof-format/-/pprof-format-2.0.7.tgz", "integrity": "sha512-1qWaGAzwMpaXJP9opRa23nPnt2Egi7RMNoNBptEE/XwHbcn4fC2b/4U4bKc5arkGkIh2ZabpF2bEb+c5GNHEKA==" }, - "node_modules/pprof/node_modules/pify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", - "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", - "engines": { - "node": ">=10" + "node_modules/pprof/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 8" + } + }, + "node_modules/pprof/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/pprof/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/pprof/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } }, "node_modules/precinct": { @@ -17991,9 +18064,9 @@ } }, "node_modules/protobufjs": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", - "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -18634,6 +18707,28 @@ "node": ">=18.0" } }, + "node_modules/rs-jsonpath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rs-jsonpath/-/rs-jsonpath-1.1.2.tgz", + "integrity": "sha512-IQzlqtVyZniK7aOtpKGrv7BvkamSvLJkIhRGoKKDQLppNJe94BVHqpxNRjw/2042nGjtC3vyfCWyHe+3DlWgWA==", + "dependencies": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + } + }, + "node_modules/rs-jsonpath/node_modules/esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/rudder-transformer-cdk": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/rudder-transformer-cdk/-/rudder-transformer-cdk-1.4.11.tgz", @@ -19113,6 +19208,14 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stack-generator": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", @@ -19503,6 +19606,91 @@ "node": ">=10" } }, + "node_modules/static-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "dependencies": { + "escodegen": "^1.8.1" + } + }, + "node_modules/static-eval/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/static-eval/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-eval/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/stats-accumulator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/stats-accumulator/-/stats-accumulator-1.1.3.tgz", @@ -19906,9 +20094,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -20443,6 +20631,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -20833,7 +21026,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -20987,9 +21179,12 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.3.tgz", + "integrity": "sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } diff --git a/package.json b/package.json index f5908bc7ff..cc3806cb43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rudder-transformer", - "version": "1.57.1", + "version": "1.69.1", "description": "", "homepage": "https://github.com/rudderlabs/rudder-transformer#readme", "bugs": { @@ -49,7 +49,7 @@ "commit-msg": "commitlint --edit", "prepare": "node ./scripts/skipPrepareScript.js || husky install", "release": "npx standard-version", - "release:github": "DEBUG=conventional-github-releaser npx conventional-github-releaser -p angular -v", + "release:github": "DEBUG=conventional-github-releaser npx conventional-github-releaser -p angular --config github-release.config.js", "clean:node": "modclean", "check:lint": "eslint . -f json -o reports/eslint.json || exit 0" }, @@ -63,9 +63,10 @@ "@datadog/pprof": "^3.1.0", "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", - "@pyroscope/nodejs": "^0.2.6", - "@rudderstack/integrations-lib": "^0.2.4", - "@rudderstack/workflow-engine": "^0.7.2", + "@pyroscope/nodejs": "^0.2.9", + "@rudderstack/integrations-lib": "^0.2.10", + "@rudderstack/json-template-engine": "^0.15.0", + "@rudderstack/workflow-engine": "^0.8.9", "@shopify/jest-koa-mocks": "^5.1.1", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0", @@ -92,6 +93,7 @@ "koa": "^2.14.1", "koa-bodyparser": "^4.4.0", "koa2-swagger-ui": "^5.7.0", + "libphonenumber-js": "^1.11.1", "lodash": "^4.17.21", "match-json": "^1.3.5", "md5": "^2.3.0", @@ -105,9 +107,11 @@ "parse-static-imports": "^1.1.0", "prom-client": "^14.2.0", "qs": "^6.11.1", + "rs-jsonpath": "^1.1.2", "rudder-transformer-cdk": "^1.4.11", "set-value": "^4.1.0", "sha256": "^0.2.0", + "sqlstring": "^2.3.3", "stacktrace-parser": "^0.1.10", "statsd-client": "^0.4.7", "truncate-utf8-bytes": "^1.0.2", @@ -122,6 +126,7 @@ "@digitalroute/cz-conventional-changelog-for-jira": "^8.0.1", "@types/fast-json-stable-stringify": "^2.1.0", "@types/jest": "^29.5.1", + "@types/jsonpath": "^0.2.4", "@types/koa": "^2.13.6", "@types/koa-bodyparser": "^4.3.10", "@types/lodash": "^4.14.197", diff --git a/src/adapters/network.js b/src/adapters/network.js index 0720638d12..850ba649c8 100644 --- a/src/adapters/network.js +++ b/src/adapters/network.js @@ -45,14 +45,20 @@ const networkClientConfigs = { httpsAgent: new https.Agent({ keepAlive: true }), }; -const fireHTTPStats = (clientResponse, startTime, statTags) => { - const destType = statTags.destType ? statTags.destType : ''; - const feature = statTags.feature ? statTags.feature : ''; - const endpointPath = statTags.endpointPath ? statTags.endpointPath : ''; - const requestMethod = statTags.requestMethod ? statTags.requestMethod : ''; - const module = statTags.module ? statTags.module : ''; - const statusCode = clientResponse.success ? clientResponse.response.status : ''; +const fireOutgoingReqStats = ({ + destType, + feature, + endpointPath, + requestMethod, + module, + metadata = {}, + startTime, + statusCode, + clientResponse, +}) => { + const logMetaInfo = log.getLogMetadata(metadata); stats.timing('outgoing_request_latency', startTime, { + ...logMetaInfo, feature, destType, endpointPath, @@ -60,6 +66,7 @@ const fireHTTPStats = (clientResponse, startTime, statTags) => { module, }); stats.counter('outgoing_request_count', 1, { + ...logMetaInfo, feature, destType, endpointPath, @@ -70,6 +77,36 @@ const fireHTTPStats = (clientResponse, startTime, statTags) => { }); }; +const fireHTTPStats = (clientResponse, startTime, statTags) => { + const destType = statTags.destType ? statTags.destType : ''; + const feature = statTags.feature ? statTags.feature : ''; + const endpointPath = statTags.endpointPath ? statTags.endpointPath : ''; + const requestMethod = statTags.requestMethod ? statTags.requestMethod : ''; + const module = statTags.module ? statTags.module : ''; + const statusCode = clientResponse.success ? clientResponse.response.status : ''; + const defArgs = { + destType, + endpointPath, + feature, + module, + requestMethod, + statusCode, + startTime, + clientResponse, + }; + if (statTags?.metadata) { + const metadata = !Array.isArray(statTags?.metadata) ? [statTags.metadata] : statTags.metadata; + metadata?.forEach((m) => { + fireOutgoingReqStats({ + ...defArgs, + metadata: m, + }); + }); + return; + } + fireOutgoingReqStats(defArgs); +}; + const enhanceRequestOptions = (options) => { const requestOptions = { ...networkClientConfigs, @@ -322,25 +359,6 @@ const prepareProxyRequest = (request) => { return removeUndefinedValues({ endpoint, data, params, headers, method, config }); }; -/** - * depricating: handles proxying requests to destinations from server, expects requsts in "defaultRequestConfig" - * note: needed for test api - * @param {*} request - * @returns - */ -const proxyRequest = async (request, destType) => { - const { endpoint, data, method, params, headers } = prepareProxyRequest(request); - const requestOptions = { - url: endpoint, - data, - params, - headers, - method, - }; - const response = await httpSend(requestOptions, { feature: 'proxy', destType }); - return response; -}; - /** * handles http request and sends the response in a simple format that is followed in transformer * @@ -392,6 +410,38 @@ const handleHttpRequest = async (requestType = 'post', ...httpArgs) => { return { httpResponse, processedResponse }; }; +/** + * depricating: handles proxying requests to destinations from server, expects requsts in "defaultRequestConfig" + * note: needed for test api + * @param {*} request + * @returns + */ +const proxyRequest = async (request, destType) => { + const { metadata } = request; + const { endpoint, data, method, params, headers } = prepareProxyRequest(request); + const requestOptions = { + url: endpoint, + data, + params, + headers, + method, + }; + log.requestLog(`[${destType.toUpperCase()}] delivering data`, { + metadata, + requestDetails: { + body: data, + url: endpoint, + method, + }, + }); + const response = await httpSend(requestOptions, { + feature: 'proxy', + destType, + metadata, + }); + return response; +}; + module.exports = { httpSend, httpGET, diff --git a/src/adapters/utils/networkUtils.js b/src/adapters/utils/networkUtils.js index 0dcb9931e9..4b8dd4fc39 100644 --- a/src/adapters/utils/networkUtils.js +++ b/src/adapters/utils/networkUtils.js @@ -144,10 +144,11 @@ const processAxiosResponse = (clientResponse) => { } // non 2xx status handling for axios response if (response) { - const { data, status } = response; + const { data, status, headers } = response; return { response: data || '', status: status || 500, + ...(isDefinedAndNotNullAndNotEmpty(headers) ? { headers } : {}), }; } // (edge case) response and code is not present @@ -157,10 +158,11 @@ const processAxiosResponse = (clientResponse) => { }; } // success(2xx) axios response - const { data, status } = clientResponse.response; + const { data, status, headers } = clientResponse.response; return { response: data || '', status: status || 500, + ...(isDefinedAndNotNullAndNotEmpty(headers) ? { headers } : {}), }; }; diff --git a/src/cdk/v2/destinations/algolia/procWorkflow.yaml b/src/cdk/v2/destinations/algolia/procWorkflow.yaml index f9ac8e3ae6..402b48dabd 100644 --- a/src/cdk/v2/destinations/algolia/procWorkflow.yaml +++ b/src/cdk/v2/destinations/algolia/procWorkflow.yaml @@ -5,7 +5,10 @@ bindings: - path: ../../../../v0/destinations/algolia/config - name: removeUndefinedAndNullValues path: ../../../../v0/util + - name: isDefinedAndNotNull + path: ../../../../v0/util - path: ../../bindings/jsontemplate + - path: '@rudderstack/integrations-lib' steps: - name: validateInput @@ -24,6 +27,7 @@ steps: let eventTypeMap = $.eventTypeMapping(.destination.Config); let event = .message.event.trim().toLowerCase(); let eventType = .message.properties.eventType ?? eventTypeMap[event]; + let eventSubType = .message.properties.eventSubtype && eventType === 'conversion' && (.message.properties.eventSubtype in $.ALLOWED_EVENT_SUBTYPES) ? .message.properties.eventSubtype; $.assert(eventType, "eventType is mandatory for track call"); let payload = .message.().({ index: .properties.index, @@ -32,12 +36,28 @@ steps: filters: .properties.filters, objectIDs: .properties.objectIds, positions: .properties.positions, + value: $.isDefinedAndNotNull(.properties.currency) ? .properties.value, + currency: .properties.currency, userToken: {{{{$.getGenericPaths("userId", "||")}}}}, eventName: event, - eventType: eventType + eventType: eventType, + eventSubtype: eventSubType }); $.context.payload = $.genericpayloadValidator(payload); + - name: prepareObjectDataBlock + condition: $.context.payload.eventType === "conversion" && $.isDefinedAndNotNull(^.message.properties.products) && Array.isArray(^.message.properties.products) + description: | + Populate list of objectData + template: | + const products = ^.message.properties.products + products.($.removeUndefinedAndNullValues({ + "queryID" : $.isDefinedAndNotNull(.queryID) ? String(.queryID) : null, + "price": $.isDefinedAndNotNull(.price) && $.isDefinedAndNotNull(^.message.properties.currency) ? String(.price) : null, + "quantity": $.isDefinedAndNotNull(.quantity)? Number(.quantity) : null, + "discount": $.isDefinedAndNotNull(.discount) ? String(.discount) : null + }))[] + - name: populateProductsData condition: | .message.properties.products && @@ -55,11 +75,14 @@ steps: const products = .message.properties.products; const objectIDs = ~r products.objectId; $.context.payload.objectIDs = Array.isArray(objectIDs) ? objectIDs[:20]:$.context.payload.objectIDs; + $.context.payload.objectData = $.outputs.prepareObjectDataBlock + $.validatePayload($.context.payload) - name: validateDestPayload template: | const filters = $.context.payload.filters; const objectIDs = $.context.payload.objectIDs; + const objectData = $.context.payload.objectData; $.assert(!(filters && objectIDs), "event can't have both objectIds and filters at the same time."); $.assert(filters.length || objectIDs.length, "Either filters or objectIds is required and must be non empty."); diff --git a/src/cdk/v2/destinations/algolia/rtWorkflow.yaml b/src/cdk/v2/destinations/algolia/rtWorkflow.yaml index 758a71bf5b..f5442f3209 100644 --- a/src/cdk/v2/destinations/algolia/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/algolia/rtWorkflow.yaml @@ -2,7 +2,6 @@ bindings: - path: ../../../../v0/destinations/algolia/config - name: handleRtTfSingleEventError path: ../../../../v0/util/index - steps: - name: validateInput template: | @@ -28,10 +27,14 @@ steps: $.outputs.transform#idx.error.( $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) )[] + - name: batchSuccessfulEvents description: Batches the successfulEvents template: | - let batches = $.chunk($.outputs.successfulEvents, $.MAX_BATCH_SIZE); + const dontBatchTrueEvents = $.outputs.successfulEvents{.metadata.dontBatch}[]; + const dontBatchFalseEvents = $.outputs.successfulEvents{!.metadata.dontBatch}[]; + + let batches = [...$.chunk(dontBatchFalseEvents, $.MAX_BATCH_SIZE), ...$.chunk(dontBatchTrueEvents, 1)]; batches@batch.({ "batchedRequest": { "body": { @@ -56,6 +59,7 @@ steps: "statusCode": 200, "destination": batch[0].destination })[]; + - name: finalPayload template: | [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] diff --git a/src/cdk/v2/destinations/bloomreach/config.ts b/src/cdk/v2/destinations/bloomreach/config.ts new file mode 100644 index 0000000000..90fbcc63c6 --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/config.ts @@ -0,0 +1,30 @@ +import { getMappingConfig } from '../../../../v0/util'; + +export const CUSTOMER_COMMAND = 'customers'; +export const CUSTOMER_EVENT_COMMAND = 'customers/events'; +export const MAX_BATCH_SIZE = 50; + +// ref:- https://documentation.bloomreach.com/engagement/reference/batch-commands-2 +export const getBatchEndpoint = (apiBaseUrl: string, projectToken: string): string => + `${apiBaseUrl}/track/v2/projects/${projectToken}/batch`; + +const CONFIG_CATEGORIES = { + CUSTOMER_PROPERTIES_CONFIG: { name: 'BloomreachCustomerPropertiesConfig' }, +}; +const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); +export const EXCLUSION_FIELDS: string[] = [ + 'email', + 'firstName', + 'firstname', + 'first_name', + 'lastName', + 'lastname', + 'last_name', + 'name', + 'phone', + 'city', + 'birthday', + 'country', +]; +export const CUSTOMER_PROPERTIES_CONFIG = + MAPPING_CONFIG[CONFIG_CATEGORIES.CUSTOMER_PROPERTIES_CONFIG.name]; diff --git a/src/cdk/v2/destinations/bloomreach/data/BloomreachCustomerPropertiesConfig.json b/src/cdk/v2/destinations/bloomreach/data/BloomreachCustomerPropertiesConfig.json new file mode 100644 index 0000000000..cb4c2f7201 --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/data/BloomreachCustomerPropertiesConfig.json @@ -0,0 +1,36 @@ +[ + { + "destKey": "first_name", + "sourceKeys": "firstName", + "sourceFromGenericMap": true + }, + { + "destKey": "last_name", + "sourceKeys": "lastName", + "sourceFromGenericMap": true + }, + { + "destKey": "email", + "sourceKeys": "emailOnly", + "sourceFromGenericMap": true + }, + { + "destKey": "phone", + "sourceKeys": "phone", + "sourceFromGenericMap": true + }, + { + "destKey": "city", + "sourceKeys": "city", + "sourceFromGenericMap": true + }, + { + "destKey": "country", + "sourceKeys": ["traits.address.country", "context.traits.address.country"] + }, + { + "destKey": "birthday", + "sourceKeys": "birthday", + "sourceFromGenericMap": true + } +] diff --git a/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml b/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml new file mode 100644 index 0000000000..5a9dcaa18d --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/procWorkflow.yaml @@ -0,0 +1,119 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + - name: toUnixTimestamp + path: ../../../../v0/util + - name: base64Convertor + path: ../../../../v0/util + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - name: generateExclusionList + path: ../../../../v0/util + - name: extractCustomFields + path: ../../../../v0/util + - name: constructPayload + path: ../../../../v0/util + - path: ./utils + - path: ./config + +steps: + - name: messageType + template: | + $.context.messageType = .message.type.toLowerCase(); + + - name: validateInput + template: | + let messageType = $.context.messageType; + $.assert(messageType, "message Type is not present. Aborting"); + $.assert(messageType in {{$.EventType.([.IDENTIFY,.TRACK,.PAGE,.SCREEN])}}, "message type " + messageType + " is not supported"); + $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); + $.assertConfig(.destination.Config.apiKey, "API Key is not present . Aborting"); + $.assertConfig(.destination.Config.apiSecret, "API Secret is not present. Aborting"); + $.assertConfig(.destination.Config.projectToken, "Project Token is not present. Aborting"); + $.assertConfig(.destination.Config.hardID, "Hard ID is not present. Aborting"); + $.assertConfig(.destination.Config.softID, "Soft ID is not present. Aborting"); + $.assert(.message.timestamp ?? .message.originalTimestamp, "Timestamp is not present. Aborting"); + const userId = .message.().( + {{{{$.getGenericPaths("userIdOnly")}}}}; + ); + $.assert(userId || .message.anonymousId, "Either one of userId or anonymousId is required. Aborting"); + + - name: prepareIdentifyPayload + condition: $.context.messageType === {{$.EventType.IDENTIFY}} + template: | + const customerIDs = $.prepareCustomerIDs(.message, .destination); + const customerProperties = $.constructPayload(.message, $.CUSTOMER_PROPERTIES_CONFIG); + const extraCustomerProperties = $.extractCustomFields(.message, {}, ['traits', 'context.traits'], $.EXCLUSION_FIELDS); + const properties = { + ...customerProperties, + ...extraCustomerProperties + } + const data = .message.().({ + "customer_ids": customerIDs, + "update_timestamp": $.toUnixTimestamp({{{{$.getGenericPaths("timestamp")}}}}), + properties + }); + + $.context.payload = $.removeUndefinedAndNullValues({name: $.CUSTOMER_COMMAND, data}) + + - name: prepareEventName + steps: + - name: pageEventName + condition: $.context.messageType === {{$.EventType.PAGE}} + template: | + const category = .message.category || .message.properties.category; + const name = .message.name || .message.properties.name; + const eventNameArray = ["Viewed"]; + category ? eventNameArray.push(category); + name ? eventNameArray.push(name); + eventNameArray.push("Page"); + $.context.event = eventNameArray.join(" "); + - name: screenEventName + condition: $.context.messageType === {{$.EventType.SCREEN}} + template: | + const category = .message.category || .message.properties.category; + const name = .message.name || .message.properties.name; + const eventNameArray = ["Viewed"]; + category ? eventNameArray.push(category); + name ? eventNameArray.push(name); + eventNameArray.push("Screen"); + $.context.event = eventNameArray.join(" "); + - name: trackEventName + condition: $.context.messageType === {{$.EventType.TRACK}} + template: | + $.assert(.message.event, "Event name is required. Aborting"); + $.context.event = .message.event + + - name: prepareTrackPageScreenPayload + condition: $.context.messageType !== {{$.EventType.IDENTIFY}} + template: | + const customerIDs = $.prepareCustomerIDs(.message, .destination); + const data = .message.().({ + "customer_ids": customerIDs, + "timestamp": $.toUnixTimestamp({{{{$.getGenericPaths("timestamp")}}}}), + "properties": .properties, + "event_type": $.context.event, + }); + + $.context.payload = $.removeUndefinedAndNullValues({name: $.CUSTOMER_EVENT_COMMAND, data}) + + - name: buildResponse + description: In batchMode we return payload directly + condition: $.batchMode + template: | + $.context.payload + else: + name: buildResponseForProcessTransformation + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = $.getBatchEndpoint(.destination.Config.apiBaseUrl, .destination.Config.projectToken); + response.method = "POST"; + response.headers = { + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.destination.Config.apiKey + ":" + .destination.Config.apiSecret) + } + response; diff --git a/src/cdk/v2/destinations/bloomreach/rtWorkflow.yaml b/src/cdk/v2/destinations/bloomreach/rtWorkflow.yaml new file mode 100644 index 0000000000..b8b27ca02e --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/rtWorkflow.yaml @@ -0,0 +1,76 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + - path: ./utils + exportAll: true + - name: base64Convertor + path: ../../../../v0/util + - name: toUnixTimestamp + path: ../../../../v0/util + - name: BatchUtils + path: '@rudderstack/workflow-engine' + - path: ./config + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata, + "statusCode": 200 + })[] + + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + let batches = $.BatchUtils.chunkArrayBySizeAndLength( + $.outputs.successfulEvents, {maxItems: $.MAX_BATCH_SIZE}).items; + + batches@batch.({ + "batchedRequest": { + "body": { + "JSON": {"commands": ~r batch.batchedRequest[]}, + "JSON_ARRAY": {}, + "XML": {}, + "FORM": {} + }, + "version": "1", + "type": "REST", + "method": "POST", + "endpoint": batch[0].destination.Config.().($.getBatchEndpoint(.apiBaseUrl, .projectToken)), + "headers": batch[0].destination.Config.().({ + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.apiKey + ":" + .apiSecret) + }), + "params": {}, + "files": {} + }, + "metadata": ~r batch.metadata[], + "batched": true, + "statusCode": 200, + "destination": batch[0].destination + })[]; + + - name: finalPayload + template: | + [...$.outputs.batchSuccessfulEvents, ...$.outputs.failedEvents] diff --git a/src/cdk/v2/destinations/bloomreach/utils.ts b/src/cdk/v2/destinations/bloomreach/utils.ts new file mode 100644 index 0000000000..f834fa74e7 --- /dev/null +++ b/src/cdk/v2/destinations/bloomreach/utils.ts @@ -0,0 +1,31 @@ +import { isObject, isEmptyObject, getIntegrationsObj } from '../../../../v0/util'; +import { RudderMessage, Destination } from '../../../../types'; + +const getCustomerIDsFromIntegrationObject = (message: RudderMessage): any => { + const integrationObj = getIntegrationsObj(message, 'bloomreach' as any) || {}; + const { hardID, softID } = integrationObj; + const customerIDs = {}; + + if (isObject(hardID) && !isEmptyObject(hardID)) { + Object.keys(hardID).forEach((id) => { + customerIDs[id] = hardID[id]; + }); + } + + if (isObject(softID) && !isEmptyObject(softID)) { + Object.keys(softID).forEach((id) => { + customerIDs[id] = softID[id]; + }); + } + + return customerIDs; +}; + +export const prepareCustomerIDs = (message: RudderMessage, destination: Destination): any => { + const customerIDs = { + [destination.Config.hardID]: message.userId, + [destination.Config.softID]: message.anonymousId, + ...getCustomerIDsFromIntegrationObject(message), + }; + return customerIDs; +}; diff --git a/src/cdk/v2/destinations/bluecore/config.js b/src/cdk/v2/destinations/bluecore/config.js index 9b9cde9c66..98e1bb4b23 100644 --- a/src/cdk/v2/destinations/bluecore/config.js +++ b/src/cdk/v2/destinations/bluecore/config.js @@ -1,6 +1,6 @@ const { getMappingConfig } = require('../../../../v0/util'); -const BASE_URL = 'https://api.bluecore.com/api/track/mobile/v1'; +const BASE_URL = 'https://api.bluecore.app/api/track/mobile/v1'; const CONFIG_CATEGORIES = { IDENTIFY: { @@ -46,6 +46,24 @@ const EVENT_NAME_MAPPING = [ const BLUECORE_EXCLUSION_FIELDS = ['query', 'order_id', 'total']; +const IDENTIFY_EXCLUSION_LIST = [ + 'name', + 'firstName', + 'first_name', + 'firstname', + 'lastName', + 'last_name', + 'lastname', + 'email', + 'age', + 'sex', + 'address', + 'action', + 'event', +]; + +const TRACK_EXCLUSION_LIST = [...IDENTIFY_EXCLUSION_LIST, 'query', 'order_id', 'total', 'products']; + const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); module.exports = { CONFIG_CATEGORIES, @@ -53,4 +71,6 @@ module.exports = { EVENT_NAME_MAPPING, BASE_URL, BLUECORE_EXCLUSION_FIELDS, + IDENTIFY_EXCLUSION_LIST, + TRACK_EXCLUSION_LIST, }; diff --git a/src/cdk/v2/destinations/bluecore/data/bluecoreCommonConfig.json b/src/cdk/v2/destinations/bluecore/data/bluecoreCommonConfig.json index be74c7c4b3..536f77a045 100644 --- a/src/cdk/v2/destinations/bluecore/data/bluecoreCommonConfig.json +++ b/src/cdk/v2/destinations/bluecore/data/bluecoreCommonConfig.json @@ -35,7 +35,7 @@ }, { "destKey": "properties.customer.email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/cdk/v2/destinations/bluecore/procWorkflow.yaml b/src/cdk/v2/destinations/bluecore/procWorkflow.yaml index 480bced699..9828ac593c 100644 --- a/src/cdk/v2/destinations/bluecore/procWorkflow.yaml +++ b/src/cdk/v2/destinations/bluecore/procWorkflow.yaml @@ -26,7 +26,7 @@ steps: condition: $.outputs.messageType === {{$.EventType.IDENTIFY}} template: | const payload = $.constructProperties(.message); - payload.token = .destination.Config.bluecoreNamespace; + payload.properties.token = .destination.Config.bluecoreNamespace; $.verifyPayload(payload, .message); payload.event = payload.event ?? 'customer_patch'; payload.properties.distinct_id = $.populateAccurateDistinctId(payload, .message); @@ -50,7 +50,7 @@ steps: const temporaryProductArray = newPayload.properties.products ?? $.createProductForStandardEcommEvent(^.message, eventName); newPayload.properties.products = $.normalizeProductArray(temporaryProductArray); newPayload.event = eventName; - newPayload.token = ^.destination.Config.bluecoreNamespace; + newPayload.properties.token = ^.destination.Config.bluecoreNamespace; $.verifyPayload(newPayload, ^.message); $.removeUndefinedNullValuesAndEmptyObjectArray(newPayload) )[]; @@ -61,7 +61,7 @@ steps: const response = $.defaultRequestConfig(); response.body.JSON = .; response.method = "POST"; - response.endpoint = "https://api.bluecore.com/api/track/mobile/v1"; + response.endpoint = "https://api.bluecore.app/api/track/mobile/v1"; response.headers = { "Content-Type": "application/json" }; diff --git a/src/cdk/v2/destinations/bluecore/utils.js b/src/cdk/v2/destinations/bluecore/utils.js index 22ec254fe2..91eda60d0d 100644 --- a/src/cdk/v2/destinations/bluecore/utils.js +++ b/src/cdk/v2/destinations/bluecore/utils.js @@ -12,9 +12,10 @@ const { validateEventName, constructPayload, getDestinationExternalID, + extractCustomFields, } = require('../../../../v0/util'); const { CommonUtils } = require('../../../../util/common'); -const { EVENT_NAME_MAPPING } = require('./config'); +const { EVENT_NAME_MAPPING, IDENTIFY_EXCLUSION_LIST, TRACK_EXCLUSION_LIST } = require('./config'); const { EventType } = require('../../../../constants'); const { MAPPING_CONFIG, CONFIG_CATEGORIES } = require('./config'); @@ -167,6 +168,41 @@ const normalizeProductArray = (products) => { return finalProductArray; }; +const mapCustomProperties = (message) => { + let customerProperties; + const customProperties = { properties: {} }; + const messageType = message.type.toUpperCase(); + switch (messageType) { + case 'IDENTIFY': + customerProperties = extractCustomFields( + message, + {}, + ['traits', 'context.traits'], + IDENTIFY_EXCLUSION_LIST, + ); + customProperties.properties.customer = customerProperties; + break; + case 'TRACK': + customerProperties = extractCustomFields( + message, + {}, + ['traits', 'context.traits'], + IDENTIFY_EXCLUSION_LIST, + ); + customProperties.properties = extractCustomFields( + message, + {}, + ['properties'], + TRACK_EXCLUSION_LIST, + ); + customProperties.properties.customer = customerProperties; + break; + default: + break; + } + return customProperties; +}; + /** * Constructs properties based on the given message. * @@ -178,7 +214,12 @@ const constructProperties = (message) => { const commonPayload = constructPayload(message, MAPPING_CONFIG[commonCategory.name]); const category = CONFIG_CATEGORIES[message.type.toUpperCase()]; const typeSpecificPayload = constructPayload(message, MAPPING_CONFIG[category.name]); - const finalPayload = lodash.merge(commonPayload, typeSpecificPayload); + const typeSpecificCustomProperties = mapCustomProperties(message); + const finalPayload = lodash.merge( + commonPayload, + typeSpecificPayload, + typeSpecificCustomProperties, + ); return finalPayload; }; diff --git a/src/cdk/v2/destinations/emarsys/config.js b/src/cdk/v2/destinations/emarsys/config.js new file mode 100644 index 0000000000..83067c3cd3 --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/config.js @@ -0,0 +1,24 @@ +const ALLOWED_OPT_IN_VALUES = ['1', '2', '']; +const groupedSuccessfulPayload = { + identify: { + method: 'PUT', + batches: [], + }, + group: { + method: 'POST', + batches: [], + }, + track: { + method: 'POST', + batches: [], + }, +}; + +module.exports = { + MAX_BATCH_SIZE: 1000, + EMAIL_FIELD_ID: 3, + OPT_IN_FILED_ID: 31, + ALLOWED_OPT_IN_VALUES, + MAX_BATCH_SIZE_BYTES: 8000000, // 8 MB, + groupedSuccessfulPayload, +}; diff --git a/src/cdk/v2/destinations/emarsys/procWorkflow.yaml b/src/cdk/v2/destinations/emarsys/procWorkflow.yaml new file mode 100644 index 0000000000..a5c0b33f38 --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/procWorkflow.yaml @@ -0,0 +1,88 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + exportAll: true + - name: removeUndefinedValues + path: ../../../../v0/util + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - name: defaultRequestConfig + path: ../../../../v0/util + - name: getIntegrationsObj + path: ../../../../v0/util + - name: getFieldValueFromMessage + path: ../../../../v0/util + - name: CommonUtils + path: ../../../../util/common + - path: ./utils + - path: ./config + - path: lodash + name: cloneDeep + +steps: + - name: checkIfProcessed + condition: .message.statusCode + template: | + $.batchMode ? .message.body.JSON : .message + onComplete: return + - name: messageType + template: | + .message.type.toLowerCase() + - name: validateInput + template: | + let messageType = $.outputs.messageType; + $.assert(messageType, "Message type is not present. Aborting message."); + $.assert(messageType in {{$.EventType.([.TRACK, .IDENTIFY, .GROUP])}}, + "message type " + messageType + " is not supported") + $.assertConfig(.destination.Config.emersysUsername, "Emersys user name is not configured. Aborting"); + $.assertConfig(.destination.Config.emersysUserSecret, "Emersys user secret is not configured. Aborting"); + + - name: validateInputForTrack + description: Additional validation for Track events + condition: $.outputs.messageType === {{$.EventType.TRACK}} + template: | + $.assert(.message.event, "event could not be mapped to conversion rule. Aborting.") + - name: preparePayloadForIdentify + description: | + Builds identify payload. ref: https://dev.emarsys.com/docs/core-api-reference/f8ljhut3ac2i1-update-contacts + condition: $.outputs.messageType === {{$.EventType.IDENTIFY}} + template: | + $.context.payload = $.buildIdentifyPayload(.message, .destination.Config,); + - name: preparePayloadForGroup + description: | + Builds group payload. ref: https://dev.emarsys.com/docs/core-api-reference/1m0m70hy3tuov-add-contacts-to-a-contact-list + condition: $.outputs.messageType === {{$.EventType.GROUP}} + template: | + $.context.payload = $.buildGroupPayload(.message, .destination.Config,); + - name: preparePayloadForTrack + description: | + Builds track payload. ref: https://dev.emarsys.com/docs/core-api-reference/fl0xx6rwfbwqb-trigger-an-external-event + condition: $.outputs.messageType === {{$.EventType.TRACK}} + template: | + const properties = ^.message.properties; + const integrationObject = $.getIntegrationsObj(^.message, 'emarsys'); + const emersysIdentifierId = $.deduceCustomIdentifier(integrationObject, ^.destination.Config.emersysCustomIdentifier); + const payload = { + key_id: emersysIdentifierId, + external_id: $.deduceExternalIdValue(^.message,emersysIdentifierId,.destination.Config.fieldMapping), + trigger_id: integrationObject.trigger_id, + data: properties.data, + attachment:$.CommonUtils.toArray(properties.attachment), + event_time: $.getFieldValueFromMessage(^.message, 'timestamp'), + }; + $.context.payload = { + eventType: ^.message.type, + destinationPayload: { + payload: $.removeUndefinedAndNullValues(payload), + eventId: $.deduceEventId(^.message,.destination.Config), + }, + }; + - name: buildResponse + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = $.deduceEndPoint($.context.payload,.destination.Config); + response.method = "POST"; + response.headers = $.buildHeader(.destination.Config) + response diff --git a/src/cdk/v2/destinations/emarsys/rtWorkflow.yaml b/src/cdk/v2/destinations/emarsys/rtWorkflow.yaml new file mode 100644 index 0000000000..0e7132ccad --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/rtWorkflow.yaml @@ -0,0 +1,38 @@ +bindings: + - path: ./utils + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "message": .[], + "destination": ^ [idx].destination, + "metadata": ^ [idx].metadata + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + $.context.batchedPayload = $.batchResponseBuilder($.outputs.successfulEvents); + + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.context.batchedPayload] diff --git a/src/cdk/v2/destinations/emarsys/utils.js b/src/cdk/v2/destinations/emarsys/utils.js new file mode 100644 index 0000000000..e093064fc5 --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/utils.js @@ -0,0 +1,411 @@ +const lodash = require('lodash'); +const crypto = require('crypto'); +const { + InstrumentationError, + ConfigurationError, + isDefinedAndNotNullAndNotEmpty, + removeUndefinedAndNullAndEmptyValues, + removeUndefinedAndNullValues, + isDefinedAndNotNull, +} = require('@rudderstack/integrations-lib'); +const { + getIntegrationsObj, + validateEventName, + getValueFromMessage, + getHashFromArray, +} = require('../../../../v0/util'); +const { + EMAIL_FIELD_ID, + MAX_BATCH_SIZE, + OPT_IN_FILED_ID, + ALLOWED_OPT_IN_VALUES, + MAX_BATCH_SIZE_BYTES, + groupedSuccessfulPayload, +} = require('./config'); +const { EventType } = require('../../../../constants'); + +const base64Sha = (str) => { + const hexDigest = crypto.createHash('sha1').update(str).digest('hex'); + return Buffer.from(hexDigest).toString('base64'); +}; + +const getWsseHeader = (user, secret) => { + const nonce = crypto.randomBytes(16).toString('hex'); + const timestamp = new Date().toISOString(); + + const digest = base64Sha(nonce + timestamp + secret); + return `UsernameToken Username="${user}", PasswordDigest="${digest}", Nonce="${nonce}", Created="${timestamp}"`; +}; + +const buildHeader = (destConfig) => { + const { emersysUsername, emersysUserSecret } = destConfig; + if ( + !isDefinedAndNotNullAndNotEmpty(emersysUsername) || + !isDefinedAndNotNullAndNotEmpty(emersysUserSecret) + ) { + throw new ConfigurationError('Either Emarsys user name or user secret is missing. Aborting'); + } + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': getWsseHeader(emersysUsername, emersysUserSecret), + }; +}; + +const deduceCustomIdentifier = (integrationObject, emersysCustomIdentifier) => + integrationObject?.customIdentifierId || emersysCustomIdentifier || EMAIL_FIELD_ID; + +const buildIdentifyPayload = (message, destConfig) => { + let destinationPayload; + const { fieldMapping, emersysCustomIdentifier, discardEmptyProperties, defaultContactList } = + destConfig; + const payload = {}; + + const integrationObject = getIntegrationsObj(message, 'emarsys'); + const finalContactList = integrationObject?.contactListId || defaultContactList; + if (!finalContactList || !isDefinedAndNotNullAndNotEmpty(String(finalContactList))) { + throw new InstrumentationError( + 'Cannot a find a specific contact list either through configuration or via integrations object', + ); + } + if (fieldMapping) { + fieldMapping.forEach((trait) => { + const { rudderProperty, emersysProperty } = trait; + const value = getValueFromMessage(message, [ + `traits.${rudderProperty}`, + `context.traits.${rudderProperty}`, + ]); + if (value) { + payload[emersysProperty] = value; + } + }); + } + const emersysIdentifier = deduceCustomIdentifier(integrationObject, emersysCustomIdentifier); + const finalPayload = + discardEmptyProperties === true + ? removeUndefinedAndNullAndEmptyValues(payload) // empty property value has a significance in emersys + : removeUndefinedAndNullValues(payload); + if ( + isDefinedAndNotNull(finalPayload[OPT_IN_FILED_ID]) && + !ALLOWED_OPT_IN_VALUES.includes(String(finalPayload[OPT_IN_FILED_ID])) + ) { + throw new InstrumentationError( + `Only ${ALLOWED_OPT_IN_VALUES} values are allowed for optin field`, + ); + } + + if (isDefinedAndNotNullAndNotEmpty(payload[emersysIdentifier])) { + destinationPayload = { + key_id: emersysIdentifier, + contacts: [finalPayload], + contact_list_id: finalContactList, + }; + } else { + throw new InstrumentationError( + 'Either configured custom contact identifier value or default identifier email value is missing', + ); + } + return { eventType: message.type, destinationPayload }; +}; + +const findRudderPropertyByEmersysProperty = (emersysProperty, fieldMapping) => { + // find the object where the emersysProperty matches the input + const item = lodash.find(fieldMapping, { emersysProperty: String(emersysProperty) }); + // Return the rudderProperty if the object is found, otherwise return null + return item ? item.rudderProperty : 'email'; +}; + +const deduceExternalIdValue = (message, emersysIdentifier, fieldMapping) => { + const configuredPayloadProperty = findRudderPropertyByEmersysProperty( + emersysIdentifier, + fieldMapping, + ); + const externalIdValue = getValueFromMessage(message, [ + `traits.${configuredPayloadProperty}`, + `context.traits.${configuredPayloadProperty}`, + ]); + + if (!isDefinedAndNotNull(deduceExternalIdValue)) { + throw new InstrumentationError( + `Could not find value for externalId required in ${message.type} call. Aborting.`, + ); + } + + return externalIdValue; +}; + +const buildGroupPayload = (message, destConfig) => { + const { emersysCustomIdentifier, defaultContactList, fieldMapping } = destConfig; + const integrationObject = getIntegrationsObj(message, 'emarsys'); + const emersysIdentifier = deduceCustomIdentifier(integrationObject, emersysCustomIdentifier); + const externalIdValue = deduceExternalIdValue(message, emersysIdentifier, fieldMapping); + if (!isDefinedAndNotNull(externalIdValue)) { + throw new InstrumentationError( + `No value found in payload for contact custom identifier of id ${emersysIdentifier}`, + ); + } + const payload = { + key_id: emersysIdentifier, + external_ids: [externalIdValue], + }; + return { + eventType: message.type, + destinationPayload: { + payload, + contactListId: message.groupId || defaultContactList, + }, + }; +}; + +const deduceEventId = (message, destConfig) => { + let eventId; + const { eventsMapping } = destConfig; + const { event } = message; + validateEventName(event); + if (Array.isArray(eventsMapping) && eventsMapping.length > 0) { + const keyMap = getHashFromArray(eventsMapping, 'from', 'to', false); + eventId = keyMap[event]; + } + if (!eventId) { + throw new ConfigurationError(`${event} is not mapped to any Emersys external event. Aborting`); + } + return eventId; +}; + +const deduceEndPoint = (finalPayload) => { + let endPoint; + let eventId; + let contactListId; + const { eventType, destinationPayload } = finalPayload; + switch (eventType) { + case EventType.IDENTIFY: + endPoint = 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1'; + break; + case EventType.GROUP: + contactListId = destinationPayload.contactListId; + endPoint = `https://api.emarsys.net/api/v2/contactlist/${contactListId}/add`; + break; + case EventType.TRACK: + eventId = destinationPayload.eventId; + endPoint = `https://api.emarsys.net/api/v2/event/${eventId}/trigger`; + break; + default: + break; + } + return endPoint; +}; + +const estimateJsonSize = (obj) => new Blob([JSON.stringify(obj)]).size; + +const createSingleIdentifyPayload = (keyId, contacts, contactListId) => ({ + key_id: keyId, + contacts, + contact_list_id: contactListId, +}); + +const ensureSizeConstraints = (contacts) => { + const chunks = []; + let currentBatch = []; + + contacts.forEach((contact) => { + // Start a new batch if adding the next contact exceeds size limits + if ( + currentBatch.length === 0 || + estimateJsonSize([...currentBatch, contact]) < MAX_BATCH_SIZE_BYTES + ) { + currentBatch.push(contact); + } else { + chunks.push(currentBatch); + currentBatch = [contact]; + } + }); + + // Add the remaining batch if not empty + if (currentBatch.length > 0) { + chunks.push(currentBatch); + } + + return chunks; +}; + +const createIdentifyBatches = (events) => { + const groupedIdentifyPayload = lodash.groupBy( + events, + (item) => + `${item.message[0].body.JSON.destinationPayload.key_id}-${item.message[0].body.JSON.destinationPayload.contact_list_id}`, + ); + return lodash.flatMap(groupedIdentifyPayload, (group) => { + const firstItem = group[0].message[0].body.JSON.destinationPayload; + // eslint-disable-next-line @typescript-eslint/naming-convention + const { key_id, contact_list_id } = firstItem; + + const allContacts = lodash.flatMap( + group, + (item) => item.message[0].body.JSON.destinationPayload.contacts, + ); + const initialChunks = lodash.chunk(allContacts, MAX_BATCH_SIZE); + const finalChunks = lodash.flatMap(initialChunks, ensureSizeConstraints); + + // Include metadata for each chunk + return finalChunks.map((contacts) => ({ + payload: createSingleIdentifyPayload(key_id, contacts, contact_list_id), + metadata: group.map((g) => g.metadata), + })); + }); +}; + +const createGroupBatches = (events) => { + const grouped = lodash.groupBy( + events, + (item) => + `${item.message[0].body.JSON.destinationPayload.payload.key_id}-${item.message[0].body.JSON.destinationPayload.contactListId}`, + ); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return Object.entries(grouped).flatMap(([key, group]) => { + const keyId = group[0].message[0].body.JSON.destinationPayload.payload.key_id; + const { contactListId } = group[0].message[0].body.JSON.destinationPayload; + const combinedExternalIds = group.reduce((acc, item) => { + acc.push(...item.message[0].body.JSON.destinationPayload.payload.external_ids); + return acc; + }, []); + + const idChunks = lodash.chunk(combinedExternalIds, MAX_BATCH_SIZE); + + return idChunks.map((chunk) => ({ + endpoint: `https://api.emarsys.net/api/v2/contactlist/${contactListId}/add`, + payload: { + key_id: keyId, + external_ids: chunk, + }, + metadata: group.map((g) => g.metadata), + })); + }); +}; + +const createTrackBatches = (events) => [ + { + endpoint: events[0].message[0].endpoint, + payload: events[0].message[0].body.JSON.destinationPayload.payload, + metadata: [events[0].metadata], + }, +]; +const formatIdentifyPayloadsWithEndpoint = (combinedPayloads, endpointUrl = '') => + combinedPayloads.map((singleCombinedPayload) => ({ + endpoint: endpointUrl, + payload: singleCombinedPayload.payload, + metadata: singleCombinedPayload.metadata, + })); + +const buildBatchedRequest = (batches, method, constants, batchedStatus = true) => + batches.map((batch) => ({ + batchedRequest: { + body: { + JSON: batch.payload, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: constants.version, + type: constants.type, + method, + endpoint: batch.endpoint, + headers: constants.headers, + params: {}, + files: {}, + }, + metadata: batch.metadata, + batched: batchedStatus, + statusCode: 200, + destination: constants.destination, + })); + +// Helper to initialize the constants used across batch processing +function initializeConstants(successfulEvents) { + if (successfulEvents.length === 0) return null; + return { + version: successfulEvents[0].message[0].version, + type: successfulEvents[0].message[0].type, + headers: successfulEvents[0].message[0].headers, + destination: successfulEvents[0].destination, + }; +} + +// Helper to append requests based on batched events and constants +function appendRequestsToOutput(groupPayload, output, constants, batched = true) { + if (groupPayload.batches) { + const requests = buildBatchedRequest( + groupPayload.batches, + groupPayload.method, + constants, + batched, + ); + output.push(...requests); + } +} + +// Process batches based on event types +function processEventBatches(typedEventGroups, constants) { + let batchesOfIdentifyEvents; + const finalOutput = []; + + // Process each event group based on type + Object.keys(typedEventGroups).forEach((eventType) => { + switch (eventType) { + case EventType.IDENTIFY: + batchesOfIdentifyEvents = createIdentifyBatches(typedEventGroups[eventType]); + groupedSuccessfulPayload.identify.batches = formatIdentifyPayloadsWithEndpoint( + batchesOfIdentifyEvents, + 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + ); + break; + case EventType.GROUP: + groupedSuccessfulPayload.group.batches = createGroupBatches(typedEventGroups[eventType]); + break; + case EventType.TRACK: + groupedSuccessfulPayload.track.batches = createTrackBatches(typedEventGroups[eventType]); + break; + default: + break; + } + }); + + // Convert batches into requests for each event type and push to final output + appendRequestsToOutput(groupedSuccessfulPayload.identify, finalOutput, constants); + appendRequestsToOutput(groupedSuccessfulPayload.group, finalOutput, constants); + appendRequestsToOutput(groupedSuccessfulPayload.track, finalOutput, constants, false); + + return finalOutput; +} + +// Entry function to create batches from successful events +function batchResponseBuilder(successfulEvents) { + const constants = initializeConstants(successfulEvents); + if (!constants) return []; + + const typedEventGroups = lodash.groupBy( + successfulEvents, + (event) => event.message[0].body.JSON.eventType, + ); + + return processEventBatches(typedEventGroups, constants); +} + +module.exports = { + buildIdentifyPayload, + buildGroupPayload, + buildHeader, + deduceEndPoint, + batchResponseBuilder, + base64Sha, + getWsseHeader, + findRudderPropertyByEmersysProperty, + formatIdentifyPayloadsWithEndpoint, + createSingleIdentifyPayload, + createIdentifyBatches, + ensureSizeConstraints, + createGroupBatches, + deduceExternalIdValue, + deduceEventId, + deduceCustomIdentifier, +}; diff --git a/src/cdk/v2/destinations/emarsys/utils.test.js b/src/cdk/v2/destinations/emarsys/utils.test.js new file mode 100644 index 0000000000..3802567ecb --- /dev/null +++ b/src/cdk/v2/destinations/emarsys/utils.test.js @@ -0,0 +1,543 @@ +const { EVENT_TYPE } = require('rudder-transformer-cdk/build/constants'); +const { + buildIdentifyPayload, + buildGroupPayload, + base64Sha, + getWsseHeader, + findRudderPropertyByEmersysProperty, + createGroupBatches, + deduceEventId, +} = require('./utils'); +const { + checkIfEventIsAbortableAndExtractErrorMessage, +} = require('../../../../v1/destinations/emarsys/networkHandler'); +const crypto = require('crypto'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { responses } = require('../../../../../test/testHelper'); + +describe('Emarsys utils', () => { + describe('base64Sha', () => { + it('should return a base64 encoded SHA1 hash of the input string', () => { + const input = 'test'; + const expected = 'YTk0YThmZTVjY2IxOWJhNjFjNGMwODczZDM5MWU5ODc5ODJmYmJkMw=='; + const result = base64Sha(input); + expect(result).toEqual(expected); + }); + + it('should return an empty string when input is empty', () => { + const input = ''; + const expected = 'ZGEzOWEzZWU1ZTZiNGIwZDMyNTViZmVmOTU2MDE4OTBhZmQ4MDcwOQ=='; + const result = base64Sha(input); + expect(result).toEqual(expected); + }); + }); + + describe('getWsseHeader', () => { + beforeEach(() => { + jest + .spyOn(crypto, 'randomBytes') + .mockReturnValue(Buffer.from('abcdef1234567890abcdef1234567890', 'hex')); + jest.spyOn(Date.prototype, 'toISOString').mockReturnValue('2024-04-28T12:34:56.789Z'); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should generate a correct WSSE header', () => { + const user = 'testUser'; + const secret = 'testSecret'; + const expectedNonce = 'abcdef1234567890abcdef1234567890'; + const expectedTimestamp = '2024-04-28T12:34:56.789Z'; + const expectedDigest = base64Sha(expectedNonce + expectedTimestamp + secret); + const expectedHeader = `UsernameToken Username="${user}", PasswordDigest="${expectedDigest}", Nonce="${expectedNonce}", Created="${expectedTimestamp}"`; + const result = getWsseHeader(user, secret); + + expect(result).toBe(expectedHeader); + }); + }); + + describe('buildIdentifyPayload', () => { + it('should correctly build payload with field mapping', () => { + const message = { + type: 'identify', + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + optin: 1, + }, + }; + const destination = { + fieldMapping: [ + { rudderProperty: 'firstName', emersysProperty: '1' }, + { rudderProperty: 'lastName', emersysProperty: '2' }, + { rudderProperty: 'email', emersysProperty: '3' }, + { rudderProperty: 'optin', emersysProperty: '31' }, + ], + defaultContactList: 'dummyContactList', + }; + const expectedPayload = { + contact_list_id: 'dummyContactList', + contacts: [ + { + 1: 'John', + 2: 'Doe', + 3: 'john.doe@example.com', + 31: 1, + }, + ], + key_id: 3, + }; + + const result = buildIdentifyPayload(message, destination); + + expect(result.eventType).toBe(EVENT_TYPE.IDENTIFY); + expect(result.destinationPayload).toEqual(expectedPayload); + }); + + it('should throw error when opt-in field value is not allowed', () => { + const message = { + type: 'identify', + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + optin: 3, + }, + }; + const destination = { + fieldMapping: [ + { rudderProperty: 'firstName', emersysProperty: '1' }, + { rudderProperty: 'lastName', emersysProperty: '2' }, + { rudderProperty: 'email', emersysProperty: '3' }, + { rudderProperty: 'optin', emersysProperty: '31' }, + ], + defaultContactList: 'dummyList', + }; + expect(() => { + buildIdentifyPayload(message, destination); + }).toThrow('Only 1,2, values are allowed for optin field'); + }); + + it('should throw error when no contact list can be assigned field value is not allowed', () => { + const message = { + type: 'identify', + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + optin: 1, + }, + }; + const destination = { + fieldMapping: [ + { rudderProperty: 'firstName', emersysProperty: '1' }, + { rudderProperty: 'lastName', emersysProperty: '2' }, + { rudderProperty: 'email', emersysProperty: '3' }, + { rudderProperty: 'optin', emersysProperty: '31' }, + ], + }; + expect(() => { + buildIdentifyPayload(message, destination); + }).toThrow( + 'Cannot a find a specific contact list either through configuration or via integrations object', + ); + }); + + it('should correctly build payload with field mapping present in integrations object', () => { + const message = { + type: 'identify', + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + optin: 1, + }, + integrations: { + EMARSYS: { + customIdentifierId: 1, + contactListId: 'objectListId', + }, + }, + }; + const destination = { + fieldMapping: [ + { rudderProperty: 'firstName', emersysProperty: '1' }, + { rudderProperty: 'lastName', emersysProperty: '2' }, + { rudderProperty: 'email', emersysProperty: '3' }, + { rudderProperty: 'optin', emersysProperty: '31' }, + ], + defaultContactList: 'dummyContactList', + }; + const expectedPayload = { + contact_list_id: 'objectListId', + contacts: [ + { + 1: 'John', + 2: 'Doe', + 3: 'john.doe@example.com', + 31: 1, + }, + ], + key_id: 1, + }; + + const result = buildIdentifyPayload(message, destination); + + expect(result.eventType).toBe(EVENT_TYPE.IDENTIFY); + expect(result.destinationPayload).toEqual(expectedPayload); + }); + }); + + describe('buildGroupPayload', () => { + // Returns an object with eventType and destinationPayload keys when given valid message and destination inputs + it('should return an object with eventType and destinationPayload keys when given valid message and destination inputs with default externalId', () => { + const message = { + type: 'group', + groupId: 'group123', + context: { + traits: { + email: 'test@example.com', + }, + }, + }; + const destination = { + Config: { + emersysCustomIdentifier: '3', + defaultContactList: 'list123', + fieldMapping: [ + { emersysProperty: '100', rudderProperty: 'customId' }, + { emersysProperty: '3', rudderProperty: 'email' }, + ], + }, + }; + const result = buildGroupPayload(message, destination); + expect(result).toEqual({ + eventType: 'group', + destinationPayload: { + payload: { + key_id: 3, + external_ids: ['test@example.com'], + }, + contactListId: 'group123', + }, + }); + }); + + it('should return an object with eventType and destinationPayload keys when given valid message and destination inputs with configured externalId', () => { + const message = { + type: 'group', + groupId: 'group123', + context: { + traits: { + email: 'test@example.com', + customId: '123', + }, + }, + }; + const destination = { + emersysCustomIdentifier: '100', + defaultContactList: 'list123', + fieldMapping: [ + { emersysProperty: '100', rudderProperty: 'customId' }, + { emersysProperty: '3', rudderProperty: 'email' }, + ], + }; + const result = buildGroupPayload(message, destination); + expect(result).toEqual({ + eventType: 'group', + destinationPayload: { + payload: { + key_id: '100', + external_ids: ['123'], + }, + contactListId: 'group123', + }, + }); + }); + + it('should throw an InstrumentationError if emersysCustomIdentifier value is not present in payload', () => { + const message = { + type: 'group', + groupId: 'group123', + context: { + traits: { + email: 'test@example.com', + }, + }, + }; + const destination = { + emersysCustomIdentifier: 'customId', + defaultContactList: 'list123', + fieldMapping: [ + { emersysProperty: 'customId', rudderProperty: 'customId' }, + { emersysProperty: 'email', rudderProperty: 'email' }, + ], + }; + expect(() => { + buildGroupPayload(message, destination); + }).toThrow(InstrumentationError); + }); + }); + + describe('createGroupBatches', () => { + // Should group events by key_id and contactListId + it('should group events by key_id and contactListId when events are provided', () => { + // Arrange + const events = [ + { + message: [ + { + body: { + JSON: { + destinationPayload: { + payload: { + key_id: 'key1', + external_ids: ['id1', 'id2'], + }, + contactListId: 'list1', + }, + }, + }, + }, + ], + metadata: { jobId: 1, userId: 'u1' }, + }, + { + message: [ + { + body: { + JSON: { + destinationPayload: { + payload: { + key_id: 'key2', + external_ids: ['id3', 'id4'], + }, + contactListId: 'list2', + }, + }, + }, + }, + ], + metadata: { jobId: 2, userId: 'u2' }, + }, + { + message: [ + { + body: { + JSON: { + destinationPayload: { + payload: { + key_id: 'key1', + external_ids: ['id5', 'id6'], + }, + contactListId: 'list1', + }, + }, + }, + }, + ], + metadata: { jobId: 3, userId: 'u3' }, + }, + ]; + + // Act + const result = createGroupBatches(events); + + // Assert + expect(result).toEqual([ + { + endpoint: 'https://api.emarsys.net/api/v2/contactlist/list1/add', + payload: { + key_id: 'key1', + external_ids: ['id1', 'id2', 'id5', 'id6'], + }, + metadata: [ + { jobId: 1, userId: 'u1' }, + { jobId: 3, userId: 'u3' }, + ], + }, + { + endpoint: 'https://api.emarsys.net/api/v2/contactlist/list2/add', + payload: { + key_id: 'key2', + external_ids: ['id3', 'id4'], + }, + metadata: [{ jobId: 2, userId: 'u2' }], + }, + ]); + }); + + // Should return an empty array if no events are provided + it('should return an empty array when no events are provided', () => { + // Arrange + const events = []; + + // Act + const result = createGroupBatches(events); + + // Assert + expect(result).toEqual([]); + }); + }); + + describe('findRudderPropertyByEmersysProperty', () => { + // Returns the correct rudderProperty when given a valid emersysProperty and fieldMapping + it('should return the correct rudderProperty when given a valid emersysProperty and fieldMapping', () => { + const emersysProperty = 'firstName'; + const fieldMapping = [ + { emersysProperty: 'email', rudderProperty: 'email' }, + { emersysProperty: 'firstName', rudderProperty: 'firstName' }, + { emersysProperty: 'lastName', rudderProperty: 'lastName' }, + ]; + + const result = findRudderPropertyByEmersysProperty(emersysProperty, fieldMapping); + + expect(result).toBe('firstName'); + }); + + // Returns null when given an empty fieldMapping + it('should return null when given an empty fieldMapping', () => { + const emersysProperty = 'email'; + const fieldMapping = []; + + const result = findRudderPropertyByEmersysProperty(emersysProperty, fieldMapping); + + expect(result).toBe('email'); + }); + }); + + describe('checkIfEventIsAbortableAndExtractErrorMessage', () => { + // Returns {isAbortable: false, errorMsg: ''} if event is neither a string nor an object with keyId. + it('should return {isAbortable: false, errorMsg: ""} when event is neither a string nor an object with keyId', () => { + const event = 123; + const destinationResponse = { + response: { + data: { + errors: { + errorKey: { + errorCode: 'errorMessage', + }, + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + keyId, + ); + + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Returns {isAbortable: false, errorMsg: ''} if errors object is empty. + it('should return {isAbortable: false, errorMsg: ""} when errors object is empty', () => { + const event = 'event'; + const destinationResponse = { + response: { + data: { + errors: {}, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + keyId, + ); + + expect(result).toEqual({ isAbortable: false, errorMsg: '' }); + }); + + // Returns {isAbortable: true, errorMsg} if event is a string and has a corresponding error in the errors object. + it('should return {isAbortable: true, errorMsg} when event is a string and has a corresponding error in the errors object', () => { + const event = 'event'; + const destinationResponse = { + response: { + data: { + errors: { + event: { + errorCode: 'errorMessage', + }, + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + keyId, + ); + + expect(result).toEqual({ isAbortable: true, errorMsg: '{"errorCode":"errorMessage"}' }); + }); + + // Returns {isAbortable: true, errorMsg} if event is an object with keyId and has a corresponding error in the errors object. + it('should return {isAbortable: true, errorMsg} when event is an object with keyId and has a corresponding error in the errors object', () => { + const event = { + keyId: 'event', + }; + const destinationResponse = { + response: { + data: { + errors: { + event: { + errorCode: 'errorMessage', + }, + }, + }, + }, + }; + const keyId = 'keyId'; + + const result = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + keyId, + ); + + expect(result).toEqual({ isAbortable: true, errorMsg: '{"errorCode":"errorMessage"}' }); + }); + }); + + describe('deduceEventId', () => { + // When a valid event name is provided and there is a mapping for it, the function should return the corresponding eventId. + it('should return the corresponding eventId when a valid event name is provided and there is a mapping for it', () => { + const message = { event: 'validEvent' }; + const destConfig = { eventsMapping: [{ from: 'validEvent', to: 'eventId' }] }; + const result = deduceEventId(message, destConfig); + expect(result).toBe('eventId'); + }); + + // When an invalid event name is provided, the function should throw a ConfigurationError. + it('should throw a ConfigurationError when an invalid event name is provided', () => { + const message = { event: 'invalidEvent' }; + const destConfig = { eventsMapping: [{ from: 'validEvent', to: 'eventId' }] }; + expect(() => deduceEventId(message, destConfig)).toThrow(ConfigurationError); + }); + + // When a valid event name is provided and there is no mapping for it, the function should throw a ConfigurationError. + it('should throw a ConfigurationError when a valid event name is provided and there is no mapping for it', () => { + const message = { event: 'validEvent' }; + const destConfig = { eventsMapping: [] }; + expect(() => deduceEventId(message, destConfig)).toThrow(ConfigurationError); + }); + + // When eventsMapping is not an array, the function should throw a TypeError. + it('should throw a TypeError when eventsMapping is not an array', () => { + const message = { event: 'validEvent' }; + const destConfig = { eventsMapping: 'notAnArray' }; + expect(() => deduceEventId(message, destConfig)).toThrow( + 'validEvent is not mapped to any Emersys external event. Aborting', + ); + }); + }); +}); diff --git a/src/cdk/v2/destinations/fullstory/procWorkflow.yaml b/src/cdk/v2/destinations/fullstory/procWorkflow.yaml index 1a54e8688c..26da955623 100644 --- a/src/cdk/v2/destinations/fullstory/procWorkflow.yaml +++ b/src/cdk/v2/destinations/fullstory/procWorkflow.yaml @@ -76,7 +76,7 @@ steps: "use_most_recent": .message.properties.useMostRecent, }; $.context.payload.user = { - "id": .message.properties.userId ?? .message.userId, + "uid": .message.properties.userId ?? .message.userId, } - name: cleanPayload diff --git a/src/cdk/v2/destinations/gladly/procWorkflow.yaml b/src/cdk/v2/destinations/gladly/procWorkflow.yaml index a53a0ca8f5..dcefc9d774 100644 --- a/src/cdk/v2/destinations/gladly/procWorkflow.yaml +++ b/src/cdk/v2/destinations/gladly/procWorkflow.yaml @@ -61,7 +61,8 @@ steps: headers: $.getHeaders(.destination) } const endpoint = $.getEndpoint(.destination) + "?" + $.getQueryParams($.context.payload); - const rawResponse = await $.httpGET(endpoint,requestOptions) + const reqStats = {metadata:.metadata, module: 'router',feature: "transformation", destType:"gladly",requestMethod:"get",endpointPath:"/api/v1/customer-profiles"} + const rawResponse = await $.httpGET(endpoint,requestOptions, reqStats) const processedResponse = $.processAxiosResponse(rawResponse) processedResponse diff --git a/src/cdk/v2/destinations/heap/procWorkflow.yaml b/src/cdk/v2/destinations/heap/procWorkflow.yaml index 8326a61a79..ac12e7e02a 100644 --- a/src/cdk/v2/destinations/heap/procWorkflow.yaml +++ b/src/cdk/v2/destinations/heap/procWorkflow.yaml @@ -40,7 +40,9 @@ steps: }); .message.properties.idempotencyKey ? ($.context.payload.idempotency_key = .message.properties.idempotencyKey); - + - name: validateuserId + template: | + $.assert($.context.payload.identity, "userId is required"); - name: finalPayload description: In batchMode we return payload directly condition: $.batchMode diff --git a/src/cdk/v2/destinations/intercom/config.js b/src/cdk/v2/destinations/intercom/config.js index 518d805d41..c3b806f9f9 100644 --- a/src/cdk/v2/destinations/intercom/config.js +++ b/src/cdk/v2/destinations/intercom/config.js @@ -4,6 +4,7 @@ const BASE_AU_ENDPOINT = 'https://api.au.intercom.io'; const SEARCH_CONTACT_ENDPOINT = 'contacts/search'; const CREATE_OR_UPDATE_COMPANY_ENDPOINT = 'companies'; +const TAGS_ENDPOINT = 'tags'; const ReservedAttributes = { v1UserAttributes: [ @@ -78,4 +79,5 @@ module.exports = { SEARCH_CONTACT_ENDPOINT, ReservedCompanyProperties, CREATE_OR_UPDATE_COMPANY_ENDPOINT, + TAGS_ENDPOINT, }; diff --git a/src/cdk/v2/destinations/intercom/procWorkflow.yaml b/src/cdk/v2/destinations/intercom/procWorkflow.yaml index 0a8842d5e7..008b22ace5 100644 --- a/src/cdk/v2/destinations/intercom/procWorkflow.yaml +++ b/src/cdk/v2/destinations/intercom/procWorkflow.yaml @@ -16,6 +16,8 @@ bindings: - name: addExternalIdToTraits path: ../../../../v0/util - path: ../../bindings/jsontemplate + - name: HTTP_STATUS_CODES + path: ../../../../v0/util/constant steps: - name: checkIfProcessed @@ -182,10 +184,17 @@ steps: id: companyId, }; $.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "contacts" + "/" + contactId + "/" + "companies"; + const payload = $.context.payload; + const endpoint = $.context.endpoint; + await $.attachContactToCompany(payload, endpoint, .destination); + await $.addOrUpdateTagsToCompany(.message, .destination, companyId); else: name: whenSearchContactNotFound template: | + const companyId = await $.createOrUpdateCompany($.context.payload, .destination); + $.assert(companyId, "Unable to create or update company"); $.context.endpoint = $.getBaseEndpoint(.destination) + "/" + "companies"; + await $.addOrUpdateTagsToCompany(.message, .destination, companyId); - name: prepareFinalPayload template: | $.context.requestMethod = 'POST'; @@ -208,9 +217,25 @@ steps: response.method = "POST"; response.userId = .message.anonymousId; $.context.response.push(response); + payload = response.body.JSON; + const companyId = await $.createOrUpdateCompany(payload, .destination); + $.assert(companyId, "Unable to create or update company"); const attachUserAndCompanyResponse = $.attachUserAndCompany(.message, .destination.Config); attachUserAndCompanyResponse ? attachUserAndCompanyResponse.userId = .message.anonymousId; attachUserAndCompanyResponse ? $.context.response.push(attachUserAndCompanyResponse); + payload = attachUserAndCompanyResponse.body.JSON; + let endpoint = attachUserAndCompanyResponse.endpoint; + attachUserAndCompanyResponse ? await $.attachContactToCompany(payload, endpoint, .destination); + await $.addOrUpdateTagsToCompany(.message, .destination, companyId); + + - name: statusCode + condition: $.outputs.messageType === {{$.EventType.GROUP}} + template: | + $.HTTP_STATUS_CODES.SUPPRESS_EVENTS + else: + name: successStatusCode + template: | + $.HTTP_STATUS_CODES.OK - name: buildResponseForProcessTransformation description: Build response for multiple transformed event diff --git a/src/cdk/v2/destinations/intercom/rtWorkflow.yaml b/src/cdk/v2/destinations/intercom/rtWorkflow.yaml index edb7267b84..65c3a4976d 100644 --- a/src/cdk/v2/destinations/intercom/rtWorkflow.yaml +++ b/src/cdk/v2/destinations/intercom/rtWorkflow.yaml @@ -21,7 +21,7 @@ steps: "batched": false, "destination": ^[idx].destination, "metadata": ^[idx].metadata[], - "statusCode": 200 + "statusCode": .outputs.statusCode })[] - name: failedEvents template: | diff --git a/src/cdk/v2/destinations/intercom/utils.js b/src/cdk/v2/destinations/intercom/utils.js index ba3063c9f9..7e609d5c96 100644 --- a/src/cdk/v2/destinations/intercom/utils.js +++ b/src/cdk/v2/destinations/intercom/utils.js @@ -1,6 +1,10 @@ const md5 = require('md5'); const get = require('get-value'); -const { NetworkError } = require('@rudderstack/integrations-lib'); +const { + NetworkError, + ConfigurationError, + InstrumentationError, +} = require('@rudderstack/integrations-lib'); const tags = require('../../../../v0/util/tags'); const { httpPOST } = require('../../../../adapters/network'); const { @@ -27,8 +31,42 @@ const { SEARCH_CONTACT_ENDPOINT, ReservedCompanyProperties, CREATE_OR_UPDATE_COMPANY_ENDPOINT, + TAGS_ENDPOINT, } = require('./config'); +/** + * method to handle error during api call + * ref docs: https://developers.intercom.com/docs/references/rest-api/errors/error-codes/ + * https://developers.intercom.com/docs/references/rest-api/errors/error-objects/ + * https://developers.intercom.com/docs/references/rest-api/errors/http-responses/ + * e.g. + * 400 - code: parameter_not_found (or parameter_invalid), message: company not specified + * 401 - code: unauthorized, message: Access Token Invalid + * 404 - code: company_not_found, message: Company Not Found + * @param {*} message + * @param {*} processedResponse + */ +const intercomErrorHandler = (message, processedResponse) => { + const errorMessages = JSON.stringify(processedResponse.response); + if (processedResponse.status === 400) { + throw new InstrumentationError(`${message} : ${errorMessages}`); + } + if (processedResponse.status === 401) { + throw new ConfigurationError(`${message} : ${errorMessages}`); + } + if (processedResponse.status === 404) { + throw new InstrumentationError(`${message} : ${errorMessages}`); + } + throw new NetworkError( + `${message} : ${errorMessages}`, + processedResponse.status, + { + [tags]: getDynamicErrorType(processedResponse.status), + }, + processedResponse, + ); +}; + /** * Returns destination request headers * @param {*} destination @@ -285,7 +323,8 @@ const searchContact = async (message, destination) => { * @returns */ const createOrUpdateCompany = async (payload, destination) => { - const headers = getHeaders(destination); + const { apiVersion } = destination.Config; + const headers = getHeaders(destination, apiVersion); const finalPayload = JSON.stringify(removeUndefinedAndNullValues(payload)); const baseEndPoint = getBaseEndpoint(destination); const endpoint = `${baseEndPoint}/${CREATE_OR_UPDATE_COMPANY_ENDPOINT}`; @@ -369,6 +408,99 @@ const addMetadataToPayload = (payload) => { return finalPayload; }; +/** + * Api call to attach user to the company + * Ref doc v1: https://developers.intercom.com/docs/references/1.4/rest-api/users/companies-and-users/ + * Ref doc v2: https://developers.intercom.com/docs/references/2.10/rest-api/api.intercom.io/Contacts/attachContactToACompany/ + * @param {*} payload + * @param {*} endpoint + * @param {*} destination + */ +const attachContactToCompany = async (payload, endpoint, destination) => { + let { apiVersion } = destination.Config; + apiVersion = isDefinedAndNotNull(apiVersion) ? apiVersion : 'v2'; + let endpointPath = '/contact/{id}/companies'; + if (apiVersion === 'v1') { + endpointPath = '/users'; + } + const commonStatTags = { + destType: 'intercom', + feature: 'transformation', + requestMethod: 'POST', + module: 'router', + }; + const headers = getHeaders(destination, apiVersion); + const finalPayload = JSON.stringify(removeUndefinedAndNullValues(payload)); + const response = await httpPOST( + endpoint, + finalPayload, + { + headers, + }, + { + ...commonStatTags, + endpointPath, + }, + ); + + const processedResponse = processAxiosResponse(response); + if (!isHttpStatusSuccess(processedResponse.status)) { + intercomErrorHandler('Unable to attach Contact or User to Company due to', processedResponse); + } +}; + +/** + * Api calls to add or update tags to the company + * Ref doc v1: https://developers.intercom.com/docs/references/1.4/rest-api/tags/tag-or-untag-users-companies-leads-contacts/ + * Ref doc v2: https://developers.intercom.com/docs/references/2.10/rest-api/api.intercom.io/Tags/createTag/ + * @param message + * @param destination + * @param id + * @returns + */ +const addOrUpdateTagsToCompany = async (message, destination, id) => { + const companyTags = message?.context?.traits?.tags; + if (!companyTags) return; + const { apiVersion } = destination.Config; + const headers = getHeaders(destination, apiVersion); + const baseEndPoint = getBaseEndpoint(destination); + const endpoint = `${baseEndPoint}/${TAGS_ENDPOINT}`; + const statTags = { + destType: 'intercom', + feature: 'transformation', + endpointPath: '/tags', + requestMethod: 'POST', + module: 'router', + }; + await Promise.all( + companyTags.map(async (tag) => { + const finalPayload = { + name: tag, + companies: [ + { + id, + }, + ], + }; + const response = await httpPOST( + endpoint, + finalPayload, + { + headers, + }, + statTags, + ); + const processedResponse = processAxiosResponse(response); + if (!isHttpStatusSuccess(processedResponse.status)) { + intercomErrorHandler( + 'Unable to Add or Update the Tag to Company due to', + processedResponse, + ); + } + }), + ); +}; + module.exports = { getName, getHeaders, @@ -382,4 +514,6 @@ module.exports = { filterCustomAttributes, checkIfEmailOrUserIdPresent, separateReservedAndRestMetadata, + attachContactToCompany, + addOrUpdateTagsToCompany, }; diff --git a/src/cdk/v2/destinations/intercom/utils.test.js b/src/cdk/v2/destinations/intercom/utils.test.js index e651b4ea5d..2f7dac2583 100644 --- a/src/cdk/v2/destinations/intercom/utils.test.js +++ b/src/cdk/v2/destinations/intercom/utils.test.js @@ -13,6 +13,8 @@ const { filterCustomAttributes, checkIfEmailOrUserIdPresent, separateReservedAndRestMetadata, + attachContactToCompany, + addOrUpdateTagsToCompany, } = require('./utils'); const { BASE_ENDPOINT, BASE_EU_ENDPOINT, BASE_AU_ENDPOINT } = require('./config'); @@ -763,3 +765,263 @@ describe('attachUserAndCompany utility test', () => { expect(response).toEqual(expectedResponse); }); }); + +describe('attachContactToCompany utility test', () => { + it('Should successfully attach contact to company for apiVersion v2', async () => { + const payload = { + id: 'company123', + }; + const endpoint = 'https://api.intercom.io/contacts/contact123/companies'; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us', apiVersion: 'v2' } }; + + axios.post.mockResolvedValue({ + status: 200, + data: { + type: 'company', + id: 'contact123', + company_id: 'company123', + }, + }); + + await attachContactToCompany(payload, endpoint, destination); + + expect(axios.post).toHaveBeenCalledWith( + endpoint, + JSON.stringify(payload), + expect.objectContaining({ + headers: getHeaders(destination, 'v2'), + }), + ); + }); + + it('Should successfully attach contact to company for apiVersion v1', async () => { + const payload = { + user_id: 'user123', + companies: [ + { + company_id: 'company123', + name: 'Company', + }, + ], + }; + const endpoint = 'https://api.intercom.io/users'; + const destination = { Config: { apiKey: 'testApiKey', apiVersion: 'v1' } }; + + axios.post.mockResolvedValue({ + status: 200, + data: { + id: 'contact123', + user_id: 'user123', + companies: { + type: 'companies.list', + companies: [ + { + type: 'company', + company_id: 'company123', + id: '123', + name: 'Company', + }, + ], + }, + }, + }); + + await attachContactToCompany(payload, endpoint, destination); + + expect(axios.post).toHaveBeenCalledWith( + endpoint, + JSON.stringify(payload), + expect.objectContaining({ + headers: getHeaders(destination, 'v1'), + }), + ); + }); + + it('Should throw error for invalid company during attachment', async () => { + const payload = { + id: 'company123', + }; + const endpoint = 'https://api.intercom.io/contacts/contact123/companies'; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us', apiVersion: 'v2' } }; + + axios.post.mockRejectedValue({ + response: { + status: 404, + data: { + type: 'error.list', + request_id: '123', + errors: [ + { + code: 'company_not_found', + message: 'Company Not Found', + }, + ], + }, + }, + }); + + try { + await attachContactToCompany(payload, endpoint, destination); + } catch (error) { + expect(error.message).toEqual( + 'Unable to attach Contact or User to Company due to : {"type":"error.list","request_id":"123","errors":[{"code":"company_not_found","message":"Company Not Found"}]}', + ); + } + }); + + it('Should throw error for faulty payload during attachment', async () => { + const payload = {}; + const endpoint = 'https://api.intercom.io/contacts/contact123/companies'; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us', apiVersion: 'v2' } }; + + axios.post.mockRejectedValue({ + response: { + status: 400, + data: { + type: 'error.list', + request_id: '123', + errors: [ + { + code: 'parameter_not_found', + message: 'company not specified', + }, + ], + }, + }, + }); + + try { + await attachContactToCompany(payload, endpoint, destination); + } catch (error) { + expect(error.message).toEqual( + 'Unable to attach Contact or User to Company due to : {"type":"error.list","request_id":"123","errors":[{"code":"parameter_not_found","message":"company not specified"}]}', + ); + } + }); +}); + +describe('addOrUpdateTagsToCompany utility test', () => { + it('Should successfully add tags to company', async () => { + const message = { + context: { + traits: { + tags: ['tag1', 'tag2'], + }, + }, + }; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us' } }; + const id = 'companyId'; + + axios.post + .mockResolvedValueOnce({ + status: 200, + data: { type: 'tag', id: '123', name: 'tag1' }, + }) + .mockResolvedValueOnce({ + status: 200, + data: { type: 'tag', id: '124', name: 'tag2' }, + }); + + axios.post.mockClear(); + await addOrUpdateTagsToCompany(message, destination, id); + + expect(axios.post).toHaveBeenCalledTimes(2); + + expect(axios.post).toHaveBeenCalledWith( + `${getBaseEndpoint(destination)}/tags`, + { name: 'tag1', companies: [{ id: 'companyId' }] }, + expect.objectContaining({ + headers: getHeaders(destination), + }), + ); + + expect(axios.post).toHaveBeenCalledWith( + `${getBaseEndpoint(destination)}/tags`, + { name: 'tag2', companies: [{ id: 'companyId' }] }, + expect.objectContaining({ + headers: getHeaders(destination), + }), + ); + }); + + it('Should throw an error in case if axios calls returns an error', async () => { + const message = { + context: { + traits: { + tags: ['tag1'], + }, + }, + }; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us' } }; + const id = 'companyId'; + + axios.post.mockRejectedValue({ + status: 401, + data: { + type: 'error.list', + request_id: 'request_401', + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + }, + }); + + try { + axios.post.mockClear(); + await addOrUpdateTagsToCompany(message, destination, id); + } catch (error) { + expect(error.message).toEqual( + `Unable to Add or Update the Tag to Company due to : {"type":"error.list","request_id":"request_401","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]}`, + ); + } + }); + + it('Should throw a network error in case if axios calls returns an error', async () => { + const message = { + context: { + traits: { + tags: ['tag1'], + }, + }, + }; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us' } }; + const id = 'companyId'; + + axios.post.mockRejectedValue({ + status: 429, + data: { + type: 'error.list', + request_id: 'request_429', + errors: [ + { + code: 'rate_limit_exceeded', + message: 'You have exceeded the rate limit. Please try again later.', + }, + ], + }, + }); + + try { + axios.post.mockClear(); + await addOrUpdateTagsToCompany(message, destination, id); + } catch (error) { + expect(error.message).toEqual( + `Unable to Add or Update the Tag to Company due to : {"type":"error.list","request_id":"request_429","errors":[{"code":"rate_limit_exceeded","message":"You have exceeded the rate limit. Please try again later."}]}`, + ); + } + }); + + it('Should do nothing when no tags are provided', async () => { + const message = { traits: {} }; + const destination = { Config: { apiKey: 'testApiKey', apiServer: 'us' } }; + const id = 'companyId'; + + axios.post.mockClear(); + await addOrUpdateTagsToCompany(message, destination, id); + + expect(axios.post).not.toHaveBeenCalled(); + }); +}); diff --git a/src/cdk/v2/destinations/klaviyo_bulk_upload/config.js b/src/cdk/v2/destinations/klaviyo_bulk_upload/config.js new file mode 100644 index 0000000000..43a904499b --- /dev/null +++ b/src/cdk/v2/destinations/klaviyo_bulk_upload/config.js @@ -0,0 +1,5 @@ +const KLAVIYO_BULK_UPLOAD = 'klaviyo_bulk_upload'; + +module.exports = { + KLAVIYO_BULK_UPLOAD, +}; diff --git a/src/cdk/v2/destinations/klaviyo_bulk_upload/procWorkflow.yaml b/src/cdk/v2/destinations/klaviyo_bulk_upload/procWorkflow.yaml new file mode 100644 index 0000000000..44290ff520 --- /dev/null +++ b/src/cdk/v2/destinations/klaviyo_bulk_upload/procWorkflow.yaml @@ -0,0 +1,35 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - path: ./utils + - path: ../../bindings/jsontemplate + exportAll: true + - path: ./config + +steps: + - name: messageType + template: | + .message.type.toLowerCase(); + - name: validateInput + template: | + let messageType = .message.type; + $.assert(messageType, "message Type is not present. Aborting"); + $.assert(.message.type.toLowerCase() ==='identify', "Event type " + .message.type.toLowerCase() + " is not supported. Aborting message."); + $.assertConfig(.destination.Config.privateApiKey, "Private Api Key is not present. Aborting"); + + - name: generatePayload + template: | + const transformedPayload = $.combinePayloads(^.{.message.type === $.EventType.IDENTIFY}[], ^[0].destination) + transformedPayload + + - name: buildResponseForProcessTransformation + description: build response + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.outputs.generatePayload + response diff --git a/src/cdk/v2/destinations/klaviyo_bulk_upload/utils.js b/src/cdk/v2/destinations/klaviyo_bulk_upload/utils.js new file mode 100644 index 0000000000..c068c4762c --- /dev/null +++ b/src/cdk/v2/destinations/klaviyo_bulk_upload/utils.js @@ -0,0 +1,145 @@ +/* eslint-disable no-param-reassign */ +const { removeUndefinedValues } = require('../../../../v0/util'); + +const locationAttributes = [ + 'address1', + 'address2', + 'city', + 'country', + 'latitude', + 'longitude', + 'region', + 'zip', + 'ip', +]; + +const standardTraits = [ + 'email', + 'phone_number', + 'external_id', + 'anonymous_id', + '_kx', + 'first_name', + 'last_name', + 'organization', + 'title', + 'image', +]; + +function setStandardAndCustomTraits(traits) { + const standardAttributes = {}; + const customAttributes = {}; + return Object.keys(traits).reduce( + (result, key) => { + if (!locationAttributes.includes(key) && standardTraits.includes(key)) { + result.standardAttributes[key] = traits[key]; + } else if (!locationAttributes.includes(key)) { + result.customAttributes[key] = traits[key]; + } + return result; + }, + { standardAttributes, customAttributes }, + ); +} + +function generateLocationObject({ + traits: { address1, address2, city, country, latitude, longitude, region, zip, ip }, +}) { + const locationObject = { + address1, + address2, + city, + country, + latitude, + longitude, + region, + zip, + ip, + }; + + return removeUndefinedValues(locationObject); +} + +function transformSingleMessage(data, metadata) { + const { context, traits } = data; + const location = generateLocationObject(data); + const { jobId } = metadata; + const { standardAttributes, customAttributes } = setStandardAndCustomTraits(traits); + const transformedSinglePayload = { + type: 'profile', + attributes: { + ...standardAttributes, + location, + properties: customAttributes, + anonymous_id: context.externalId[0].id, + jobIdentifier: `${context.externalId[0].id}:${jobId}`, + }, + }; + if (context.externalId[0].identifierType === 'id') { + transformedSinglePayload.id = context.externalId[0].id || traits.id; + transformedSinglePayload.attributes.anonymous_id = context.externalId[0].id; + } else if (context.externalId[0].identifierType === 'email') { + transformedSinglePayload.attributes.email = context.externalId[0].id; + } + if (Object.keys(transformedSinglePayload.attributes.location).length === 0) { + delete transformedSinglePayload.attributes.location; + } + if (Object.keys(transformedSinglePayload.attributes.properties).length === 0) { + delete transformedSinglePayload.attributes.properties; + } + return removeUndefinedValues(transformedSinglePayload); +} + +function wrapCombinePayloads(transformedInputs, destinationObj) { + if (destinationObj.Config.listId) { + return { + payload: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: transformedInputs, + }, + }, + relationships: { + lists: { + data: [ + { + type: 'list', + id: destinationObj.Config.listId, + }, + ], + }, + }, + }, + }, + destination: destinationObj, + }; + } + return { + payload: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: transformedInputs, + }, + }, + }, + }, + destination: destinationObj, + }; +} + +function combinePayloads(inputs) { + const transformedInputs = inputs.map((input) => { + const { message, metadata } = input; + return transformSingleMessage(message, metadata); + }); + const destinationObj = inputs[inputs.length - 1].destination; + + const { payload } = wrapCombinePayloads(transformedInputs, destinationObj); + return { ...payload }; +} + +module.exports = { transformSingleMessage, combinePayloads }; diff --git a/src/cdk/v2/destinations/koala/procWorkflow.yaml b/src/cdk/v2/destinations/koala/procWorkflow.yaml new file mode 100644 index 0000000000..9ec0202b13 --- /dev/null +++ b/src/cdk/v2/destinations/koala/procWorkflow.yaml @@ -0,0 +1,65 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + +steps: + - name: validateInput + template: | + $.assert(.message.type, "message Type is not present. Aborting message"); + $.assert(.message.type in {{$.EventType.([.IDENTIFY, .TRACK])}}, + "message type " + .message.type + " is not supported"); + $.assertConfig(.destination.Config.publicKey, "publicKey is not present. Aborting message"); + $.context.email = .message.().({{{{$.getGenericPaths("emailOnly")}}}}); + $.context.ko_profile_id = .message.traits.ko_profile_id ?? .message.context.traits.ko_profile_id ?? .message.properties.ko_profile_id; + $.assert($.context.email || $.context.ko_profile_id, "Neither email or ko_profile_id are present on traits. Aborting message"); + - name: setMessageType + template: | + $.context.messageType = .message.type.toLowerCase(); + - name: preparePayloadForIdentify + condition: $.context.messageType === {{$.EventType.IDENTIFY}} + template: | + const traits = .message.traits ?? .message.context.traits ?? {}; + const koTraits = traits{~['ko_profile_id']} + const basePayload = { + email: $.context.email, + profile_id: $.context.ko_profile_id, + identifies: [{ + type: $.context.messageType, + sent_at: .message.().({{{{$.getGenericPaths("timestamp")}}}}), + traits: koTraits + }] + }; + + $.context.payload = basePayload + - name: preparePayloadForTrack + condition: $.context.messageType === {{$.EventType.TRACK}} + template: | + const properties = .message.properties ?? {}; + const koProperties = properties{~['ko_profile_id']} + const basePayload = { + ip: .message.context.ip ?? .message.request_ip, + email: $.context.email, + profile_id: $.context.ko_profile_id, + events: [{ + type: $.context.messageType, + event: .message.event, + message_id: .message.messageId, + sent_at: .message.().({{{{$.getGenericPaths("timestamp")}}}}), + properties: koProperties, + context: .message.context + }] + }; + + $.context.payload = basePayload + - name: buildResponseForProcessTransformation + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = "https://api2.getkoala.com/web/projects/" + .destination.Config.publicKey + "/batch"; + response.headers = { + "content-type": "application/json" + }; + response diff --git a/src/cdk/v2/destinations/koala/rtWorkflow.yaml b/src/cdk/v2/destinations/koala/rtWorkflow.yaml new file mode 100644 index 0000000000..335293b6db --- /dev/null +++ b/src/cdk/v2/destinations/koala/rtWorkflow.yaml @@ -0,0 +1,31 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata[], + "statusCode": 200 + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.outputs.successfulEvents] diff --git a/src/cdk/v2/destinations/koddi/config.js b/src/cdk/v2/destinations/koddi/config.js new file mode 100644 index 0000000000..927e1858fc --- /dev/null +++ b/src/cdk/v2/destinations/koddi/config.js @@ -0,0 +1,39 @@ +const { getMappingConfig } = require('../../../../v0/util'); + +/** + * ref :- https://developers.koddi.com/reference/winning-ads + * impressions - https://developers.koddi.com/reference/impressions-1 + * clicks - https://developers.koddi.com/reference/clicks-1 + * conversions - https://developers.koddi.com/reference/conversions-1 + */ +const EVENT_TYPES = { + IMPRESSIONS: 'impressions', + CLICKS: 'clicks', + CONVERSIONS: 'conversions', +}; + +const CONFIG_CATEGORIES = { + IMPRESSIONS: { + type: 'track', + name: 'ImpressionsConfig', + }, + CLICKS: { + type: 'track', + name: 'ClicksConfig', + }, + CONVERSIONS: { + type: 'track', + name: 'ConversionsConfig', + }, +}; + +const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); + +module.exports = { + EVENT_TYPES, + CONFIG_CATEGORIES, + MAPPING_CONFIG, + IMPRESSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.IMPRESSIONS.name], + CLICKS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.CLICKS.name], + CONVERSIONS_CONFIG: MAPPING_CONFIG[CONFIG_CATEGORIES.CONVERSIONS.name], +}; diff --git a/src/cdk/v2/destinations/koddi/data/ClicksConfig.json b/src/cdk/v2/destinations/koddi/data/ClicksConfig.json new file mode 100644 index 0000000000..96ab27b2ae --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ClicksConfig.json @@ -0,0 +1,35 @@ +[ + { + "sourceKeys": "properties.tracking_data", + "required": true, + "destKey": "trackingData" + }, + { + "sourceKeys": "properties.rank", + "required": true, + "destKey": "rank" + }, + { + "sourceKeys": "properties.beacon_issued", + "required": true, + "destKey": "beaconIssued" + }, + { + "sourceKeys": "userId", + "sourceFromGenericMap": true, + "required": true, + "destKey": "userGuid" + }, + { + "sourceKeys": "properties.test_version_override", + "destKey": "testVersionOverride" + }, + { + "sourceKeys": "properties.destination_url", + "destKey": "destinationUrl" + }, + { + "sourceKeys": "properties.overrides", + "destKey": "overrides" + } +] diff --git a/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json new file mode 100644 index 0000000000..495574f198 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ConversionsConfig.json @@ -0,0 +1,53 @@ +[ + { + "sourceKeys": "context.page.referring_domain", + "destKey": "domain" + }, + { + "sourceKeys": "context.locale", + "required": true, + "destKey": "culture" + }, + { + "sourceKeys": "properties.currency", + "required": true, + "destKey": "currency" + }, + { + "sourceKeys": ["context.ip", "request_ip"], + "destKey": "user_ip" + }, + { + "sourceKeys": "context.userAgent", + "destKey": "user_agent" + }, + { + "sourceKeys": "userId", + "sourceFromGenericMap": true, + "required": true, + "destKey": "user_guid" + }, + { + "sourceKeys": "context.device.type", + "destKey": "device_type" + }, + { + "sourceKeys": ["properties.order_id", "properties.transaction_id"], + "required": true, + "destKey": "transaction_id" + }, + { + "sourceKeys": "properties.conversion_source", + "destKey": "conversion_source" + }, + { + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "destKey": "unixtime" + }, + { + "sourceKeys": "properties.bidders", + "required": true, + "destKey": "bidders" + } +] diff --git a/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json b/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json new file mode 100644 index 0000000000..de53703b32 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/data/ImpressionsConfig.json @@ -0,0 +1,22 @@ +[ + { + "sourceKeys": "properties.tracking_data", + "required": true, + "destKey": "trackingData" + }, + { + "sourceKeys": "properties.rank", + "required": true, + "destKey": "rank" + }, + { + "sourceKeys": "properties.beacon_issued", + "required": true, + "destKey": "beaconIssued" + }, + { + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "destKey": "ts" + } +] diff --git a/src/cdk/v2/destinations/koddi/procWorkflow.yaml b/src/cdk/v2/destinations/koddi/procWorkflow.yaml new file mode 100644 index 0000000000..cc3f0166dc --- /dev/null +++ b/src/cdk/v2/destinations/koddi/procWorkflow.yaml @@ -0,0 +1,33 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - path: ./utils + - path: ./config + +steps: + - name: messageType + template: | + .message.type.toLowerCase(); + - name: eventType + template: | + .message.integrations.koddi.eventType.toLowerCase(); + - name: validateInput + template: | + let messageType = $.outputs.messageType; + let eventType = $.outputs.eventType; + $.assert(messageType, "message Type is not present. Aborting message."); + $.assert(messageType in {{$.EventType.([.TRACK])}}, "message type " + messageType + " is not supported"); + $.assert(eventType in {{$.EVENT_TYPES.([.IMPRESSIONS, .CLICKS, .CONVERSIONS])}}, "event type " + eventType + " is not supported"); + $.assertConfig(.destination.Config.apiBaseUrl, "API Base URL is not present. Aborting"); + $.assertConfig(.destination.Config.clientName, "Client Name is not present. Aborting"); + - name: preparePayload + template: | + const payload = $.constructFullPayload($.outputs.eventType, .message, .destination.Config); + $.context.payload = $.removeUndefinedAndNullValues(payload); + - name: buildResponse + template: | + const response = $.constructResponse($.outputs.eventType, .destination.Config, $.context.payload); + response diff --git a/src/cdk/v2/destinations/koddi/rtWorkflow.yaml b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml new file mode 100644 index 0000000000..dd438a911c --- /dev/null +++ b/src/cdk/v2/destinations/koddi/rtWorkflow.yaml @@ -0,0 +1,31 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata[], + "statusCode": 200 + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + - name: finalPayload + template: | + [...$.outputs.successfulEvents, ...$.outputs.failedEvents] diff --git a/src/cdk/v2/destinations/koddi/utils.js b/src/cdk/v2/destinations/koddi/utils.js new file mode 100644 index 0000000000..13014e2e7c --- /dev/null +++ b/src/cdk/v2/destinations/koddi/utils.js @@ -0,0 +1,116 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { EVENT_TYPES, IMPRESSIONS_CONFIG, CLICKS_CONFIG, CONVERSIONS_CONFIG } = require('./config'); +const { + constructPayload, + defaultRequestConfig, + toUnixTimestamp, + stripTrailingSlash, +} = require('../../../../v0/util'); + +const validateBidders = (bidders) => { + if (!Array.isArray(bidders)) { + throw new InstrumentationError('properties.bidders should be an array of objects. Aborting.'); + } + if (bidders.length === 0) { + throw new InstrumentationError( + 'properties.bidders should contains at least one bidder. Aborting.', + ); + } + bidders.forEach((bidder) => { + if (!(bidder.bidder || bidder.alternate_bidder)) { + throw new InstrumentationError('bidder or alternate_bidder is not present. Aborting.'); + } + if (!bidder.count) { + throw new InstrumentationError('count is not present. Aborting.'); + } + if (!bidder.base_price) { + throw new InstrumentationError('base_price is not present. Aborting.'); + } + }); +}; + +/** + * This function constructs payloads based upon mappingConfig for all calls. + * @param {*} eventType + * @param {*} message + * @param {*} Config + * @returns + */ +const constructFullPayload = (eventType, message, Config) => { + let payload; + switch (eventType) { + case EVENT_TYPES.IMPRESSIONS: + payload = constructPayload(message, IMPRESSIONS_CONFIG); + payload.clientName = Config.clientName; + break; + case EVENT_TYPES.CLICKS: + payload = constructPayload(message, CLICKS_CONFIG); + payload.clientName = Config.clientName; + if (!Config.testVersionOverride) { + payload.testVersionOverride = null; + } + if (!Config.overrides) { + payload.overrides = null; + } + break; + case EVENT_TYPES.CONVERSIONS: + payload = constructPayload(message, CONVERSIONS_CONFIG); + payload.client_name = Config.clientName; + payload.unixtime = toUnixTimestamp(payload.unixtime); + validateBidders(payload.bidders); + break; + default: + throw new InstrumentationError(`event type ${eventType} is not supported.`); + } + return payload; +}; + +const getEndpoint = (eventType, Config) => { + let endpoint = stripTrailingSlash(Config.apiBaseUrl); + switch (eventType) { + case EVENT_TYPES.IMPRESSIONS: + endpoint += '?action=impression'; + break; + case EVENT_TYPES.CLICKS: + endpoint += '?action=click'; + break; + case EVENT_TYPES.CONVERSIONS: + endpoint += '/conversion'; + break; + default: + throw new InstrumentationError(`event type ${eventType} is not supported.`); + } + return endpoint; +}; + +/** + * This function constructs response based upon event. + * @param {*} eventType + * @param {*} Config + * @param {*} payload + * @returns + */ +const constructResponse = (eventType, Config, payload) => { + if (!Object.values(EVENT_TYPES).includes(eventType)) { + throw new InstrumentationError(`event type ${eventType} is not supported.`); + } + const response = defaultRequestConfig(); + response.endpoint = getEndpoint(eventType, Config); + response.headers = { + accept: 'application/json', + }; + if (eventType === EVENT_TYPES.CONVERSIONS) { + response.body.JSON = payload; + response.method = 'POST'; + response.headers = { + ...response.headers, + 'content-type': 'application/json', + }; + } else { + response.params = payload; + response.method = 'GET'; + } + return response; +}; + +module.exports = { getEndpoint, validateBidders, constructFullPayload, constructResponse }; diff --git a/src/cdk/v2/destinations/koddi/utils.test.js b/src/cdk/v2/destinations/koddi/utils.test.js new file mode 100644 index 0000000000..2c1f660f70 --- /dev/null +++ b/src/cdk/v2/destinations/koddi/utils.test.js @@ -0,0 +1,421 @@ +const { + getEndpoint, + validateBidders, + constructFullPayload, + constructResponse, +} = require('./utils'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); + +describe('getEndpoint', () => { + it('returns the correct endpoint for IMPRESSIONS event', () => { + const eventType = 'impressions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com/', + clientName: 'test-client', + }; + const result = getEndpoint(eventType, Config); + expect(result).toEqual('https://www.test-client.com?action=impression'); + }); + + it('returns the correct endpoint for CLICKS event', () => { + const eventType = 'clicks'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const result = getEndpoint(eventType, Config); + expect(result).toEqual('https://www.test-client.com?action=click'); + }); + + it('returns the correct endpoint for IMPRESSIONS event', () => { + const eventType = 'conversions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const result = getEndpoint(eventType, Config); + expect(result).toEqual('https://www.test-client.com/conversion'); + }); + + it('should throw error for unsupported event', () => { + const eventType = 'test'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + expect(() => getEndpoint(eventType, Config)).toThrow(InstrumentationError); + expect(() => getEndpoint(eventType, Config)).toThrow('event type test is not supported.'); + }); +}); + +describe('validateBidders', () => { + it('should throw error if bidders is not an array', () => { + const bidders = {}; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'properties.bidders should be an array of objects. Aborting.', + ); + }); + + it('should throw error if bidders is an empty array', () => { + const bidders = []; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'properties.bidders should contains at least one bidder. Aborting.', + ); + }); + + it('should throw error if bidder or alternate_bidder is not present', () => { + const bidders = [ + { count: 1, base_price: 100 }, + { bidder: 'bidder1', count: 2, base_price: 200 }, + { alternate_bidder: 'alternate1', count: 3, base_price: 300 }, + ]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow( + 'bidder or alternate_bidder is not present. Aborting.', + ); + }); + + it('should throw error if count is not present', () => { + const bidders = [{ bidder: 'bidder1', alternate_bidder: 'alternate1', base_price: 100 }]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow('count is not present. Aborting.'); + }); + + it('should throw error if base_price is not present', () => { + const bidders = [{ bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1 }]; + expect(() => validateBidders(bidders)).toThrow(InstrumentationError); + expect(() => validateBidders(bidders)).toThrow('base_price is not present. Aborting.'); + }); + + it('should not throw error if all required fields are present for all bidders', () => { + const bidders = [ + { bidder: 'bidder1', alternate_bidder: 'alternate1', count: 1, base_price: 100 }, + { bidder: 'bidder2', alternate_bidder: 'alternate2', count: 2, base_price: 200 }, + ]; + expect(() => validateBidders(bidders)).not.toThrow(); + }); +}); + +describe('constructFullPayload', () => { + it('should construct payload for IMPRESSIONS event', () => { + const eventType = 'impressions'; + const message = { + type: 'track', + event: 'Impressions Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + ts: '2024-03-03T00:29:12.117+05:30', + }; + const payload = constructFullPayload(eventType, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for IMPRESSIONS event', () => { + const eventType = 'impressions'; + const message = { + type: 'track', + event: 'Impressions Event', + properties: { + tracking_data: '', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventType, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "properties.tracking_data"'); + } + }); + + it('should construct payload for CLICKS event', () => { + const eventType = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + overrides: null, + testVersionOverride: null, + }; + const payload = constructFullPayload(eventType, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should construct payload with non-null value if overrides and testVersionOverride are enable and values for these are provided for CLICKS event ', () => { + const eventType = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + overrides: 'overridden-value', + testVersionOverride: 1, + }, + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + overrides: true, + testVersionOverride: false, + }; + const expectedPayload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + overrides: 'overridden-value', + testVersionOverride: null, + }; + const payload = constructFullPayload(eventType, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for CLICKS event', () => { + const eventType = 'clicks'; + const message = { + type: 'track', + event: 'Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventType, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "userId"'); + } + }); + + it('should construct payload for CONVERSIONS event', () => { + const eventType = 'conversions'; + const message = { + type: 'track', + event: 'Conversions Event', + properties: { + currency: 'USD', + order_id: '123', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const expectedPayload = { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: '123', + unixtime: 1709405952, + user_guid: '1234', + user_ip: '127.0.0.1', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }; + const payload = constructFullPayload(eventType, message, Config); + expect(payload).toEqual(expectedPayload); + }); + it('should throw error if required value is missing for CONVERSIONS event', () => { + const eventType = 'conversions'; + const message = { + type: 'track', + event: 'Conversions Event', + properties: { + currency: 'USD', + order_id: '123', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }, + context: { + ip: '127.0.0.1', + }, + timestamp: '2024-03-03T00:29:12.117+05:30', + anonymousId: '1234', + }; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + try { + const payload = constructFullPayload(eventType, message, Config); + } catch (error) { + expect(error.message).toEqual('Missing required value from "context.locale"'); + } + }); + + it('should throw error for unsupported event', () => { + const eventType = 'test'; + const message = {}; + const Config = {}; + expect(() => constructFullPayload(eventType, message, Config)).toThrow(InstrumentationError); + expect(() => constructFullPayload(eventType, message, Config)).toThrow( + 'event type test is not supported.', + ); + }); +}); + +describe('constructResponse', () => { + it('should construct response for IMPRESSIONS event', () => { + const eventType = 'impressions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + ts: '2024-03-03T00:29:12.117+05:30', + }; + const expectedResponse = { + endpoint: 'https://www.test-client.com?action=impression', + headers: { + accept: 'application/json', + }, + method: 'GET', + params: payload, + }; + const response = constructResponse(eventType, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should construct response for CLICKS event', () => { + const eventType = 'clicks'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + beaconIssued: '2024-03-04T15:32:56.409Z', + clientName: 'test-client', + rank: 1, + trackingData: 'dummy-tracking-data', + userGuid: '1234', + }; + const expectedResponse = { + endpoint: 'https://www.test-client.com?action=click', + headers: { + accept: 'application/json', + }, + method: 'GET', + params: payload, + }; + const response = constructResponse(eventType, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should construct response for CONVERSIONS event', () => { + const eventType = 'conversions'; + const Config = { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }; + const payload = { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: '123', + unixtime: 1709405952, + userGuid: '1234', + user_ip: '127.0.0.1', + bidders: [ + { + bidder: 'dummy-bidder-id', + count: 1, + base_price: 100.1, + }, + ], + }; + + const expectedResponse = { + endpoint: 'https://www.test-client.com/conversion', + headers: { + accept: 'application/json', + 'content-type': 'application/json', + }, + method: 'POST', + body: { + JSON: payload, + }, + }; + const response = constructResponse(eventType, Config, payload); + expect(response).toMatchObject(expectedResponse); + }); + + it('should throw error for unsupported event', () => { + const eventType = 'test'; + const Config = {}; + const payload = {}; + expect(() => constructResponse(eventType, Config, payload)).toThrow(InstrumentationError); + expect(() => constructResponse(eventType, Config, payload)).toThrow( + 'event type test is not supported.', + ); + }); +}); diff --git a/src/cdk/v2/destinations/linkedin_ads/config.js b/src/cdk/v2/destinations/linkedin_ads/config.js new file mode 100644 index 0000000000..344980e7d0 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/config.js @@ -0,0 +1,26 @@ +const { getMappingConfig } = require('../../../../v0/util'); + +// ref : https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/conversions-api?view=li-lms-2024-02&tabs=http#adding-multiple-conversion-events-in-a-batch +const BATCH_ENDPOINT = 'https://api.linkedin.com/rest/conversionEvents'; +const API_HEADER_METHOD = 'BATCH_CREATE'; +const API_VERSION = '202402'; // yyyymm format +const API_PROTOCOL_VERSION = '2.0.0'; + +const CONFIG_CATEGORIES = { + USER_INFO: { + name: 'linkedinUserInfoConfig', + type: 'user', + }, +}; + +const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); + +module.exports = { + MAX_BATCH_SIZE: 5000, + BATCH_ENDPOINT, + API_HEADER_METHOD, + API_VERSION, + API_PROTOCOL_VERSION, + CONFIG_CATEGORIES, + MAPPING_CONFIG, +}; diff --git a/src/cdk/v2/destinations/linkedin_ads/data/linkedinUserInfoConfig.json b/src/cdk/v2/destinations/linkedin_ads/data/linkedinUserInfoConfig.json new file mode 100644 index 0000000000..760510b5b3 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/data/linkedinUserInfoConfig.json @@ -0,0 +1,31 @@ +[ + { + "destKey": "firstName", + "sourceKeys": "firstName", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "lastName", + "sourceKeys": "lastName", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "title", + "sourceKeys": "title", + "required": false, + "sourceFromGenericMap": true + }, + { + "destKey": "companyName", + "sourceKeys": "context.traits.companyName", + "required": false + }, + { + "destKey": "countryCode", + "sourceKeys": "countryCode", + "sourceFromGenericMap": true, + "required": false + } +] diff --git a/src/cdk/v2/destinations/linkedin_ads/procWorkflow.yaml b/src/cdk/v2/destinations/linkedin_ads/procWorkflow.yaml new file mode 100644 index 0000000000..4b17afc368 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/procWorkflow.yaml @@ -0,0 +1,80 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + exportAll: true + - name: removeUndefinedValues + path: ../../../../v0/util + - name: removeUndefinedNullValuesAndEmptyObjectArray + path: ../../../../v0/util + - name: defaultRequestConfig + path: ../../../../v0/util + - name: OAuthSecretError + path: '@rudderstack/integrations-lib' + - path: ./utils + - path: ./config + - path: lodash + name: cloneDeep + +steps: + - name: checkIfProcessed + condition: .message.statusCode + template: | + $.batchMode ? .message.body.JSON : .message + onComplete: return + - name: messageType + template: | + .message.type.toLowerCase() + - name: validateInput + template: | + let messageType = $.outputs.messageType; + $.assert(messageType, "Message type is not present. Aborting message."); + $.assert(messageType in {{$.EventType.([.TRACK])}}, + "message type " + messageType + " is not supported") + + - name: validateInputForTrack + description: Additional validation for Track events + condition: $.outputs.messageType === {{$.EventType.TRACK}} + template: | + $.assert(.message.event, "event could not be mapped to conversion rule. Aborting.") + - name: commonFields + description: | + Builds common fields in destination payload. + ref: https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/conversions-api?view=li-lms-2024-02&tabs=curl#adding-multiple-conversion-events-in-a-batch + template: | + let commonFields = .message.().({ + "conversionHappenedAt": $.fetchAndVerifyConversionHappenedAt(^.message), + "eventId": $.getOneByPaths(., ^.destination.Config.deduplicationKey) ?? .messageId, + "conversionValue":$.calculateConversionObject(^.message), + "user":{ + "userIds":$.fetchUserIds(^.message,^.destination.Config), + "userInfo":$.curateUserInfoObject(^.message) + } + }); + $.removeUndefinedValues(commonFields) + - name: basePayload + template: | + const payload = $.outputs.commonFields; + payload + + - name: deduceConversionEventRules + template: | + $.context.deduceConversionRulesArray = $.deduceConversionRules(.message.event,.destination.Config) + + - name: preparePayload + template: | + $.context.payloads = $.context.deduceConversionRulesArray@conversionRuleId.( + const newPayload = $.cloneDeep($.outputs.basePayload); + newPayload.conversion = $.createConversionString(conversionRuleId); + $.removeUndefinedNullValuesAndEmptyObjectArray(newPayload) + )[]; + - name: buildResponse + template: | + $.assertThrow((.metadata.secret && .metadata.secret.accessToken), new $.OAuthSecretError("Secret or accessToken is not present in the metadata")) + const accessToken = .metadata.secret.accessToken + const response = $.defaultRequestConfig(); + response.body.JSON = {elements: $.context.payloads}; + response.endpoint = $.BATCH_ENDPOINT; + response.method = "POST"; + response.headers = $.generateHeader(accessToken) + response diff --git a/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml b/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml new file mode 100644 index 0000000000..dda322e45e --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/rtWorkflow.yaml @@ -0,0 +1,39 @@ +bindings: + - path: ./utils + - path: ./config + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "message": .[], + "destination": ^ [idx].destination, + "metadata": ^ [idx].metadata + })[] + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + $.batchResponseBuilder($.outputs.successfulEvents); + + - name: finalPayload + template: | + [...$.outputs.failedEvents, ...$.outputs.batchSuccessfulEvents] diff --git a/src/cdk/v2/destinations/linkedin_ads/utils.js b/src/cdk/v2/destinations/linkedin_ads/utils.js new file mode 100644 index 0000000000..69fea4299d --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/utils.js @@ -0,0 +1,285 @@ +const lodash = require('lodash'); +const crypto = require('crypto'); +const moment = require('moment'); + +const { + InstrumentationError, + getHashFromArrayWithDuplicate, + isDefinedAndNotNullAndNotEmpty, + ConfigurationError, +} = require('@rudderstack/integrations-lib'); +const { + getFieldValueFromMessage, + constructPayload, + getDestinationExternalID, +} = require('../../../../v0/util'); +const { + MAPPING_CONFIG, + CONFIG_CATEGORIES, + MAX_BATCH_SIZE, + API_HEADER_METHOD, + API_PROTOCOL_VERSION, + API_VERSION, +} = require('./config'); +const { + AUTH_STATUS_INACTIVE, + REFRESH_TOKEN, +} = require('../../../../adapters/networkhandler/authConstants'); + +const formatEmail = (email, destConfig) => { + if (email) { + if (destConfig.hashData === true) { + return crypto.createHash('sha256').update(email).digest('hex'); + } + return email; + } + return null; +}; + +const fetchUserIds = (message, destConfig) => { + const userIds = []; + const email = formatEmail(getFieldValueFromMessage(message, 'email'), destConfig); + const linkedinFirstPartyAdsTrackingUUID = getDestinationExternalID( + message, + 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + ); + const acxiomId = getDestinationExternalID(message, 'ACXIOM_ID'); + const oracleMoatId = getDestinationExternalID(message, 'ORACLE_MOAT_ID'); + if (!email && !linkedinFirstPartyAdsTrackingUUID && !acxiomId && !oracleMoatId) { + throw new InstrumentationError( + '[LinkedIn Conversion API] no matching user id found. Please provide at least one of the following: email, linkedinFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId', + ); + } + + if (email) { + userIds.push({ idType: 'SHA256_EMAIL', idValue: email }); + } + if (linkedinFirstPartyAdsTrackingUUID) { + userIds.push({ + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: linkedinFirstPartyAdsTrackingUUID, + }); + } + if (acxiomId) { + userIds.push({ idType: 'ACXIOM_ID', idValue: acxiomId }); + } + if (oracleMoatId) { + userIds.push({ idType: 'ORACLE_MOAT_ID', idValue: oracleMoatId }); + } + return userIds; +}; + +const curateUserInfoObject = (message) => { + const commonCategory = CONFIG_CATEGORIES.USER_INFO; + const commonPayload = constructPayload(message, MAPPING_CONFIG[commonCategory.name]); + if (commonPayload.firstName && commonPayload.lastName) { + return commonPayload; + } + return null; +}; + +function checkIfPricePresent(properties) { + // Check if 'products' exists and is an array + if (Array.isArray(properties?.products)) { + // Use 'some' to check if at least one object has a 'price' field + const hasPrice = properties.products.some((product) => product.hasOwnProperty('price')); + return hasPrice; + } + return !!properties.price; +} + +const calculateConversionObject = (message) => { + const { properties, event } = message; + + const calculateAmount = () => { + if (properties?.products && properties.products.length > 0) { + return properties.products.reduce( + (acc, product) => acc + (product.price || 0) * (product.quantity || 1), + 0, + ); + } + return properties.price * (properties.quantity ?? 1); + }; + if (checkIfPricePresent(properties)) { + const conversionObject = { + currencyCode: properties.currency || 'USD', + amount: `${calculateAmount()}`, + }; + return conversionObject; + } + throw new InstrumentationError( + `[LinkedIn Conversion API]: Cannot map price for event ${event}. Aborting`, + ); +}; + +const deduceConversionRules = (trackEventName, destConfig) => { + let conversionRule; + const { conversionMapping } = destConfig; + if (conversionMapping.length > 0) { + const keyMap = getHashFromArrayWithDuplicate(conversionMapping, 'from', 'to', false); + conversionRule = keyMap[trackEventName]; + } + if (isDefinedAndNotNullAndNotEmpty(conversionRule)) { + const finalEvent = typeof conversionRule === 'string' ? [conversionRule] : [...conversionRule]; + return finalEvent; + } + throw new ConfigurationError( + `[LinkedIn Conversion API] no matching conversion rule found for ${trackEventName}. Please provide a conversion rule. Aborting`, + ); +}; + +const createConversionString = (ruleId) => `urn:lla:llaPartnerConversion:${ruleId}`; + +const generateHeader = (accessToken) => { + const headers = { + 'Content-Type': 'application/json', + 'X-RestLi-Method': API_HEADER_METHOD, + 'X-Restli-Protocol-Version': API_PROTOCOL_VERSION, + 'LinkedIn-Version': API_VERSION, + Authorization: `Bearer ${accessToken}`, + }; + return headers; +}; + +const fetchAndVerifyConversionHappenedAt = (message) => { + const timeStamp = message.timestamp || message.originalTimestamp; + if (timeStamp) { + const start = moment(timeStamp); + if (!start.isValid()) { + throw new InstrumentationError('Invalid timestamp format.'); + } + const current = moment(); + // calculates past event in days + const deltaDay = current.diff(start, 'days', true); + + if (Math.ceil(deltaDay) > 90) { + throw new InstrumentationError('Events must be sent within ninety days of their occurrence.'); + } + } + + const timeInMilliseconds = moment(timeStamp).valueOf(); // `valueOf` returns the time in milliseconds + return timeInMilliseconds; +}; + +function batchResponseBuilder(successfulEvents) { + if (successfulEvents.length === 0) { + return []; + } + const constants = { + version: successfulEvents[0].message[0].version, + type: successfulEvents[0].message[0].type, + method: successfulEvents[0].message[0].method, + endpoint: successfulEvents[0].message[0].endpoint, + headers: successfulEvents[0].message[0].headers, + destination: successfulEvents[0].destination, + }; + + const allElements = successfulEvents.flatMap((event) => event.message[0].body.JSON.elements); + const allMetadata = successfulEvents.map((event) => event.metadata); + + // Using lodash to chunk the elements into groups of up to 3 + const chunkedElements = lodash.chunk(allElements, MAX_BATCH_SIZE); + const chunkedMetadata = lodash.chunk(allMetadata, MAX_BATCH_SIZE); + + return chunkedElements.map((elementsBatch, index) => ({ + batchedRequest: { + body: { + JSON: { elements: elementsBatch }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: constants.version, + type: constants.type, + method: constants.method, + endpoint: constants.endpoint, + headers: constants.headers, + params: {}, + files: {}, + }, + metadata: chunkedMetadata[index], + batched: true, + statusCode: 200, + destination: constants.destination, + })); +} + +function constructPartialStatus(errorMessage) { + const errorPattern = /Index: (\d+), ERROR :: (.*?)\n/g; + let match; + const errorMap = {}; + + try { + // eslint-disable-next-line no-cond-assign + while ((match = errorPattern.exec(errorMessage)) !== null) { + const [, index, message] = match; + errorMap[index] = message; + } + + return errorMap; + } catch (e) { + return null; + } +} + +function createResponseArray(metadata, partialStatus) { + const partialStatusArray = Object.entries(partialStatus).map(([index, message]) => [ + Number(index), + message, + ]); + // Convert destPartialStatus to an object for easier lookup + const errorMap = partialStatusArray.reduce((acc, [index, message]) => { + const jobId = metadata[index]?.jobId; // Get the jobId from the metadata array based on the index + if (jobId !== undefined) { + acc[jobId] = message; + } + return acc; + }, {}); + + return metadata.map((item) => { + const error = errorMap[item.jobId]; + return { + statusCode: error ? 400 : 500, + metadata: item, + error: error || 'success', + }; + }); +} + +/** + * + * @param {*} destinationResponse example: {status : 401, response {"status":401,"serviceErrorCode":65601,"code":"REVOKED_ACCESS_TOKEN","message":"The token used in the request has been revoked by the user"}} + * @returns proper auth error category + */ +const getAuthErrCategoryFromStCode = (destinationResponse) => { + const { status, response } = destinationResponse; + if (status === 401) { + if (response.code === 'REVOKED_ACCESS_TOKEN') { + // ACCESS_DENIED + return AUTH_STATUS_INACTIVE; + } + // UNAUTHORIZED + return REFRESH_TOKEN; + } + if (status === 403) { + // ACCESS_DENIED + return AUTH_STATUS_INACTIVE; + } + return ''; +}; + +module.exports = { + formatEmail, + calculateConversionObject, + curateUserInfoObject, + fetchUserIds, + deduceConversionRules, + createConversionString, + generateHeader, + fetchAndVerifyConversionHappenedAt, + batchResponseBuilder, + constructPartialStatus, + createResponseArray, + checkIfPricePresent, + getAuthErrCategoryFromStCode, +}; diff --git a/src/cdk/v2/destinations/linkedin_ads/utils.test.js b/src/cdk/v2/destinations/linkedin_ads/utils.test.js new file mode 100644 index 0000000000..ee52928198 --- /dev/null +++ b/src/cdk/v2/destinations/linkedin_ads/utils.test.js @@ -0,0 +1,293 @@ +const crypto = require('crypto'); +const { + formatEmail, + calculateConversionObject, + fetchUserIds, + curateUserInfoObject, + deduceConversionRules, + generateHeader, + constructPartialStatus, + createResponseArray, + checkIfPricePresent, +} = require('./utils'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { API_HEADER_METHOD, API_PROTOCOL_VERSION, API_VERSION } = require('./config'); + +describe('formatEmail', () => { + // Returns a hashed email when a valid email is passed as argument. + it('should return a hashed email when a valid email is passed as argument', () => { + const email = 'test@example.com'; + const hashedEmail = crypto.createHash('sha256').update(email).digest('hex'); + expect(formatEmail(email, { hashData: true })).toEqual(hashedEmail); + }); + + // Returns null when an empty string is passed as argument. + it('should return null when an empty string is passed as argument', () => { + const email = ''; + expect(formatEmail(email)).toBeNull(); + }); +}); + +describe('calculateConversionObject', () => { + // Returns a conversion object with currency code 'USD' and amount 0 when message properties are empty + it('should throw instrumentation error when message properties are empty', () => { + const message = { properties: {} }; + expect(() => { + fetchUserIds(calculateConversionObject(message)); + }).toThrow(InstrumentationError); + }); + + // Returns a conversion object with currency code 'USD' and amount 0 when message properties price is defined but quantity is 0 + it('should return a conversion object with currency code "USD" and amount 0 when message properties price is defined but quantity is 0', () => { + const message = { properties: { price: 10, quantity: 0 } }; + const conversionObject = calculateConversionObject(message); + expect(conversionObject).toEqual({ currencyCode: 'USD', amount: '0' }); + }); +}); + +describe('fetchUserIds', () => { + // Throws an InstrumentationError when no user id is found in the message and no exception is caught + it('should throw an InstrumentationError when no user id is found in the message and no exception is caught', () => { + const message = {}; + const destConfig = { + hashData: true, + }; + expect(() => { + fetchUserIds(message, destConfig); + }).toThrow(InstrumentationError); + }); + it('should create user Ids array of objects with all allowed values', () => { + const message = { + context: { + traits: { + email: 'abc@gmail.com', + }, + externalId: [ + { + type: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + id: 'abcdefg', + }, + { + type: 'ACXIOM_ID', + id: '123456', + }, + { + type: 'ORACLE_MOAT_ID', + id: '789012', + }, + ], + }, + }; + const destConfig = { + hashData: true, + }; + const userIdArray = fetchUserIds(message, destConfig); + expect(userIdArray).toEqual([ + { + idType: 'SHA256_EMAIL', + idValue: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + { + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: 'abcdefg', + }, + { + idType: 'ACXIOM_ID', + idValue: '123456', + }, + { + idType: 'ORACLE_MOAT_ID', + idValue: '789012', + }, + ]); + }); +}); + +describe('curateUserInfoObject', () => { + // Returns a non-null object when given a message with both first and last name + it('should return a non-null object when given a message with both first and last name and other properties', () => { + const message = { + context: { + traits: { + firstName: 'John', + lastName: 'Doe', + title: 'Mr.', + companyName: 'RudderTest', + countryCode: 'USA', + }, + }, + }; + const result = curateUserInfoObject(message); + expect(result).toEqual({ + firstName: 'John', + lastName: 'Doe', + title: 'Mr.', + companyName: 'RudderTest', + countryCode: 'USA', + }); + }); + // Returns a null object when given a message with an empty first name + it('should return a null object when given a message without both first and last name', () => { + const message = { + context: { + traits: { + title: 'Mr.', + companyName: 'RudderTest', + countryCode: 'USA', + }, + }, + }; + const result = curateUserInfoObject(message); + expect(result).toEqual(null); + }); +}); + +describe('deduceConversionRules', () => { + // When conversionMapping is empty, return ConfigurationError + it('should return ConfigurationError when conversionMapping is empty', () => { + const trackEventName = 'eventName'; + const destConfig = { + conversionMapping: [], + }; + expect(() => deduceConversionRules(trackEventName, destConfig)).toThrow(ConfigurationError); + }); + + // When conversionMapping is not empty, return the conversion rule + it('should return the conversion rule when conversionMapping is not empty', () => { + const trackEventName = 'eventName'; + const destConfig = { + conversionMapping: [{ from: 'eventName', to: 'conversionEvent' }], + }; + const result = deduceConversionRules(trackEventName, destConfig); + expect(result).toEqual(['conversionEvent']); + }); + + it('should return the conversion rule when conversionMapping is not empty', () => { + const trackEventName = 'eventName'; + const destConfig = { + conversionMapping: [ + { from: 'eventName', to: 'conversionEvent' }, + { from: 'eventName', to: 'conversionEvent2' }, + ], + }; + const result = deduceConversionRules(trackEventName, destConfig); + expect(result).toEqual(['conversionEvent', 'conversionEvent2']); + }); +}); + +describe('generateHeader', () => { + // Returns a headers object with Content-Type, X-RestLi-Method, X-Restli-Protocol-Version, LinkedIn-Version, and Authorization keys when passed a valid access token. + it('should return a headers object with all keys when passed a valid access token', () => { + // Arrange + const accessToken = 'validAccessToken'; + + // Act + const result = generateHeader(accessToken); + + // Assert + expect(result).toEqual({ + 'Content-Type': 'application/json', + 'X-RestLi-Method': API_HEADER_METHOD, + 'X-Restli-Protocol-Version': API_PROTOCOL_VERSION, + 'LinkedIn-Version': API_VERSION, + Authorization: `Bearer ${accessToken}`, + }); + }); + + // Returns a headers object with default values for all keys when passed an invalid access token. + it('should return a headers object with default values for all keys when passed an invalid access token', () => { + // Arrange + const accessToken = 'invalidAccessToken'; + + // Act + const result = generateHeader(accessToken); + + // Assert + expect(result).toEqual({ + 'Content-Type': 'application/json', + 'X-RestLi-Method': API_HEADER_METHOD, + 'X-Restli-Protocol-Version': API_PROTOCOL_VERSION, + 'LinkedIn-Version': API_VERSION, + Authorization: `Bearer ${accessToken}`, + }); + }); +}); + +describe('constructPartialStatus', () => { + // The function correctly constructs a map of error messages when given a string containing error messages. + it('should correctly construct a map of error messages when given a string containing error messages', () => { + const errorMessage = 'Index: 1, ERROR :: Error 1\nIndex: 2, ERROR :: Error 2\n'; + const expectedErrorMap = { + 1: 'Error 1', + 2: 'Error 2', + }; + + const result = constructPartialStatus(errorMessage); + + expect(result).toEqual(expectedErrorMap); + }); + + // The function throws an error when given a non-string input. + it('should throw an error when given a non-string input', () => { + const errorMessage = 123; + const result = constructPartialStatus(errorMessage); + expect(result).toEqual({}); + }); +}); + +describe('createResponseArray', () => { + // Returns an array of objects with statusCode, metadata and error properties + it('should return an array of objects with statusCode, metadata and error properties', () => { + // Arrange + const metadata = [{ jobId: 1 }, { jobId: 2 }, { jobId: 3 }]; + const partialStatus = { + 0: 'Partial status message 1', + 2: 'Partial status message 3', + }; + + // Act + const result = createResponseArray(metadata, partialStatus); + + // Assert + expect(result).toEqual([ + { + statusCode: 400, + metadata: { jobId: 1 }, + error: 'Partial status message 1', + }, + { + statusCode: 500, + metadata: { jobId: 2 }, + error: 'success', + }, + { + statusCode: 400, + metadata: { jobId: 3 }, + error: 'Partial status message 3', + }, + ]); + }); +}); + +describe('checkIfPricePresent', () => { + // Returns true if properties object has a 'price' field + it('should return true when properties object has a price field', () => { + const properties = { price: 10 }; + const result = checkIfPricePresent(properties); + expect(result).toBe(true); + }); + + // Returns true if properties object has a 'products' array with an object containing a 'price' field and a 'price' field in the properties object + it('should return true when properties object has a products array with an object containing a price field and a price field in the properties object', () => { + const properties = { products: [{ price: 10 }, { quantity: 3 }], price: 20 }; + const result = checkIfPricePresent(properties); + expect(result).toBe(true); + }); + + // Returns false if properties object does not have a 'price' field or a 'products' array with an object containing a 'price' field + it('should return false when properties object does not have a price field or a products array with an object containing a price field', () => { + const properties = { quantity: 5 }; + const result = checkIfPricePresent(properties); + expect(result).toBe(false); + }); +}); diff --git a/src/cdk/v2/destinations/movable_ink/config.js b/src/cdk/v2/destinations/movable_ink/config.js new file mode 100644 index 0000000000..9a0200ab44 --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/config.js @@ -0,0 +1,4 @@ +module.exports = { + MAX_REQUEST_SIZE_IN_BYTES: 1000000, + MAX_BATCH_SIZE: 1000, +}; diff --git a/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml new file mode 100644 index 0000000000..fef11124b3 --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/procWorkflow.yaml @@ -0,0 +1,74 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + - name: defaultRequestConfig + path: ../../../../v0/util + - name: toUnixTimestampInMS + path: ../../../../v0/util + - name: base64Convertor + path: ../../../../v0/util + - path: ./utils + +steps: + - name: messageType + template: | + .message.type.toLowerCase(); + + - name: validateInput + template: | + let messageType = $.outputs.messageType; + $.assert(messageType, "message Type is not present. Aborting"); + $.assert(messageType in {{$.EventType.([.IDENTIFY,.TRACK])}}, "message type " + messageType + " is not supported"); + $.assertConfig(.destination.Config.endpoint, "Movable Ink Endpoint is not present. Aborting"); + $.assertConfig(.destination.Config.accessKey, "Access key is not present . Aborting"); + $.assertConfig(.destination.Config.accessSecret, "Access Secret is not present. Aborting"); + $.assert(.message.timestamp ?? .message.originalTimestamp, "Timestamp is not present. Aborting"); + $.assert(!(messageType === {{$.EventType.TRACK}} && !(.message.event)), "Event name is not present. Aborting"); + + const userId = .message.().( + {{{{$.getGenericPaths("userIdOnly")}}}}; + ); + const email = .message.().( + {{{{$.getGenericPaths("email")}}}}; + ); + + $.assert(userId || email || .message.anonymousId, "Either one of userId or email or anonymousId is required. Aborting"); + $.validateEventPayload(.message); + + - name: preparePayload + description: Prepare payload for identify and track. This payload schema needs to be configured in the Movable Ink dashboard. Movable Ink will discard any additional fields from the input payload. + template: | + const userId = .message.().( + {{{{$.getGenericPaths("userIdOnly")}}}}; + ); + const email = .message.().( + {{{{$.getGenericPaths("email")}}}}; + ); + const timestampInUnix = $.toUnixTimestampInMS(.message.().( + {{{{$.getGenericPaths("timestamp")}}}}; + )); + $.context.payload = { + ...(.message), + userId: userId || email, + timestamp: timestampInUnix, + anonymousId: .message.anonymousId + } + + - name: buildResponse + description: In batchMode we return payload directly + condition: $.batchMode + template: | + $.context.payload + else: + name: buildResponseForProcessTransformation + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.context.payload; + response.endpoint = .destination.Config.endpoint; + response.method = "POST"; + response.headers = { + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.destination.Config.accessKey + ":" + .destination.Config.accessSecret) + } + response; diff --git a/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml b/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml new file mode 100644 index 0000000000..3ffa49f15b --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/rtWorkflow.yaml @@ -0,0 +1,74 @@ +bindings: + - name: handleRtTfSingleEventError + path: ../../../../v0/util/index + - path: ./utils + exportAll: true + - name: base64Convertor + path: ../../../../v0/util + - name: BatchUtils + path: '@rudderstack/workflow-engine' + - path: ./config + +steps: + - name: validateInput + template: | + $.assert(Array.isArray(^) && ^.length > 0, "Invalid event array") + + - name: transform + externalWorkflow: + path: ./procWorkflow.yaml + bindings: + - name: batchMode + value: true + loopOverInput: true + + - name: successfulEvents + template: | + $.outputs.transform#idx.output.({ + "batchedRequest": ., + "batched": false, + "destination": ^[idx].destination, + "metadata": ^[idx].metadata, + "statusCode": 200 + })[] + + - name: failedEvents + template: | + $.outputs.transform#idx.error.( + $.handleRtTfSingleEventError(^[idx], .originalError ?? ., {}) + )[] + + - name: batchSuccessfulEvents + description: Batches the successfulEvents + template: | + let batches = $.BatchUtils.chunkArrayBySizeAndLength( + $.outputs.successfulEvents, {maxSizeInBytes: $.MAX_REQUEST_SIZE_IN_BYTES, maxItems: $.MAX_BATCH_SIZE}).items; + + batches@batch.({ + "batchedRequest": { + "body": { + "JSON": {"events": ~r batch.batchedRequest[]}, + "JSON_ARRAY": {}, + "XML": {}, + "FORM": {} + }, + "version": "1", + "type": "REST", + "method": "POST", + "endpoint": batch[0].destination.Config.().(.endpoint), + "headers": batch[0].destination.Config.().({ + "Content-Type": "application/json", + "Authorization": "Basic " + $.base64Convertor(.accessKey + ":" + .accessSecret) + }), + "params": {}, + "files": {} + }, + "metadata": ~r batch.metadata[], + "batched": true, + "statusCode": 200, + "destination": batch[0].destination + })[]; + + - name: finalPayload + template: | + [...$.outputs.batchSuccessfulEvents, ...$.outputs.failedEvents] diff --git a/src/cdk/v2/destinations/movable_ink/utils.js b/src/cdk/v2/destinations/movable_ink/utils.js new file mode 100644 index 0000000000..04d7046b1a --- /dev/null +++ b/src/cdk/v2/destinations/movable_ink/utils.js @@ -0,0 +1,21 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); + +const validateEventPayload = (message) => { + const { event } = message; + const { properties } = message; + if (event === 'Products Searched' && !properties?.query) { + throw new InstrumentationError("Missing 'query' property in properties. Aborting"); + } + + if ( + (event === 'Product Added' || + event === 'Product Removed' || + event === 'Product Viewed' || + event === 'Category Viewed') && + !properties?.product_id + ) { + throw new InstrumentationError("Missing 'product_id' property in properties. Aborting"); + } +}; + +module.exports = { validateEventPayload }; diff --git a/src/cdk/v2/destinations/ninetailed/config.js b/src/cdk/v2/destinations/ninetailed/config.js index c38496a415..efb1a8908e 100644 --- a/src/cdk/v2/destinations/ninetailed/config.js +++ b/src/cdk/v2/destinations/ninetailed/config.js @@ -17,13 +17,8 @@ const ConfigCategories = { type: 'identify', name: 'identifyMapping', }, - PAGE: { - type: 'page', - name: 'pageMapping', - }, }; -// MAX_BATCH_SIZE : // Maximum number of events to send in a single batch const mappingConfig = getMappingConfig(ConfigCategories, __dirname); const batchEndpoint = 'https://experience.ninetailed.co/v2/organizations/{{organisationId}}/environments/{{environment}}/events'; diff --git a/src/cdk/v2/destinations/ninetailed/data/contextMapping.json b/src/cdk/v2/destinations/ninetailed/data/contextMapping.json index 3d6392dd1e..ad301c8150 100644 --- a/src/cdk/v2/destinations/ninetailed/data/contextMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/contextMapping.json @@ -1,12 +1,10 @@ [ { "sourceKeys": "app.name", - "required": true, "destKey": "app.name" }, { "sourceKeys": "app.version", - "required": true, "destKey": "app.version" }, { @@ -15,12 +13,16 @@ }, { "sourceKeys": "library.name", - "required": true, - "destKey": "library.name" + "destKey": "library.name", + "metadata": { + "defaultValue": "Rudderstack Ninetailed Destination" + } }, { "sourceKeys": "library.version", - "required": true, + "metadata": { + "defaultValue": "1" + }, "destKey": "library.version" }, { @@ -37,7 +39,9 @@ }, { "sourceKeys": "location", - "required": true, + "metadata": { + "defaultValue": {} + }, "destKey": "location" } ] diff --git a/src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json b/src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json index 3ab72d1b9f..22fc637728 100644 --- a/src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/generalPayloadMapping.json @@ -11,7 +11,9 @@ }, { "sourceKeys": "channel", - "required": true, + "metadata": { + "defaultValue": "server" + }, "destKey": "channel" }, { diff --git a/src/cdk/v2/destinations/ninetailed/data/identifyMapping.json b/src/cdk/v2/destinations/ninetailed/data/identifyMapping.json index e8d3f7797d..b1a340bd98 100644 --- a/src/cdk/v2/destinations/ninetailed/data/identifyMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/identifyMapping.json @@ -2,13 +2,14 @@ { "sourceKeys": "traits", "sourceFromGenericMap": true, - "required": true, + "metadata": { + "defaultValue": {} + }, "destKey": "traits" }, { "sourceKeys": "userIdOnly", "sourceFromGenericMap": true, - "required": true, "destKey": "userId" } ] diff --git a/src/cdk/v2/destinations/ninetailed/data/pageMapping.json b/src/cdk/v2/destinations/ninetailed/data/pageMapping.json deleted file mode 100644 index 80ec2f58f1..0000000000 --- a/src/cdk/v2/destinations/ninetailed/data/pageMapping.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "sourceKeys": "properties", - "required": true, - "destKey": "properties" - } -] diff --git a/src/cdk/v2/destinations/ninetailed/data/trackMapping.json b/src/cdk/v2/destinations/ninetailed/data/trackMapping.json index 44af6dd1a3..5a13f5bba2 100644 --- a/src/cdk/v2/destinations/ninetailed/data/trackMapping.json +++ b/src/cdk/v2/destinations/ninetailed/data/trackMapping.json @@ -1,7 +1,9 @@ [ { "sourceKeys": "properties", - "required": true, + "metadata": { + "defaultValue": {} + }, "destKey": "properties" }, { diff --git a/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml b/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml index 6f5056ce10..e31912386a 100644 --- a/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml +++ b/src/cdk/v2/destinations/ninetailed/procWorkflow.yaml @@ -16,14 +16,13 @@ steps: template: | let messageType = $.outputs.messageType; $.assert(messageType, "message Type is not present. Aborting"); - $.assert(messageType in {{$.EventType.([.TRACK,.IDENTIFY,.PAGE])}}, "message type " + messageType + " is not supported"); + $.assert(messageType in {{$.EventType.([.TRACK,.IDENTIFY])}}, "message type " + messageType + " is not supported"); $.assertConfig(.destination.Config.organisationId, "Organisation ID is not present. Aborting"); $.assertConfig(.destination.Config.environment, "Environment is not present. Aborting"); - name: preparePayload template: | const payload = $.constructFullPayload(.message); $.context.payload = $.removeUndefinedAndNullValues(payload); - - name: buildResponse template: | const response = $.defaultRequestConfig(); diff --git a/src/cdk/v2/destinations/ninetailed/utils.js b/src/cdk/v2/destinations/ninetailed/utils.js index b716422a0e..fec1271561 100644 --- a/src/cdk/v2/destinations/ninetailed/utils.js +++ b/src/cdk/v2/destinations/ninetailed/utils.js @@ -30,12 +30,7 @@ const constructFullPayload = (message) => { message, config.mappingConfig[config.ConfigCategories.IDENTIFY.name], ); - break; - case 'page': - typeSpecifcPayload = constructPayload( - message, - config.mappingConfig[config.ConfigCategories.PAGE.name], - ); + typeSpecifcPayload.userId = typeSpecifcPayload.userId || ''; break; default: break; diff --git a/src/cdk/v2/destinations/ortto/procWorkflow.yaml b/src/cdk/v2/destinations/ortto/procWorkflow.yaml index dfd7118c41..27c3749cc8 100644 --- a/src/cdk/v2/destinations/ortto/procWorkflow.yaml +++ b/src/cdk/v2/destinations/ortto/procWorkflow.yaml @@ -48,9 +48,9 @@ steps: "str::ei": {{{{$.getGenericPaths("userId")}}}}, "str::language": .context.traits.language || .context.locale, "phn::phone": phone ? {"n": phone}, - "bol::gdpr": .context.traits.gdpr ?? true, - "bol::p": .context.traits.emailConsent || false, - "bol::sp": .context.traits.smsConsent || false, + "bol::gdpr": .context.traits.gdpr, + "bol::p": .context.traits.emailConsent, + "bol::sp": .context.traits.smsConsent, }, "location": {"source_ip": .context.ip} }); @@ -80,11 +80,8 @@ steps: steps: - name: getTrimmedEvent template: | - let customEvent = ""; const event = .message.event; - .destination.Config.orttoEventsMapping@order.( - customEvent = event === .rsEventName ? .orttoEventName : null; - ) + const customEvent = .destination.Config.orttoEventsMapping.{.rsEventName === event}.orttoEventName; $.assert(customEvent, "Event names is not mapped"); "act:cm:"+customEvent.trim().toLowerCase().replace(new RegExp('\\s+', 'g'),'-'); diff --git a/src/cdk/v2/destinations/rakuten/data/propertiesMapping.json b/src/cdk/v2/destinations/rakuten/data/propertiesMapping.json index db5d36fc4d..1a69f53205 100644 --- a/src/cdk/v2/destinations/rakuten/data/propertiesMapping.json +++ b/src/cdk/v2/destinations/rakuten/data/propertiesMapping.json @@ -6,8 +6,10 @@ }, { "sourceKeys": ["properties.tr", "properties.ran_site_id", "properties.ranSiteID"], - "required": true, - "destKey": "tr" + "destKey": "tr", + "metadata": { + "defaultValue": " " + } }, { "sourceKeys": ["properties.land", "properties.land_time", "properties.landTime"], diff --git a/src/cdk/v2/destinations/reddit/procWorkflow.yaml b/src/cdk/v2/destinations/reddit/procWorkflow.yaml index 59725c1257..06d2c95f25 100644 --- a/src/cdk/v2/destinations/reddit/procWorkflow.yaml +++ b/src/cdk/v2/destinations/reddit/procWorkflow.yaml @@ -12,6 +12,7 @@ bindings: path: ../../../../v0/util/index - name: OAuthSecretError path: '@rudderstack/integrations-lib' + - path: ./utils steps: - name: validateInput @@ -35,13 +36,13 @@ steps: const os = (.message.context.os.name)? .message.context.os.name.toLowerCase(): null; const hashData = .destination.Config.hashData; let user = .message.().({ - "email": hashData ? $.SHA256({{{{$.getGenericPaths("email")}}}}) : ({{{{$.getGenericPaths("email")}}}}), - "external_id": hashData ? $.SHA256({{{{$.getGenericPaths("userId")}}}}) : ({{{{$.getGenericPaths("userId")}}}}), - "ip_address": hashData? $.SHA256(.context.ip || .request_ip) : (.context.ip || .request_ip), + "email": hashData ? $.SHA256({{{{$.getGenericPaths("email")}}}}.trim()) : ({{{{$.getGenericPaths("email")}}}}), + "external_id": hashData ? $.SHA256({{{{$.getGenericPaths("userId")}}}}.trim()) : ({{{{$.getGenericPaths("userId")}}}}), + "ip_address": hashData? $.SHA256(.context.ip.trim() || .request_ip.trim()) : (.context.ip || .request_ip), "uuid": .properties.uuid, "user_agent": .context.userAgent, - "idfa": $.isAppleFamily(os)? (hashData? $.SHA256(.context.device.advertisingId): .context.device.advertisingId): null, - "aaid": os === "android" && .context.device ? (hashData? $.SHA256(.context.device.advertisingId): .context.device.advertisingId): null, + "idfa": $.isAppleFamily(os)? (hashData? $.SHA256(.context.device.advertisingId.trim()): .context.device.advertisingId): null, + "aaid": os === "android" && .context.device ? (hashData? $.SHA256(.context.device.advertisingId.trim()): .context.device.advertisingId): null, "opt_out": .properties.optOut, "screen_dimensions": {"width": .context.screen.width, "height": .context.screen.height}, }); @@ -56,14 +57,14 @@ steps: const event_type = (eventNames.length === 0 || eventNames[0]==="") ? ({"tracking_type": "Custom", "custom_event_name": event}): ({tracking_type: eventNames[0]}); - name: customFields - condition: $.outputs.prepareTrackPayload.eventType.tracking_type === "Purchase" + condition: $.outputs.prepareTrackPayload.eventType.tracking_type in ['Purchase', 'AddToCart', 'ViewContent'] reference: 'https://ads-api.reddit.com/docs/v2/#tag/Conversions/paths/~1api~1v2.0~1conversions~1events~1%7Baccount_id%7D/post' template: | - const revenue_in_cents = .message.properties.revenue ? Math.round(Number(.message.properties.revenue)*100) + const revenue_in_cents = $.populateRevenueField($.outputs.prepareTrackPayload.eventType.tracking_type,^.message.properties) const customFields = .message.().({ "currency": .properties.currency, "value_decimal": revenue_in_cents ? revenue_in_cents / 100, - "item_count": (Array.isArray(.properties.products) && .properties.products.length) || (.properties.itemCount && Number(.properties.itemCount)), + "item_count": $.outputs.prepareTrackPayload.eventType.tracking_type === 'Purchase' ? (Array.isArray(.properties.products) && .properties.products.length) || (.properties.itemCount && Number(.properties.itemCount)) : null, "value": revenue_in_cents, "conversion_id": .properties.conversionId || .messageId, }); diff --git a/src/cdk/v2/destinations/reddit/utils.js b/src/cdk/v2/destinations/reddit/utils.js index c108603235..f562d31313 100644 --- a/src/cdk/v2/destinations/reddit/utils.js +++ b/src/cdk/v2/destinations/reddit/utils.js @@ -28,6 +28,59 @@ const batchEvents = (successfulEvents) => { const batchedEvents = batchEventChunks(eventChunks); return batchedEvents; }; + +const calculateDefaultRevenue = (properties) => { + // Check if working with products array + if (properties?.products && properties.products.length > 0) { + // Check if all product prices are undefined + if (properties.products.every((product) => product.price === undefined)) { + return null; // Return null if all product prices are undefined + } + // Proceed with calculation if not all prices are undefined + return properties.products.reduce( + (acc, product) => acc + (product.price || 0) * (product.quantity || 1), + 0, + ); + } + // For single product scenario, check if price is undefined + if (properties.price === undefined) { + return null; // Return null if price is undefined + } + // Proceed with calculation if price is defined + return properties.price * (properties.quantity ?? 1); +}; + +const populateRevenueField = (eventType, properties) => { + let revenueInCents; + switch (eventType) { + case 'Purchase': + revenueInCents = + properties.revenue && !Number.isNaN(properties.revenue) + ? Math.round(Number(properties?.revenue) * 100) + : null; + break; + case 'AddToCart': + revenueInCents = + properties.price && !Number.isNaN(properties.price) + ? Math.round(Number(properties?.price) * Number(properties?.quantity || 1) * 100) + : null; + break; + default: + // for viewContent + // eslint-disable-next-line no-case-declarations + const revenue = calculateDefaultRevenue(properties); + revenueInCents = revenue ? revenue * 100 : null; + break; + } + + if (lodash.isNaN(revenueInCents)) { + return null; + } + // Return the value as it is if it's not NaN + return revenueInCents; +}; module.exports = { batchEvents, + populateRevenueField, + calculateDefaultRevenue, }; diff --git a/src/cdk/v2/destinations/reddit/utils.test.js b/src/cdk/v2/destinations/reddit/utils.test.js new file mode 100644 index 0000000000..7cfa87e38f --- /dev/null +++ b/src/cdk/v2/destinations/reddit/utils.test.js @@ -0,0 +1,121 @@ +const { calculateDefaultRevenue, populateRevenueField } = require('./utils'); + +describe('calculateDefaultRevenue', () => { + // Calculates revenue for a single product with defined price and quantity + it('should calculate revenue for a single product with defined price and quantity', () => { + const properties = { + price: 10, + quantity: 2, + }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBe(20); + }); + + // Returns null for properties parameter being undefined + it('should return null for price parameter being undefined', () => { + const properties = { products: [{ quantity: 1 }] }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBeNull(); + }); + + // Calculates revenue for a single product with defined price and default quantity + it('should calculate revenue for a single product with defined price and default quantity', () => { + const properties = { + price: 10, + }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBe(10); + }); + + // Calculates revenue for multiple products with defined prices and quantities + it('should calculate revenue for multiple products with defined prices and quantities', () => { + const properties = { + products: [{ price: 10, quantity: 2 }, { quantity: 3 }], + }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBe(20); + }); + + // Calculates revenue for multiple products with defined prices and default quantities + it('should calculate revenue for multiple products with defined prices and default quantities', () => { + const properties = { + products: [{ price: 10 }, { price: 5 }], + }; + + const result = calculateDefaultRevenue(properties); + + expect(result).toBe(15); + }); +}); + +describe('populateRevenueField', () => { + // Returns revenue in cents for Purchase event type with valid revenue property + it('should return revenue in cents when Purchase event type has valid revenue property', () => { + const eventType = 'Purchase'; + const properties = { + revenue: '10.50', + }; + const expected = 1050; + + const result = populateRevenueField(eventType, properties); + + expect(result).toBe(expected); + }); + + // Returns null for Purchase event type with revenue property as non-numeric string + it('should return null when Purchase event type has revenue property as non-numeric string', () => { + const eventType = 'Purchase'; + const properties = { + revenue: 'invalid', + }; + const expected = null; + + const result = populateRevenueField(eventType, properties); + + expect(result).toBe(expected); + }); + + // Returns revenue in cents for AddToCart event type with valid price and quantity properties + it('should return revenue in cents when AddToCart event type has valid price and quantity properties', () => { + const eventType = 'AddToCart'; + const properties = { + price: '10.50', + quantity: 2, + }; + const expected = 2100; + + const result = populateRevenueField(eventType, properties); + + expect(result).toBe(expected); + }); + + // Returns revenue in cents for ViewContent event type with valid properties + it('should return revenue in cents when ViewContent event type has valid properties', () => { + const eventType = 'ViewContent'; + const properties = { + products: [ + { + price: '10.50', + quantity: 2, + }, + { + price: '5.25', + quantity: 3, + }, + ], + }; + const expected = 3675; + + const result = populateRevenueField(eventType, properties); + + expect(result).toBe(expected); + }); +}); diff --git a/src/cdk/v2/destinations/yandex_metrica_offline_events/config.js b/src/cdk/v2/destinations/yandex_metrica_offline_events/config.js new file mode 100644 index 0000000000..83513c3856 --- /dev/null +++ b/src/cdk/v2/destinations/yandex_metrica_offline_events/config.js @@ -0,0 +1,5 @@ +const YANDEX_METRICA_OFFLINE_EVENTS = 'yandex_metrica_offline_events'; + +module.exports = { + YANDEX_METRICA_OFFLINE_EVENTS, +}; diff --git a/src/cdk/v2/destinations/yandex_metrica_offline_events/procWorkflow.yaml b/src/cdk/v2/destinations/yandex_metrica_offline_events/procWorkflow.yaml new file mode 100644 index 0000000000..690bc399ee --- /dev/null +++ b/src/cdk/v2/destinations/yandex_metrica_offline_events/procWorkflow.yaml @@ -0,0 +1,36 @@ +bindings: + - name: EventType + path: ../../../../constants + - path: ../../bindings/jsontemplate + exportAll: true + - path: ./config + - name: removeUndefinedAndNullValues + path: ../../../../v0/util + - name: defaultRequestConfig + path: ../../../../v0/util + - path: ./utils + +steps: + - name: validateInput + template: | + let messageType = .message.type; + $.assert(messageType, "message Type is not present. Aborting message."); + $.assert(.message.type.toLowerCase() ==='identify', "Event type " + .message.type.toLowerCase() + " is not supported. Aborting message."); + $.assert(.message.traits || .message.properties, "Message traits/properties not present. Aborting message."); + + - name: prepareData + template: | + let data = .message.traits + let identifierType = .message.context.externalId[0].identifierType; + let identifierValue = .message.context.externalId[0].id; + identifierValue = String(identifierValue); + data = $.setIdentifier(data, identifierType, identifierValue) + data = $.validateData(data) + data + + - name: buildResponseForProcessTransformation + description: build response + template: | + const response = $.defaultRequestConfig(); + response.body.JSON = $.outputs.prepareData + response diff --git a/src/cdk/v2/destinations/yandex_metrica_offline_events/utils.js b/src/cdk/v2/destinations/yandex_metrica_offline_events/utils.js new file mode 100644 index 0000000000..032b0b636d --- /dev/null +++ b/src/cdk/v2/destinations/yandex_metrica_offline_events/utils.js @@ -0,0 +1,51 @@ +/* eslint-disable no-param-reassign */ +const { InstrumentationError, isDefinedNotNullNotEmpty } = require('@rudderstack/integrations-lib'); +const moment = require('moment'); + +const setIdentifier = (data, identifierType, identifierValue) => { + const updatedData = data; + if (identifierType === 'ClientId') { + updatedData.ClientId = identifierValue; + } else if (identifierType === 'YCLID') { + updatedData.Yclid = identifierValue; + } else if (identifierType === 'UserId') { + updatedData.UserId = identifierValue; + } else { + throw new InstrumentationError( + 'Invalid identifier type passed in external Id. Valid types are ClientId, YCLID, UserId. Aborting!', + ); + } + return updatedData; +}; + +function isUnixTimestamp(datetime) { + if (moment.unix(datetime).isValid()) { + return datetime; + } + const unixTimestamp = moment(datetime).unix(); + if (moment.unix(unixTimestamp).isValid()) { + return unixTimestamp; + } + throw new InstrumentationError('Invalid timestamp. Aborting!'); +} + +const validateData = (data) => { + const { Price, DateTime } = data; + if (!isDefinedNotNullNotEmpty(data)) { + throw new InstrumentationError('No traits found in the payload. Aborting!'); + } + if (Price && typeof Price !== 'number') { + throw new InstrumentationError('Price can only be a numerical value. Aborting!'); + } + if (!isDefinedNotNullNotEmpty(DateTime)) { + throw new InstrumentationError('DateTime cannot be empty. Aborting!'); + } + data.DateTime = String(isUnixTimestamp(DateTime)); + return data; +}; + +module.exports = { + setIdentifier, + validateData, + isUnixTimestamp, +}; diff --git a/src/cdk/v2/handler.ts b/src/cdk/v2/handler.ts index edd14e7298..74ebb716e6 100644 --- a/src/cdk/v2/handler.ts +++ b/src/cdk/v2/handler.ts @@ -1,9 +1,9 @@ import { - WorkflowEngine, - WorkflowEngineFactory, - TemplateType, ExecutionBindings, StepOutput, + TemplateType, + WorkflowEngine, + WorkflowEngineFactory, } from '@rudderstack/workflow-engine'; import { FixMe } from '../../util/types'; @@ -11,12 +11,14 @@ import tags from '../../v0/util/tags'; import { getErrorInfo, + getPlatformBindingsPaths, getRootPathForDestination, getWorkflowPath, - getPlatformBindingsPaths, isCdkV2Destination, } from './utils'; +import logger from '../../logger'; + const defTags = { [tags.TAG_NAMES.IMPLEMENTATION]: tags.IMPLEMENTATIONS.CDK_V2, }; @@ -86,6 +88,8 @@ export async function processCdkV2Workflow( bindings: Record = {}, ) { try { + logger.debug(`Processing cdkV2 workflow`, { destType }); + const workflowEngine = await getCachedWorkflowEngine(destType, feature, bindings); return await executeWorkflow(workflowEngine, parsedEvent, requestMetadata); } catch (error) { diff --git a/src/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index d1b2b24de0..419c56d2c6 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -152,6 +152,28 @@ const DestCanonicalNames = { 'the trade desk', ], INTERCOM: ['INTERCOM', 'intercom', 'Intercom'], + GOOGLE_ADWORDS_REMARKETING_LISTS: [ + 'GOOGLE_ADWORDS_REMARKETING_LISTS', + 'google_adwords_remarketing_lists', + 'Google Adwords Remarketing Lists', + 'google adwords remarketing lists', + ], + GOOGLE_ADWORDS_OFFLINE_CONVERSIONS: [ + 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + 'google_adwords_offline_conversions', + 'Google Adwords Offline Conversions', + 'google adwords offline conversions', + ], + koala: ['Koala', 'koala', 'KOALA'], + bloomreach: ['Bloomreach', 'bloomreach', 'BLOOMREACH'], + KLAVIYO_BULK_UPLOAD: [ + 'klaviyo bulk upload', + 'klaviyo_bulk_upload', + 'klaviyoBulkUpload', + 'Klaviyo Bulk Upload', + 'klaviyobulkupload', + ], + emarsys: ['EMARSYS', 'Emarsys', 'emarsys'], }; module.exports = { DestHandlerMap, DestCanonicalNames }; diff --git a/src/controllers/bulkUpload.ts b/src/controllers/bulkUpload.ts index babb8b6db1..cb0bcfed3c 100644 --- a/src/controllers/bulkUpload.ts +++ b/src/controllers/bulkUpload.ts @@ -1,14 +1,14 @@ /* eslint-disable global-require, import/no-dynamic-require, @typescript-eslint/no-unused-vars */ import { client as errNotificationClient } from '../util/errorNotifier'; -import logger from '../logger'; +import { + getDestFileUploadHandler, + getJobStatusHandler, + getPollStatusHandler, +} from '../util/fetchDestinationHandlers'; import { CatchErr, ContextBodySimple } from '../util/types'; +import logger from '../logger'; // TODO: To be refactored and redisgned -const getDestFileUploadHandler = (version, dest) => - require(`../${version}/destinations/${dest}/fileUpload`); -const getPollStatusHandler = (version, dest) => require(`../${version}/destinations/${dest}/poll`); -const getJobStatusHandler = (version, dest) => - require(`../${version}/destinations/${dest}/fetchJobStatus`); const ERROR_MESSAGE_PROCESSOR_STRING = 'Error occurred while processing payload.'; const getCommonMetadata = (ctx) => @@ -31,10 +31,7 @@ const getReqMetadata = (ctx) => { }; export const fileUpload = async (ctx) => { - logger.debug( - 'Native(Bulk-Upload): Request to transformer:: /fileUpload route', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Bulk-Upload): Request to transformer:: /fileUpload route', ctx.request.body); const getReqMetadataFileUpload = () => { try { const reqBody = ctx.request.body; @@ -69,18 +66,12 @@ export const fileUpload = async (ctx) => { }); } ctx.body = response; - logger.debug( - 'Native(Bulk-Upload): Response from transformer:: /fileUpload route', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Bulk-Upload): Response from transformer:: /fileUpload route', ctx.body); return ctx.body; }; export const pollStatus = async (ctx) => { - logger.debug( - 'Native(Bulk-Upload): Request to transformer:: /pollStatus route', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Bulk-Upload): Request to transformer:: /pollStatus route', ctx.request.body); const { destType }: ContextBodySimple = ctx.request.body; const destFileUploadHandler = getPollStatusHandler('v0', destType.toLowerCase()); @@ -104,17 +95,14 @@ export const pollStatus = async (ctx) => { }); } ctx.body = response; - logger.debug( - 'Native(Bulk-Upload): Request from transformer:: /pollStatus route', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Bulk-Upload): Request from transformer:: /pollStatus route', ctx.body); return ctx.body; }; export const getWarnJobStatus = async (ctx) => { logger.debug( 'Native(Bulk-Upload): Request to transformer:: /getWarningJobs route', - JSON.stringify(ctx.request.body), + ctx.request.body, ); const { destType }: ContextBodySimple = ctx.request.body; @@ -140,17 +128,14 @@ export const getWarnJobStatus = async (ctx) => { }); } ctx.body = response; - logger.debug( - 'Native(Bulk-Upload): Request from transformer:: /getWarningJobs route', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Bulk-Upload): Request from transformer:: /getWarningJobs route', ctx.body); return ctx.body; }; export const getFailedJobStatus = async (ctx) => { logger.debug( 'Native(Bulk-Upload): Request to transformer:: /getFailedJobs route', - JSON.stringify(ctx.request.body), + ctx.request.body, ); const { destType }: ContextBodySimple = ctx.request.body; @@ -176,9 +161,6 @@ export const getFailedJobStatus = async (ctx) => { }); } ctx.body = response; - logger.debug( - 'Native(Bulk-Upload): Request from transformer:: /getFailedJobs route', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Bulk-Upload): Request from transformer:: /getFailedJobs route', ctx.body); return ctx.body; }; diff --git a/src/controllers/delivery.ts b/src/controllers/delivery.ts index 4334dc33b2..9e06b23f4d 100644 --- a/src/controllers/delivery.ts +++ b/src/controllers/delivery.ts @@ -1,28 +1,28 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable sonarjs/no-duplicate-string */ -import { Context } from 'koa'; import { isDefinedAndNotNullAndNotEmpty } from '@rudderstack/integrations-lib'; +import { Context } from 'koa'; +import { ServiceSelector } from '../helpers/serviceSelector'; +import { DeliveryTestService } from '../services/delivertTest/deliveryTest'; +import { DestinationPostTransformationService } from '../services/destination/postTransformation'; import { MiscService } from '../services/misc'; import { - DeliveryV1Response, DeliveryV0Response, + DeliveryV1Response, ProcessorTransformationOutput, ProxyV0Request, ProxyV1Request, } from '../types/index'; -import { ServiceSelector } from '../helpers/serviceSelector'; -import { DeliveryTestService } from '../services/delivertTest/deliveryTest'; +import { FixMe } from '../util/types'; +import tags from '../v0/util/tags'; import { ControllerUtility } from './util'; import logger from '../logger'; -import { DestinationPostTransformationService } from '../services/destination/postTransformation'; -import tags from '../v0/util/tags'; -import { FixMe } from '../util/types'; const NON_DETERMINABLE = 'Non-determinable'; export class DeliveryController { public static async deliverToDestination(ctx: Context) { - logger.debug('Native(Delivery):: Request to transformer::', JSON.stringify(ctx.request.body)); + logger.debug('Native(Delivery):: Request to transformer for delivery::', ctx.request.body); let deliveryResponse: DeliveryV0Response; const requestMetadata = MiscService.getRequestMetadata(ctx); const deliveryRequest = ctx.request.body as ProxyV0Request; @@ -52,12 +52,12 @@ export class DeliveryController { ctx.body = { output: deliveryResponse }; ControllerUtility.deliveryPostProcess(ctx, deliveryResponse.status); - logger.debug('Native(Delivery):: Response from transformer::', JSON.stringify(ctx.body)); + logger.debug('Native(Delivery):: Response from transformer after delivery::', ctx.body); return ctx; } public static async deliverToDestinationV1(ctx: Context) { - logger.debug('Native(Delivery):: Request to transformer::', JSON.stringify(ctx.request.body)); + logger.debug('Native(Delivery):: Request to transformer for delivery::', ctx.request.body); let deliveryResponse: DeliveryV1Response; const requestMetadata = MiscService.getRequestMetadata(ctx); const deliveryRequest = ctx.request.body as ProxyV1Request; @@ -91,15 +91,12 @@ export class DeliveryController { ControllerUtility.deliveryPostProcess(ctx); } - logger.debug('Native(Delivery):: Response from transformer::', JSON.stringify(ctx.body)); + logger.debug('Native(Delivery):: Response from transformer::', ctx.body); return ctx; } public static async testDestinationDelivery(ctx: Context) { - logger.debug( - 'Native(Delivery-Test):: Request to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Delivery-Test):: Request to transformer::', ctx.request.body); const { destination }: { destination: string } = ctx.params; const { version }: { version: string } = ctx.params; const { @@ -117,7 +114,7 @@ export class DeliveryController { ); ctx.body = { output: response }; ControllerUtility.postProcess(ctx); - logger.debug('Native(Delivery-Test):: Response from transformer::', JSON.stringify(ctx.body)); + logger.debug('Native(Delivery-Test):: Response from transformer after delivery::', ctx.body); return ctx; } } diff --git a/src/controllers/destination.ts b/src/controllers/destination.ts index d8b3c94524..1b9d19c5af 100644 --- a/src/controllers/destination.ts +++ b/src/controllers/destination.ts @@ -1,29 +1,25 @@ import { Context } from 'koa'; -import { MiscService } from '../services/misc'; -import { DestinationPreTransformationService } from '../services/destination/preTransformation'; +import { ServiceSelector } from '../helpers/serviceSelector'; import { DestinationPostTransformationService } from '../services/destination/postTransformation'; +import { DestinationPreTransformationService } from '../services/destination/preTransformation'; +import { MiscService } from '../services/misc'; import { ProcessorTransformationRequest, - RouterTransformationRequest, ProcessorTransformationResponse, + RouterTransformationRequest, RouterTransformationResponse, } from '../types/index'; -import { ServiceSelector } from '../helpers/serviceSelector'; -import { ControllerUtility } from './util'; +import { DynamicConfigParser } from '../util/dynamicConfigParser'; import stats from '../util/stats'; -import logger from '../logger'; import { getIntegrationVersion } from '../util/utils'; -import tags from '../v0/util/tags'; -import { DynamicConfigParser } from '../util/dynamicConfigParser'; import { checkInvalidRtTfEvents } from '../v0/util'; +import tags from '../v0/util/tags'; +import { ControllerUtility } from './util'; +import logger from '../logger'; export class DestinationController { public static async destinationTransformAtProcessor(ctx: Context) { - const startTime = new Date(); - logger.debug( - 'Native(Process-Transform):: Requst to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Process-Transform):: Requst to transformer::', ctx.request.body); let resplist: ProcessorTransformationResponse[]; const requestMetadata = MiscService.getRequestMetadata(ctx); let events = ctx.request.body as ProcessorTransformationRequest[]; @@ -69,35 +65,16 @@ export class DestinationController { } ctx.body = resplist; ControllerUtility.postProcess(ctx); - logger.debug( - 'Native(Process-Transform):: Response from transformer::', - JSON.stringify(ctx.body), - ); stats.histogram('dest_transform_output_events', resplist.length, { destination, version, ...metaTags, }); - stats.timing('dest_transform_request_latency', startTime, { - destination, - feature: tags.FEATURES.PROCESSOR, - version, - ...metaTags, - }); - stats.increment('dest_transform_requests', { - destination, - version, - ...metaTags, - }); return ctx; } public static async destinationTransformAtRouter(ctx: Context) { - const startTime = new Date(); - logger.debug( - 'Native(Router-Transform):: Requst to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Router-Transform):: Requst to transformer::', ctx.request.body); const requestMetadata = MiscService.getRequestMetadata(ctx); const routerRequest = ctx.request.body as RouterTransformationRequest; const destination = routerRequest.destType; @@ -155,25 +132,11 @@ export class DestinationController { version: 'v0', ...metaTags, }); - logger.debug( - 'Native(Router-Transform):: Response from transformer::', - JSON.stringify(ctx.body), - ); - stats.timing('dest_transform_request_latency', startTime, { - destination, - version: 'v0', - feature: tags.FEATURES.ROUTER, - ...metaTags, - }); return ctx; } public static batchProcess(ctx: Context) { - logger.debug( - 'Native(Process-Transform-Batch):: Requst to transformer::', - JSON.stringify(ctx.request.body), - ); - const startTime = new Date(); + logger.debug('Native(Process-Transform-Batch):: Requst to transformer::', ctx.request.body); const requestMetadata = MiscService.getRequestMetadata(ctx); const routerRequest = ctx.request.body as RouterTransformationRequest; const destination = routerRequest.destType; @@ -204,15 +167,6 @@ export class DestinationController { ctx.body = [errResp]; } ControllerUtility.postProcess(ctx); - logger.debug( - 'Native(Process-Transform-Batch):: Response from transformer::', - JSON.stringify(ctx.body), - ); - stats.timing('dest_transform_request_latency', startTime, { - destination, - feature: tags.FEATURES.BATCH, - version: 'v0', - }); return ctx; } } diff --git a/src/controllers/regulation.ts b/src/controllers/regulation.ts index 318b5ed4e7..ecb55ff7a3 100644 --- a/src/controllers/regulation.ts +++ b/src/controllers/regulation.ts @@ -1,20 +1,15 @@ import { Context } from 'koa'; -import logger from '../logger'; -import { UserDeletionRequest, UserDeletionResponse } from '../types'; import { ServiceSelector } from '../helpers/serviceSelector'; -import tags from '../v0/util/tags'; -import stats from '../util/stats'; import { DestinationPostTransformationService } from '../services/destination/postTransformation'; +import { UserDeletionRequest, UserDeletionResponse } from '../types'; +import tags from '../v0/util/tags'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { CatchErr } from '../util/types'; +import logger from '../logger'; export class RegulationController { public static async deleteUsers(ctx: Context) { - logger.debug( - 'Native(Process-Transform):: Requst to transformer::', - JSON.stringify(ctx.request.body), - ); - const startTime = new Date(); + logger.debug('Native(Process-Transform):: Requst to transformer::', ctx.request.body); let rudderDestInfo: any; try { const rudderDestInfoHeader = ctx.get('x-rudder-dest-info'); @@ -50,10 +45,6 @@ export class RegulationController { ctx.body = [{ error, statusCode: 500 }] as UserDeletionResponse[]; // TODO: responses array length is always 1. Is that okay? ctx.status = 500; } - stats.timing('dest_transform_request_latency', startTime, { - feature: tags.FEATURES.USER_DELETION, - version: 'v0', - }); return ctx; } } diff --git a/src/controllers/source.ts b/src/controllers/source.ts index ef5483a756..bc4b77bd3d 100644 --- a/src/controllers/source.ts +++ b/src/controllers/source.ts @@ -1,16 +1,13 @@ import { Context } from 'koa'; -import { MiscService } from '../services/misc'; import { ServiceSelector } from '../helpers/serviceSelector'; +import { MiscService } from '../services/misc'; +import { SourcePostTransformationService } from '../services/source/postTransformation'; import { ControllerUtility } from './util'; import logger from '../logger'; -import { SourcePostTransformationService } from '../services/source/postTransformation'; export class SourceController { public static async sourceTransform(ctx: Context) { - logger.debug( - 'Native(Source-Transform):: Request to transformer::', - JSON.stringify(ctx.request.body), - ); + logger.debug('Native(Source-Transform):: Request to transformer::', ctx.request.body); const requestMetadata = MiscService.getRequestMetadata(ctx); const events = ctx.request.body as object[]; const { version, source }: { version: string; source: string } = ctx.params; @@ -34,10 +31,11 @@ export class SourceController { ctx.body = [resp]; } ControllerUtility.postProcess(ctx); - logger.debug( - 'Native(Source-Transform):: Response from transformer::', - JSON.stringify(ctx.body), - ); + logger.debug('Native(Source-Transform):: Response from transformer::', { + srcResponse: ctx.body, + version, + source, + }); return ctx; } } diff --git a/src/controllers/trackingPlan.ts b/src/controllers/trackingPlan.ts index 74e47e0ec9..e4802cfc4d 100644 --- a/src/controllers/trackingPlan.ts +++ b/src/controllers/trackingPlan.ts @@ -7,7 +7,7 @@ export class TrackingPlanController { const events = ctx.request.body; const requestSize = Number(ctx.request.get('content-length')); const reqParams = ctx.request.query; - const response = await TrackingPlanservice.validateTrackingPlan(events, requestSize, reqParams); + const response = await TrackingPlanservice.validate(events, requestSize, reqParams); ctx.body = response.body; ControllerUtility.postProcess(ctx, response.status); return ctx; diff --git a/src/controllers/userTransform.ts b/src/controllers/userTransform.ts index c344bd072a..47adb079fe 100644 --- a/src/controllers/userTransform.ts +++ b/src/controllers/userTransform.ts @@ -1,28 +1,29 @@ import { Context } from 'koa'; -import { ProcessorTransformationRequest, UserTransformationServiceResponse } from '../types/index'; import { UserTransformService } from '../services/userTransform'; -import logger from '../logger'; +import { ProcessorTransformationRequest, UserTransformationServiceResponse } from '../types/index'; import { - setupUserTransformHandler, extractLibraries, + setupUserTransformHandler, validateCode, } from '../util/customTransformer'; import { ControllerUtility } from './util'; +import logger from '../logger'; export class UserTransformController { public static async transform(ctx: Context) { logger.debug( '(User transform - router:/customTransform ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); + const requestSize = Number(ctx.request.get('content-length')); const events = ctx.request.body as ProcessorTransformationRequest[]; const processedRespone: UserTransformationServiceResponse = - await UserTransformService.transformRoutine(events, ctx.state.features); + await UserTransformService.transformRoutine(events, ctx.state.features, requestSize); ctx.body = processedRespone.transformedEvents; ControllerUtility.postProcess(ctx, processedRespone.retryStatus); logger.debug( '(User transform - router:/customTransform ):: Response from transformer', - JSON.stringify(ctx.response.body), + ctx.response.body, ); return ctx; } @@ -30,19 +31,20 @@ export class UserTransformController { public static async testTransform(ctx: Context) { logger.debug( '(User transform - router:/transformation/test ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); - const { events, trRevCode, libraryVersionIDs = [] } = ctx.request.body as any; + const { events, trRevCode, libraryVersionIDs = [], credentials = [] } = ctx.request.body as any; const response = await UserTransformService.testTransformRoutine( events, trRevCode, libraryVersionIDs, + credentials, ); ctx.body = response.body; ControllerUtility.postProcess(ctx, response.status); logger.debug( '(User transform - router:/transformation/test ):: Response from transformer', - JSON.stringify(ctx.response.body), + ctx.response.body, ); return ctx; } @@ -50,7 +52,7 @@ export class UserTransformController { public static async testTransformLibrary(ctx: Context) { logger.debug( '(User transform - router:/transformationLibrary/test ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); try { const { code, language = 'javascript' } = ctx.request.body as any; @@ -65,7 +67,7 @@ export class UserTransformController { } logger.debug( '(User transform - router:/transformationLibrary/test ):: Response from transformer', - JSON.stringify(ctx.response.body), + ctx.response.body, ); return ctx; } @@ -73,7 +75,7 @@ export class UserTransformController { public static async testTransformSethandle(ctx: Context) { logger.debug( '(User transform - router:/transformation/sethandle ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); try { const { trRevCode, libraryVersionIDs = [] } = ctx.request.body as any; @@ -95,7 +97,7 @@ export class UserTransformController { } logger.debug( '(User transform - router:/transformation/sethandle ):: Response from transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); return ctx; } @@ -103,7 +105,7 @@ export class UserTransformController { public static async extractLibhandle(ctx: Context) { logger.debug( '(User transform - router:/extractLibs ):: Request to transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); try { const { @@ -133,7 +135,7 @@ export class UserTransformController { } logger.debug( '(User transform - router:/extractLibs ):: Response from transformer', - JSON.stringify(ctx.request.body), + ctx.request.body, ); return ctx; } diff --git a/src/features.json b/src/features.json index 5460111a22..58af795a77 100644 --- a/src/features.json +++ b/src/features.json @@ -66,7 +66,13 @@ "REDDIT": true, "THE_TRADE_DESK": true, "INTERCOM": true, - "NINETAILED": true + "NINETAILED": true, + "KOALA": true, + "LINKEDIN_ADS": true, + "BLOOMREACH": true, + "MOVABLE_INK": true, + "EMARSYS": true, + "KODDI": true }, "regulations": [ "BRAZE", @@ -80,8 +86,9 @@ "ENGAGE", "CUSTIFY", "SENDGRID", - "SPRIG" + "SPRIG", + "EMARSYS" ], "supportSourceTransformV1": true, - "supportTransformerProxyV1": false + "supportTransformerProxyV1": true } diff --git a/src/index.ts b/src/index.ts index 36f32f1aed..c5de26c776 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,16 +1,19 @@ +import dotenv from 'dotenv'; +import gracefulShutdown from 'http-graceful-shutdown'; import Koa from 'koa'; import bodyParser from 'koa-bodyparser'; -import gracefulShutdown from 'http-graceful-shutdown'; -import dotenv from 'dotenv'; -import logger from './logger'; -import cluster from './util/cluster'; +import { addRequestSizeMiddleware, addStatMiddleware, initPyroscope } from './middleware'; +import { addSwaggerRoutes, applicationRoutes } from './routes'; import { metricsRouter } from './routes/metricsRouter'; -import { addStatMiddleware, addRequestSizeMiddleware, initPyroscope } from './middleware'; -import { logProcessInfo } from './util/utils'; -import { applicationRoutes, addSwaggerRoutes } from './routes'; +import cluster from './util/cluster'; import { RedisDB } from './util/redis/redisConnector'; +import { logProcessInfo } from './util/utils'; dotenv.config(); + +// eslint-disable-next-line import/first +import logger from './logger'; + const clusterEnabled = process.env.CLUSTER_ENABLED !== 'false'; const port = parseInt(process.env.PORT ?? '9090', 10); const metricsPort = parseInt(process.env.METRICS_PORT || '9091', 10); @@ -32,7 +35,6 @@ app.use( addRequestSizeMiddleware(app); addSwaggerRoutes(app); -logger.info('Using new routes'); applicationRoutes(app); function finalFunction() { diff --git a/src/interfaces/DestinationService.ts b/src/interfaces/DestinationService.ts index 4947089b5d..b45d9a427c 100644 --- a/src/interfaces/DestinationService.ts +++ b/src/interfaces/DestinationService.ts @@ -1,14 +1,14 @@ import { DeliveryV0Response, + DeliveryV1Response, MetaTransferObject, ProcessorTransformationRequest, ProcessorTransformationResponse, + ProxyRequest, RouterTransformationRequestData, RouterTransformationResponse, UserDeletionRequest, UserDeletionResponse, - ProxyRequest, - DeliveryV1Response, } from '../types/index'; export interface DestinationService { diff --git a/src/legacy/router.js b/src/legacy/router.js index 74cd1d7ce3..6e74422cba 100644 --- a/src/legacy/router.js +++ b/src/legacy/router.js @@ -542,9 +542,7 @@ if (startDestTransformer) { (event) => `${event.metadata.destinationId}_${event.metadata.sourceId}`, ); } - stats.counter('user_transform_function_group_size', Object.entries(groupedEvents).length, { - processSessions, - }); + stats.counter('user_transform_function_group_size', Object.entries(groupedEvents).length, {}); let ctxStatusCode = 200; const transformedEvents = []; @@ -649,13 +647,10 @@ if (startDestTransformer) { ctx.status = ctxStatusCode; ctx.set('apiVersion', API_VERSION); - stats.timing('user_transform_request_latency', startTime, { - processSessions, - }); - stats.increment('user_transform_requests', { processSessions }); - stats.histogram('user_transform_output_events', transformedEvents.length, { - processSessions, - }); + stats.timing('user_transform_request_latency', startTime, {}); + stats.timingSummary('user_transform_request_latency_summary', startTime, {}); + stats.increment('user_transform_requests', {}); + stats.histogram('user_transform_output_events', transformedEvents.length, {}); }); } } diff --git a/src/logger.js b/src/logger.js index 0685df3387..6daff56c67 100644 --- a/src/logger.js +++ b/src/logger.js @@ -1,38 +1,162 @@ /* istanbul ignore file */ +const { LOGLEVELS, structuredLogger } = require('@rudderstack/integrations-lib'); -const levelDebug = 0; // Most verbose logging level -const levelInfo = 1; // Logs about state of the application -const levelWarn = 2; // Logs about warnings which dont immediately halt the application -const levelError = 3; // Logs about errors which dont immediately halt the application -// any value greater than levelError will work as levelNone +// LOGGER_IMPL can be `console` or `winston` +const loggerImpl = process.env.LOGGER_IMPL ?? 'winston'; -let logLevel = process.env.LOG_LEVEL ? parseInt(process.env.LOG_LEVEL, 10) : levelInfo; +let logLevel = process.env.LOG_LEVEL ?? 'error'; + +const logger = structuredLogger({ + level: logLevel, + fillExcept: [ + 'destinationId', + 'sourceId', + 'destinationType', + 'workspaceId', + 'module', + 'implementation', + 'feature', + 'destType', + ], +}); + +const getLogger = () => { + switch (loggerImpl) { + case 'winston': + return logger; + case 'console': + return console; + } +}; const setLogLevel = (level) => { + const logger = getLogger(); logLevel = level || logLevel; + logger?.setLogLevel(logLevel); +}; + +/** + * obtains the metadata for logging + * + * @param {*} metadata + * @returns { destinationId:string, sourceId:string, workspaceId: string, destType:string, module:string, implementation:string, feature:string } + */ +const getLogMetadata = (metadata) => { + let reqMeta = metadata; + if (Array.isArray(metadata)) { + [reqMeta] = metadata; + } + const destType = reqMeta?.destType || reqMeta?.destinationType; + return { + ...(reqMeta?.destinationId && { destinationId: reqMeta.destinationId }), + ...(reqMeta?.sourceId && { sourceId: reqMeta.sourceId }), + ...(reqMeta?.workspaceId && { workspaceId: reqMeta.workspaceId }), + ...(destType && { destType }), + ...(reqMeta?.module && { module: reqMeta.module }), + ...(reqMeta?.implementation && { implementation: reqMeta.implementation }), + ...(reqMeta?.feature && { feature: reqMeta.feature }), + }; +}; + +const formLogArgs = (args) => { + let msg = ''; + let otherArgs = []; + args.forEach((arg) => { + if (typeof arg !== 'object') { + msg += ' ' + arg; + return; + } + otherArgs.push(arg); + }); + return [msg, ...otherArgs]; +}; + +/** + * Perform logging operation on logMethod passed + * + * **Good practices**: + * - Do not have more than one array args in logger + * @param {*} logMethod + * - instance method reference + * - The logger should implement all of debug/info/warn/error methods + * @param {*} logArgs + * - the arguments that needs to be passed to logger instance method + */ +const log = (logMethod, logArgs) => { + const [message, ...args] = formLogArgs(logArgs); + const [logInfo, ...otherArgs] = args; + if (logInfo) { + const { metadata, ...otherLogInfoArgs } = logInfo; + if (Array.isArray(metadata)) { + metadata.forEach((m) => { + logMethod( + message, + { + ...getLogMetadata(m), + ...otherLogInfoArgs, + }, + ...otherArgs, + ); + }); + return; + } + logMethod( + message, + { + ...getLogMetadata(metadata), + ...otherLogInfoArgs, + }, + ...otherArgs, + ); + return; + } + logMethod(message); }; const debug = (...args) => { - if (levelDebug >= logLevel) { - console.debug(...args); + const logger = getLogger(); + if (LOGLEVELS.debug <= LOGLEVELS[logLevel]) { + log(logger.debug, args); } }; const info = (...args) => { - if (levelInfo >= logLevel) { - console.info(...args); + const logger = getLogger(); + if (LOGLEVELS.info <= LOGLEVELS[logLevel]) { + log(logger.info, args); } }; const warn = (...args) => { - if (levelWarn >= logLevel) { - console.warn(...args); + const logger = getLogger(); + if (LOGLEVELS.warn <= LOGLEVELS[logLevel]) { + log(logger.warn, args); } }; const error = (...args) => { - if (levelError >= logLevel) { - console.error(...args); + const logger = getLogger(); + if (LOGLEVELS.error <= LOGLEVELS[logLevel]) { + log(logger.error, args); + } +}; + +const requestLog = (identifierMsg, { metadata, requestDetails: { url, body, method } }) => { + const logger = getLogger(); + if (LOGLEVELS[logLevel] === LOGLEVELS.warn) { + const reqLogArgs = [identifierMsg, { metadata, url, body, method }]; + log(logger.warn, reqLogArgs); + } +}; + +const responseLog = ( + identifierMsg, + { metadata, responseDetails: { response: body, status, headers } }, +) => { + const logger = getLogger(); + if (LOGLEVELS[logLevel] === LOGLEVELS.warn) { + const resLogArgs = [identifierMsg, { metadata, body, status, headers }]; + log(logger.warn, resLogArgs); } }; @@ -42,8 +166,11 @@ module.exports = { warn, error, setLogLevel, - levelDebug, - levelInfo, - levelWarn, - levelError, + // levelDebug, + // levelInfo, + // levelWarn, + // levelError, + responseLog, + getLogMetadata, + requestLog, }; diff --git a/src/middleware.js b/src/middleware.js index 53aabc90e3..543b3af8d1 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -1,4 +1,5 @@ const Pyroscope = require('@pyroscope/nodejs'); +const { getDestTypeFromContext } = require('@rudderstack/integrations-lib'); const stats = require('./util/stats'); function initPyroscope() { @@ -26,6 +27,7 @@ function durationMiddleware() { method: ctx.method, code: ctx.status, route: ctx.request.url, + destType: getDestTypeFromContext(ctx), }; stats.timing('http_request_duration', startTime, labels); }; diff --git a/src/services/comparator.ts b/src/services/comparator.ts index 36cb0ebd5a..0e28339797 100644 --- a/src/services/comparator.ts +++ b/src/services/comparator.ts @@ -14,10 +14,10 @@ import { UserDeletionRequest, UserDeletionResponse, } from '../types'; -import tags from '../v0/util/tags'; +import { CommonUtils } from '../util/common'; import stats from '../util/stats'; +import tags from '../v0/util/tags'; import logger from '../logger'; -import { CommonUtils } from '../util/common'; const NS_PER_SEC = 1e9; diff --git a/src/services/destination/__tests__/nativeIntegration.test.ts b/src/services/destination/__tests__/nativeIntegration.test.ts index 59c8b41881..3ec3222b9d 100644 --- a/src/services/destination/__tests__/nativeIntegration.test.ts +++ b/src/services/destination/__tests__/nativeIntegration.test.ts @@ -1,11 +1,11 @@ -import { NativeIntegrationDestinationService } from '../nativeIntegration'; -import { DestinationPostTransformationService } from '../postTransformation'; +import { FetchHandler } from '../../../helpers/fetchHandlers'; import { - ProcessorTransformationRequest, ProcessorTransformationOutput, + ProcessorTransformationRequest, ProcessorTransformationResponse, } from '../../../types/index'; -import { FetchHandler } from '../../../helpers/fetchHandlers'; +import { NativeIntegrationDestinationService } from '../nativeIntegration'; +import { DestinationPostTransformationService } from '../postTransformation'; afterEach(() => { jest.clearAllMocks(); diff --git a/src/services/destination/__tests__/postTransformation.test.ts b/src/services/destination/__tests__/postTransformation.test.ts index f961dcbce7..050ae57b7b 100644 --- a/src/services/destination/__tests__/postTransformation.test.ts +++ b/src/services/destination/__tests__/postTransformation.test.ts @@ -1,4 +1,4 @@ -import { MetaTransferObject, ProcessorTransformationRequest } from '../../../types/index'; +import { MetaTransferObject } from '../../../types/index'; import { DestinationPostTransformationService } from '../postTransformation'; import { ProcessorTransformationResponse } from '../../../types'; diff --git a/src/services/destination/cdkV2Integration.ts b/src/services/destination/cdkV2Integration.ts index c18a5cd936..a91bc5674b 100644 --- a/src/services/destination/cdkV2Integration.ts +++ b/src/services/destination/cdkV2Integration.ts @@ -1,27 +1,27 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable class-methods-use-this */ -import groupBy from 'lodash/groupBy'; import { TransformationError } from '@rudderstack/integrations-lib'; +import groupBy from 'lodash/groupBy'; import { processCdkV2Workflow } from '../../cdk/v2/handler'; import { DestinationService } from '../../interfaces/DestinationService'; import { DeliveryV0Response, + DeliveryV1Response, ErrorDetailer, MetaTransferObject, + ProcessorTransformationOutput, ProcessorTransformationRequest, ProcessorTransformationResponse, + ProxyRequest, RouterTransformationRequestData, RouterTransformationResponse, - ProcessorTransformationOutput, UserDeletionRequest, UserDeletionResponse, - ProxyRequest, - DeliveryV1Response, } from '../../types/index'; -import tags from '../../v0/util/tags'; -import { DestinationPostTransformationService } from './postTransformation'; import stats from '../../util/stats'; import { CatchErr } from '../../util/types'; +import tags from '../../v0/util/tags'; +import { DestinationPostTransformationService } from './postTransformation'; export class CDKV2DestinationService implements DestinationService { public init() {} @@ -59,6 +59,13 @@ export class CDKV2DestinationService implements DestinationService { // TODO: Change the promise type const respList: ProcessorTransformationResponse[][] = await Promise.all( events.map(async (event) => { + const metaTo = this.getTags( + destinationType, + event.metadata.destinationId, + event.metadata.workspaceId, + tags.FEATURES.PROCESSOR, + ); + metaTo.metadata = event.metadata; try { const transformedPayloads: | ProcessorTransformationOutput @@ -68,7 +75,6 @@ export class CDKV2DestinationService implements DestinationService { tags.FEATURES.PROCESSOR, requestMetadata, ); - stats.increment('event_transform_success', { destType: destinationType, module: tags.MODULES.DESTINATION, @@ -85,13 +91,6 @@ export class CDKV2DestinationService implements DestinationService { undefined, ); } catch (error: CatchErr) { - const metaTo = this.getTags( - destinationType, - event.metadata.destinationId, - event.metadata.workspaceId, - tags.FEATURES.PROCESSOR, - ); - metaTo.metadata = event.metadata; const erroredResp = DestinationPostTransformationService.handleProcessorTransformFailureEvents( error, diff --git a/src/services/destination/nativeIntegration.ts b/src/services/destination/nativeIntegration.ts index 2bb82fc602..38a27ea71d 100644 --- a/src/services/destination/nativeIntegration.ts +++ b/src/services/destination/nativeIntegration.ts @@ -1,31 +1,31 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable sonarjs/no-duplicate-string */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import groupBy from 'lodash/groupBy'; import cloneDeep from 'lodash/cloneDeep'; +import groupBy from 'lodash/groupBy'; +import networkHandlerFactory from '../../adapters/networkHandlerFactory'; +import { FetchHandler } from '../../helpers/fetchHandlers'; import { DestinationService } from '../../interfaces/DestinationService'; import { + DeliveryJobState, DeliveryV0Response, + DeliveryV1Response, ErrorDetailer, MetaTransferObject, + ProcessorTransformationOutput, ProcessorTransformationRequest, ProcessorTransformationResponse, + ProxyRequest, + ProxyV0Request, + ProxyV1Request, RouterTransformationRequestData, RouterTransformationResponse, - ProcessorTransformationOutput, UserDeletionRequest, UserDeletionResponse, - ProxyRequest, - ProxyV0Request, - ProxyV1Request, - DeliveryV1Response, - DeliveryJobState, } from '../../types/index'; -import { DestinationPostTransformationService } from './postTransformation'; -import networkHandlerFactory from '../../adapters/networkHandlerFactory'; -import { FetchHandler } from '../../helpers/fetchHandlers'; -import tags from '../../v0/util/tags'; import stats from '../../util/stats'; +import tags from '../../v0/util/tags'; +import { DestinationPostTransformationService } from './postTransformation'; export class NativeIntegrationDestinationService implements DestinationService { public init() {} @@ -63,6 +63,13 @@ export class NativeIntegrationDestinationService implements DestinationService { const destHandler = FetchHandler.getDestHandler(destinationType, version); const respList: ProcessorTransformationResponse[][] = await Promise.all( events.map(async (event) => { + const metaTO = this.getTags( + destinationType, + event.metadata?.destinationId, + event.metadata?.workspaceId, + tags.FEATURES.PROCESSOR, + ); + metaTO.metadata = event.metadata; try { const transformedPayloads: | ProcessorTransformationOutput @@ -73,13 +80,6 @@ export class NativeIntegrationDestinationService implements DestinationService { destHandler, ); } catch (error: any) { - const metaTO = this.getTags( - destinationType, - event.metadata?.destinationId, - event.metadata?.workspaceId, - tags.FEATURES.PROCESSOR, - ); - metaTO.metadata = event.metadata; const erroredResp = DestinationPostTransformationService.handleProcessorTransformFailureEvents( error, @@ -152,6 +152,13 @@ export class NativeIntegrationDestinationService implements DestinationService { ); const groupedEvents: RouterTransformationRequestData[][] = Object.values(allDestEvents); const response = groupedEvents.map((destEvents) => { + const metaTO = this.getTags( + destinationType, + destEvents[0].metadata.destinationId, + destEvents[0].metadata.workspaceId, + tags.FEATURES.BATCH, + ); + metaTO.metadatas = events.map((event) => event.metadata); try { const destBatchedRequests: RouterTransformationResponse[] = destHandler.batch( destEvents, @@ -159,13 +166,6 @@ export class NativeIntegrationDestinationService implements DestinationService { ); return destBatchedRequests; } catch (error: any) { - const metaTO = this.getTags( - destinationType, - destEvents[0].metadata.destinationId, - destEvents[0].metadata.workspaceId, - tags.FEATURES.BATCH, - ); - metaTO.metadatas = events.map((event) => event.metadata); const errResp = DestinationPostTransformationService.handleBatchTransformFailureEvents( error, metaTO, @@ -201,6 +201,7 @@ export class NativeIntegrationDestinationService implements DestinationService { destinationResponse: processedProxyResponse, rudderJobMetadata, destType: destinationType, + destinationRequest: deliveryRequest, }; let responseProxy = networkHandler.responseHandler(responseParams); // Adaption Logic for V0 to V1 @@ -264,6 +265,7 @@ export class NativeIntegrationDestinationService implements DestinationService { error: `${destType}: Doesn't support deletion of users`, } as UserDeletionResponse; } + const metaTO = this.getTags(destType, 'unknown', 'unknown', tags.FEATURES.USER_DELETION); try { const result: UserDeletionResponse = await destUserDeletionHandler.processDeleteUsers({ ...request, @@ -276,7 +278,6 @@ export class NativeIntegrationDestinationService implements DestinationService { }); return result; } catch (error: any) { - const metaTO = this.getTags(destType, 'unknown', 'unknown', tags.FEATURES.USER_DELETION); return DestinationPostTransformationService.handleUserDeletionFailureEvents( error, metaTO, diff --git a/src/services/destination/postTransformation.ts b/src/services/destination/postTransformation.ts index 161547683b..7ab0d96af8 100644 --- a/src/services/destination/postTransformation.ts +++ b/src/services/destination/postTransformation.ts @@ -1,24 +1,30 @@ /* eslint-disable no-param-reassign */ +import { PlatformError } from '@rudderstack/integrations-lib'; import cloneDeep from 'lodash/cloneDeep'; -import isObject from 'lodash/isObject'; import isEmpty from 'lodash/isEmpty'; -import { PlatformError } from '@rudderstack/integrations-lib'; +import isObject from 'lodash/isObject'; import { + DeliveryJobState, + DeliveryV0Response, + DeliveryV1Response, + MetaTransferObject, + ProcessorTransformationOutput, ProcessorTransformationRequest, ProcessorTransformationResponse, RouterTransformationResponse, - ProcessorTransformationOutput, - DeliveryV0Response, - MetaTransferObject, UserDeletionResponse, - DeliveryV1Response, - DeliveryJobState, } from '../../types/index'; -import { generateErrorObject } from '../../v0/util'; -import { ErrorReportingService } from '../errorReporting'; -import tags from '../../v0/util/tags'; import stats from '../../util/stats'; import { FixMe } from '../../util/types'; +import { generateErrorObject } from '../../v0/util'; +import tags from '../../v0/util/tags'; +import { ErrorReportingService } from '../errorReporting'; +import logger from '../../logger'; + +const defaultErrorMessages = { + router: '[Router Transform] Error occurred while processing the payload.', + delivery: '[Delivery] Error occured while processing payload', +} as const; export class DestinationPostTransformationService { public static handleProcessorTransformSucessEvents( @@ -62,6 +68,10 @@ export class DestinationPostTransformationService { error: errObj.message || '[Processor Transform] Error occurred while processing the payload.', statTags: errObj.statTags, } as ProcessorTransformationResponse; + logger.error( + errObj.message || '[Processor Transform] Error occurred while processing the payload.', + metaTo.errorDetails, + ); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } @@ -99,6 +109,7 @@ export class DestinationPostTransformationService { ...resp.statTags, ...metaTo.errorDetails, }; + logger.error(resp.error || defaultErrorMessages.router, metaTo.errorDetails); stats.increment('event_transform_failure', metaTo.errorDetails); } else { stats.increment('event_transform_success', { @@ -124,9 +135,10 @@ export class DestinationPostTransformationService { metadata: metaTo.metadatas, batched: false, statusCode: errObj.status, - error: errObj.message || '[Router Transform] Error occurred while processing the payload.', + error: errObj.message || defaultErrorMessages.router, statTags: errObj.statTags, } as RouterTransformationResponse; + logger.error(errObj.message || defaultErrorMessages.router, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); stats.increment('event_transform_failure', metaTo.errorDetails); return resp; @@ -141,9 +153,10 @@ export class DestinationPostTransformationService { metadata: metaTo.metadatas, batched: false, statusCode: 500, // for batch we should consider code error hence keeping retryable - error: errObj.message || '[Batch Transform] Error occurred while processing payload.', + error: errObj.message || defaultErrorMessages.delivery, statTags: errObj.statTags, } as RouterTransformationResponse; + logger.error(error as string, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } @@ -174,6 +187,7 @@ export class DestinationPostTransformationService { const errObj = generateErrorObject(error, metaTo.errorDetails, false); const metadataArray = metaTo.metadatas; if (!Array.isArray(metadataArray)) { + logger.error('Proxy v1 endpoint error : metadataArray is not an array', metaTo.errorDetails); // Panic throw new PlatformError('Proxy v1 endpoint error : metadataArray is not an array'); } @@ -182,7 +196,7 @@ export class DestinationPostTransformationService { error: JSON.stringify(error.destinationResponse?.response) || errObj.message || - '[Delivery] Error occured while processing payload', + defaultErrorMessages.delivery, statusCode: errObj.status, metadata, } as DeliveryJobState; @@ -198,7 +212,7 @@ export class DestinationPostTransformationService { authErrorCategory: errObj.authErrorCategory, }), } as DeliveryV1Response; - + logger.error(errObj.message, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } @@ -216,6 +230,7 @@ export class DestinationPostTransformationService { authErrorCategory: errObj.authErrorCategory, }), } as UserDeletionResponse; + logger.error(errObj.message, metaTo.errorDetails); ErrorReportingService.reportError(error, metaTo.errorContext, resp); return resp; } diff --git a/src/services/misc.ts b/src/services/misc.ts index e0953d08bf..09051edeec 100644 --- a/src/services/misc.ts +++ b/src/services/misc.ts @@ -1,10 +1,10 @@ /* eslint-disable global-require, import/no-dynamic-require */ import fs from 'fs'; -import path from 'path'; import { Context } from 'koa'; +import path from 'path'; import { DestHandlerMap } from '../constants/destinationCanonicalNames'; -import { Metadata } from '../types'; import { getCPUProfile, getHeapProfile } from '../middleware'; +import { Metadata } from '../types'; export class MiscService { public static getDestHandler(dest: string, version: string) { diff --git a/src/services/source/__tests__/nativeIntegration.test.ts b/src/services/source/__tests__/nativeIntegration.test.ts index bb40438811..a2a5af041e 100644 --- a/src/services/source/__tests__/nativeIntegration.test.ts +++ b/src/services/source/__tests__/nativeIntegration.test.ts @@ -1,8 +1,8 @@ +import { FetchHandler } from '../../../helpers/fetchHandlers'; +import { RudderMessage, SourceTransformationResponse } from '../../../types/index'; +import stats from '../../../util/stats'; import { NativeIntegrationSourceService } from '../nativeIntegration'; import { SourcePostTransformationService } from '../postTransformation'; -import { SourceTransformationResponse, RudderMessage } from '../../../types/index'; -import stats from '../../../util/stats'; -import { FetchHandler } from '../../../helpers/fetchHandlers'; afterEach(() => { jest.clearAllMocks(); diff --git a/src/services/source/nativeIntegration.ts b/src/services/source/nativeIntegration.ts index 6eaef2f835..a4f26d068a 100644 --- a/src/services/source/nativeIntegration.ts +++ b/src/services/source/nativeIntegration.ts @@ -1,3 +1,4 @@ +import { FetchHandler } from '../../helpers/fetchHandlers'; import { SourceService } from '../../interfaces/SourceService'; import { ErrorDetailer, @@ -5,11 +6,11 @@ import { RudderMessage, SourceTransformationResponse, } from '../../types/index'; +import stats from '../../util/stats'; import { FixMe } from '../../util/types'; -import { SourcePostTransformationService } from './postTransformation'; -import { FetchHandler } from '../../helpers/fetchHandlers'; import tags from '../../v0/util/tags'; -import stats from '../../util/stats'; +import { SourcePostTransformationService } from './postTransformation'; +import logger from '../../logger'; export class NativeIntegrationSourceService implements SourceService { public getTags(): MetaTransferObject { @@ -33,6 +34,7 @@ export class NativeIntegrationSourceService implements SourceService { _requestMetadata: NonNullable, ): Promise { const sourceHandler = FetchHandler.getSourceHandler(sourceType, version); + const metaTO = this.getTags(); const respList: SourceTransformationResponse[] = await Promise.all( sourceEvents.map(async (sourceEvent) => { try { @@ -40,11 +42,13 @@ export class NativeIntegrationSourceService implements SourceService { await sourceHandler.process(sourceEvent); return SourcePostTransformationService.handleSuccessEventsSource(respEvents); } catch (error: FixMe) { - const metaTO = this.getTags(); stats.increment('source_transform_errors', { source: sourceType, version, }); + logger.debug(`Error during source Transform: ${error}`, { + ...logger.getLogMetadata(metaTO.errorDetails), + }); return SourcePostTransformationService.handleFailureEventsSource(error, metaTO); } }), diff --git a/src/services/trackingPlan.ts b/src/services/trackingPlan.ts index 2e68df55e9..93b6ee11ff 100644 --- a/src/services/trackingPlan.ts +++ b/src/services/trackingPlan.ts @@ -1,88 +1,87 @@ import logger from '../logger'; import { RetryRequestError, RespStatusError, constructValidationErrors } from '../util/utils'; -import { getMetadata } from '../v0/util'; +import { getMetadata, getTrackingPlanMetadata } from '../v0/util'; import eventValidator from '../util/eventValidation'; import stats from '../util/stats'; +import { HTTP_STATUS_CODES } from '../v0/util/constant'; export class TrackingPlanservice { - public static async validateTrackingPlan(events, requestSize, reqParams) { - const requestStartTime = new Date(); + public static async validate(events, requestSize, reqParams) { + const startTime = Date.now(); const respList: any[] = []; - const metaTags = events[0].metadata ? getMetadata(events[0].metadata) : {}; + const metaTags = events.length && events[0].metadata ? getMetadata(events[0].metadata) : {}; + const tpTags: any = + events.length && events[0].metadata ? getTrackingPlanMetadata(events[0].metadata) : {}; let ctxStatusCode = 200; for (let i = 0; i < events.length; i++) { + let eventValidationResponse: any; + let exceptionOccured = false; + const eventStartTime = Date.now(); const event = events[i]; - const eventStartTime = new Date(); + try { - const parsedEvent = event; - parsedEvent.request = { query: reqParams }; - const hv = await eventValidator.handleValidation(parsedEvent); - if (hv['dropEvent']) { - respList.push({ - output: event.message, - metadata: event.metadata, - statusCode: 400, - validationErrors: hv['validationErrors'], - error: JSON.stringify(constructValidationErrors(hv['validationErrors'])), - }); - stats.counter('tp_violation_type', 1, { - violationType: hv['violationType'], - ...metaTags, - }); - } else { - respList.push({ - output: event.message, - metadata: event.metadata, - statusCode: 200, - validationErrors: hv['validationErrors'], - error: JSON.stringify(constructValidationErrors(hv['validationErrors'])), - }); - stats.counter('tp_propagated_events', 1, { - ...metaTags, - }); - } - } catch (error) { - const errMessage = `Error occurred while validating : ${error}`; - logger.error(errMessage); - let status = 200; + event.request = { query: reqParams }; + const validatedEvent = await eventValidator.handleValidation(event); + eventValidationResponse = { + output: event.message, + metadata: event.metadata, + statusCode: validatedEvent['dropEvent'] + ? HTTP_STATUS_CODES.BAD_REQUEST + : HTTP_STATUS_CODES.OK, + validationErrors: validatedEvent['validationErrors'], + error: JSON.stringify(constructValidationErrors(validatedEvent['validationErrors'])), + }; + } catch (error: any) { + logger.debug( + `Error occurred while validating event`, + 'event', + `${event.message?.event}::${event.message?.type}`, + 'trackingPlan', + `${tpTags?.trackingPlanId}`, + 'error', + error.message, + ); + + exceptionOccured = true; + // no need to process further if + // we have error of retry request error if (error instanceof RetryRequestError) { ctxStatusCode = error.statusCode; + break; } - if (error instanceof RespStatusError) { - status = error.statusCode; - } - respList.push({ + + eventValidationResponse = { output: event.message, metadata: event.metadata, - statusCode: status, + statusCode: error instanceof RespStatusError ? error.statusCode : HTTP_STATUS_CODES.OK, validationErrors: [], - error: errMessage, - }); - stats.counter('tp_errors', 1, { - ...metaTags, - workspaceId: event.metadata?.workspaceId, - trackingPlanId: event.metadata?.trackingPlanId, - }); + error: `Error occurred while validating: ${error}`, + }; } finally { - stats.timing('tp_event_latency', eventStartTime, { + // finally on every event, we need to + // capture the information related to the validates event + stats.timing('tp_event_validation_latency', eventStartTime, { ...metaTags, + ...tpTags, + status: eventValidationResponse.statusCode, + exception: exceptionOccured, }); } - } - stats.counter('tp_events_count', events.length, { - ...metaTags, - }); + respList.push(eventValidationResponse); + } - stats.histogram('tp_request_size', requestSize, { + stats.histogram('tp_batch_size', requestSize, { ...metaTags, + ...tpTags, }); - stats.timing('tp_request_latency', requestStartTime, { + // capture overall function latency + // with metadata tags + stats.histogram('tp_batch_validation_latency', (Date.now() - startTime) / 1000, { ...metaTags, - workspaceId: events[0]?.metadata?.workspaceId, - trackingPlanId: events[0]?.metadata?.trackingPlanId, + ...tpTags, }); return { body: respList, status: ctxStatusCode }; diff --git a/src/services/userTransform.ts b/src/services/userTransform.ts index bae833c86a..83e0c807d6 100644 --- a/src/services/userTransform.ts +++ b/src/services/userTransform.ts @@ -1,5 +1,6 @@ import groupBy from 'lodash/groupBy'; import isEmpty from 'lodash/isEmpty'; +import { isNil } from 'lodash'; import { userTransformHandler } from '../routerUtils'; import { UserTransformationLibrary, @@ -14,7 +15,7 @@ import { RetryRequestError, extractStackTraceUptoLastSubstringMatch, } from '../util/utils'; -import { getMetadata, isNonFuncObject } from '../v0/util'; +import { getMetadata, getTransformationMetadata, isNonFuncObject } from '../v0/util'; import { SUPPORTED_FUNC_NAMES } from '../util/ivmFactory'; import logger from '../logger'; import stats from '../util/stats'; @@ -28,6 +29,7 @@ export class UserTransformService { public static async transformRoutine( events: ProcessorTransformationRequest[], features: FeatureFlags = {}, + requestSize = 0, ): Promise { let retryStatus = 200; const groupedEvents: NonNullable = groupBy( @@ -66,6 +68,7 @@ export class UserTransformService { destinationId: eventsToProcess[0]?.metadata.destinationId, destinationType: eventsToProcess[0]?.metadata.destinationType, workspaceId: eventsToProcess[0]?.metadata.workspaceId, + transformationId: eventsToProcess[0]?.metadata.transformationId, messageIds, }; @@ -162,16 +165,29 @@ export class UserTransformService { ), ); stats.counter('user_transform_errors', eventsToProcess.length, { - transformationId: eventsToProcess[0]?.metadata?.transformationId, - workspaceId: eventsToProcess[0]?.metadata?.workspaceId, status, ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), }); } finally { stats.timing('user_transform_request_latency', userFuncStartTime, { - workspaceId: eventsToProcess[0]?.metadata?.workspaceId, - transformationId: eventsToProcess[0]?.metadata?.transformationId, ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), + }); + + stats.timing('user_transform_batch_size', requestSize, { + ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), + }); + + stats.timingSummary('user_transform_request_latency_summary', userFuncStartTime, { + ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), + }); + + stats.timingSummary('user_transform_batch_size_summary', requestSize, { + ...metaTags, + ...getTransformationMetadata(eventsToProcess[0]?.metadata), }); } @@ -188,7 +204,7 @@ export class UserTransformService { } as UserTransformationServiceResponse; } - public static async testTransformRoutine(events, trRevCode, libraryVersionIDs) { + public static async testTransformRoutine(events, trRevCode, libraryVersionIDs, credentials) { const response: FixMe = {}; try { if (!trRevCode || !trRevCode.code || !trRevCode.codeVersion) { @@ -198,11 +214,21 @@ export class UserTransformService { throw new Error('Invalid request. Missing events'); } + const updatedEvents = events.map((ev) => { + if (isNil(ev.credentials)) { + return { + ...ev, + credentials, + }; + } + return ev; + }); + logger.debug(`[CT] Test Input Events: ${JSON.stringify(events)}`); // eslint-disable-next-line no-param-reassign trRevCode.versionId = 'testVersionId'; response.body = await userTransformHandler()( - events, + updatedEvents, trRevCode.versionId, libraryVersionIDs, trRevCode, diff --git a/src/types/index.ts b/src/types/index.ts index 68dfe3870d..150758363e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -131,18 +131,28 @@ type Destination = { WorkspaceID: string; Transformations: UserTransformationInput[]; RevisionID?: string; + IsProcessorEnabled?: boolean; + IsConnectionEnabled?: boolean; }; type UserTransformationLibrary = { VersionID: string; }; +type Credential = { + id: string; + key: string; + value: string; + isSecret: boolean; +}; + type ProcessorTransformationRequest = { request?: object; message: object; metadata: Metadata; destination: Destination; libraries?: UserTransformationLibrary[]; + credentials?: Credential[]; }; type RouterTransformationRequestData = { diff --git a/src/types/zodTypes.ts b/src/types/zodTypes.ts index 0a65a2bae2..75f12c5e9b 100644 --- a/src/types/zodTypes.ts +++ b/src/types/zodTypes.ts @@ -1,5 +1,5 @@ -import { z } from 'zod'; import { isDefinedAndNotNullAndNotEmpty } from '@rudderstack/integrations-lib'; +import { z } from 'zod'; import { isHttpStatusSuccess } from '../v0/util'; const ProcessorTransformationOutputSchema = z.object({ diff --git a/src/util/customTransformer-faas.js b/src/util/customTransformer-faas.js index 2c0bbfd8c0..07dc205582 100644 --- a/src/util/customTransformer-faas.js +++ b/src/util/customTransformer-faas.js @@ -11,9 +11,10 @@ const { } = require('./openfaas'); const { getLibraryCodeV1 } = require('./customTransforrmationsStore-v1'); +const HASH_SECRET = process.env.OPENFAAS_FN_HASH_SECRET || ''; const libVersionIdsCache = new NodeCache(); -function generateFunctionName(userTransformation, libraryVersionIds, testMode) { +function generateFunctionName(userTransformation, libraryVersionIds, testMode, hashSecret = '') { if (userTransformation.versionId === FAAS_AST_VID) return FAAS_AST_FN_NAME; if (testMode) { @@ -21,10 +22,15 @@ function generateFunctionName(userTransformation, libraryVersionIds, testMode) { return funcName.substring(0, 63).toLowerCase(); } - const ids = [userTransformation.workspaceId, userTransformation.versionId].concat( + let ids = [userTransformation.workspaceId, userTransformation.versionId].concat( (libraryVersionIds || []).sort(), ); + if (hashSecret !== '') { + ids = ids.concat([hashSecret]); + } + + // FIXME: Why the id's are sorted ?! const hash = crypto.createHash('md5').update(`${ids}`).digest('hex'); return `fn-${userTransformation.workspaceId}-${hash}`.substring(0, 63).toLowerCase(); } @@ -85,12 +91,18 @@ async function setOpenFaasUserTransform( trMetadata = {}, ) { const tags = { - transformerVersionId: userTransformation.versionId, + transformationId: userTransformation.id, identifier: 'openfaas', testMode, }; const functionName = - pregeneratedFnName || generateFunctionName(userTransformation, libraryVersionIds, testMode); + pregeneratedFnName || + generateFunctionName( + userTransformation, + libraryVersionIds, + testMode, + process.env.OPENFAAS_FN_HASH_SECRET, + ); const setupTime = new Date(); await setupFaasFunction( @@ -130,7 +142,13 @@ async function runOpenFaasUserTransform( const trMetadata = events[0].metadata ? getTransformationMetadata(events[0].metadata) : {}; // check and deploy faas function if not exists - const functionName = generateFunctionName(userTransformation, libraryVersionIds, testMode); + const functionName = generateFunctionName( + userTransformation, + libraryVersionIds, + testMode, + process.env.OPENFAAS_FN_HASH_SECRET, + ); + if (testMode) { await setOpenFaasUserTransform( userTransformation, diff --git a/src/util/customTransformer-v1.js b/src/util/customTransformer-v1.js index 7e854a3714..12dab547e6 100644 --- a/src/util/customTransformer-v1.js +++ b/src/util/customTransformer-v1.js @@ -58,10 +58,16 @@ async function userTransformHandlerV1( return { transformedEvents: events }; } + const credentialsMap = {}; + (events[0]?.credentials || []).forEach((cred) => { + credentialsMap[cred.key] = cred.value; + }); const isolatevmFactory = await getFactory( userTransformation.code, libraryVersionIds, - userTransformation.versionId, + userTransformation.id, + userTransformation.workspaceId, + credentialsMap, userTransformation.secrets || {}, testMode, ); @@ -93,6 +99,7 @@ async function userTransformHandlerV1( }; stats.counter('user_transform_function_input_events', events.length, tags); stats.timing('user_transform_function_latency', invokeTime, tags); + stats.timingSummary('user_transform_function_latency_summary', invokeTime, tags); } return { transformedEvents, logs }; diff --git a/src/util/customTransformer.js b/src/util/customTransformer.js index a87c12dd6e..5ca1fae47c 100644 --- a/src/util/customTransformer.js +++ b/src/util/customTransformer.js @@ -8,7 +8,6 @@ const { parserForImport } = require('./parser'); const stats = require('./stats'); const { fetchWithDnsWrapper } = require('./utils'); const { getMetadata, getTransformationMetadata } = require('../v0/util'); - const ISOLATE_VM_MEMORY = parseInt(process.env.ISOLATE_VM_MEMORY || '128', 10); const GEOLOCATION_TIMEOUT_IN_MS = parseInt(process.env.GEOLOCATION_TIMEOUT_IN_MS || '1000', 10); @@ -17,9 +16,11 @@ async function runUserTransform( code, secrets, eventsMetadata, - versionId, + transformationId, + workspaceId, testMode = false, ) { + const trTags = { identifier: 'v0', transformationId, workspaceId }; // TODO: Decide on the right value for memory limit const isolate = new ivm.Isolate({ memoryLimit: ISOLATE_VM_MEMORY }); const context = await isolate.createContext(); @@ -37,9 +38,9 @@ async function runUserTransform( new ivm.Reference(async (resolve, ...args) => { try { const fetchStartTime = new Date(); - const res = await fetchWithDnsWrapper(versionId, ...args); + const res = await fetchWithDnsWrapper(trTags, ...args); const data = await res.json(); - stats.timing('fetch_call_duration', fetchStartTime, { versionId }); + stats.timing('fetch_call_duration', fetchStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(data).copyInto()]); } catch (error) { resolve.applyIgnored(undefined, [new ivm.ExternalCopy('ERROR').copyInto()]); @@ -52,7 +53,7 @@ async function runUserTransform( new ivm.Reference(async (resolve, reject, ...args) => { try { const fetchStartTime = new Date(); - const res = await fetchWithDnsWrapper(versionId, ...args); + const res = await fetchWithDnsWrapper(trTags, ...args); const headersContent = {}; res.headers.forEach((value, header) => { headersContent[header] = value; @@ -68,7 +69,7 @@ async function runUserTransform( data.body = JSON.parse(data.body); } catch (e) {} - stats.timing('fetchV2_call_duration', fetchStartTime, { versionId }); + stats.timing('fetchV2_call_duration', fetchStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(data).copyInto()]); } catch (error) { const err = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); @@ -94,7 +95,7 @@ async function runUserTransform( throw new Error(`request to fetch geolocation failed with status code: ${res.status}`); } const geoData = await res.json(); - stats.timing('geo_call_duration', geoStartTime, { versionId }); + stats.timing('geo_call_duration', geoStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(geoData).copyInto()]); } catch (error) { const err = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); @@ -252,14 +253,14 @@ async function runUserTransform( isolate.dispose(); const tags = { - identifier: 'v0', errored: transformationError ? true : false, - ...(events.length && events[0].metadata ? getMetadata(events[0].metadata) : {}), - ...(events.length && events[0].metadata ? getTransformationMetadata(events[0].metadata) : {}), + ...(Object.keys(eventsMetadata).length ? getMetadata(Object.values(eventsMetadata)[0]) : {}), + ...trTags, }; stats.counter('user_transform_function_input_events', events.length, tags); stats.timing('user_transform_function_latency', invokeTime, tags); + stats.timingSummary('user_transform_function_latency_summary', invokeTime, tags); } return { @@ -285,7 +286,6 @@ async function userTransformHandler( events.forEach((ev) => { eventsMetadata[ev.message.messageId] = ev.metadata; }); - let userTransformedEvents = []; let result; if (res.codeVersion && res.codeVersion === '1') { @@ -317,7 +317,8 @@ async function userTransformHandler( res.code, res.secrets || {}, eventsMetadata, - versionId, + res.id, + res.workspaceId, testMode, ); diff --git a/src/util/customTransforrmationsStore-v1.js b/src/util/customTransforrmationsStore-v1.js index 3263049b6f..d2d14f318e 100644 --- a/src/util/customTransforrmationsStore-v1.js +++ b/src/util/customTransforrmationsStore-v1.js @@ -19,24 +19,19 @@ const getRudderLibrariesUrl = `${CONFIG_BACKEND_URL}/rudderstackTransformationLi async function getTransformationCodeV1(versionId) { const transformation = transformationCache[versionId]; if (transformation) return transformation; - const tags = { - versionId, - version: 1, - }; try { const url = `${getTransformationURL}?versionId=${versionId}`; - const startTime = new Date(); const response = await fetchWithProxy(url); responseStatusHandler(response.status, 'Transformation', versionId, url); - stats.increment('get_transformation_code', { success: 'true', ...tags }); - stats.timing('get_transformation_code_time', startTime, tags); const myJson = await response.json(); transformationCache[versionId] = myJson; return myJson; } catch (error) { - logger.error(error); - stats.increment('get_transformation_code', { success: 'false', ...tags }); + logger.error( + `Error fetching transformation V1 code for versionId: ${versionId}`, + error.message, + ); throw error; } } @@ -44,24 +39,16 @@ async function getTransformationCodeV1(versionId) { async function getLibraryCodeV1(versionId) { const library = libraryCache[versionId]; if (library) return library; - const tags = { - libraryVersionId: versionId, - version: 1, - }; try { const url = `${getLibrariesUrl}?versionId=${versionId}`; - const startTime = new Date(); const response = await fetchWithProxy(url); responseStatusHandler(response.status, 'Transformation Library', versionId, url); - stats.increment('get_libraries_code', { success: 'true', ...tags }); - stats.timing('get_libraries_code_time', startTime, tags); const myJson = await response.json(); libraryCache[versionId] = myJson; return myJson; } catch (error) { - logger.error(error); - stats.increment('get_libraries_code', { success: 'false', ...tags }); + logger.error(`Error fetching library code for versionId: ${versionId}`, error.message); throw error; } } @@ -69,26 +56,17 @@ async function getLibraryCodeV1(versionId) { async function getRudderLibByImportName(importName) { const rudderLibrary = rudderLibraryCache[importName]; if (rudderLibrary) return rudderLibrary; - const tags = { - libraryVersionId: importName, - version: 1, - type: 'rudderlibrary', - }; try { const [name, version] = importName.split('/').slice(-2); const url = `${getRudderLibrariesUrl}/${name}?version=${version}`; - const startTime = new Date(); const response = await fetchWithProxy(url); responseStatusHandler(response.status, 'Rudder Library', importName, url); - stats.increment('get_libraries_code', { success: 'true', ...tags }); - stats.timing('get_libraries_code_time', startTime, tags); const myJson = await response.json(); rudderLibraryCache[importName] = myJson; return myJson; } catch (error) { - logger.error(error); - stats.increment('get_libraries_code', { success: 'false', ...tags }); + logger.error(`Error fetching rudder library code for importName: ${importName}`, error.message); throw error; } } diff --git a/src/util/customTransforrmationsStore.js b/src/util/customTransforrmationsStore.js index 08d417c07c..2043d18875 100644 --- a/src/util/customTransforrmationsStore.js +++ b/src/util/customTransforrmationsStore.js @@ -2,7 +2,6 @@ const NodeCache = require('node-cache'); const { fetchWithProxy } = require('./fetch'); const logger = require('../logger'); const { responseStatusHandler } = require('./utils'); -const stats = require('./stats'); const myCache = new NodeCache({ stdTTL: 60 * 60 * 24 * 1 }); @@ -18,18 +17,14 @@ async function getTransformationCode(versionId) { if (transformation) return transformation; try { const url = `${getTransformationURL}?versionId=${versionId}`; - const startTime = new Date(); const response = await fetchWithProxy(url); responseStatusHandler(response.status, 'Transformation', versionId, url); - stats.increment('get_transformation_code', { versionId, success: 'true' }); - stats.timing('get_transformation_code_time', startTime, { versionId }); const myJson = await response.json(); myCache.set(versionId, myJson); return myJson; } catch (error) { - logger.error(error); - stats.increment('get_transformation_code', { versionId, success: 'false' }); + logger.error(`Error fetching transformation code for versionId: ${versionId}`, error.message); throw error; } } diff --git a/src/util/errorNotifier/bugsnag.js b/src/util/errorNotifier/bugsnag.js index ef01c58730..a6a22655ad 100644 --- a/src/util/errorNotifier/bugsnag.js +++ b/src/util/errorNotifier/bugsnag.js @@ -22,7 +22,7 @@ const { NetworkInstrumentationError, } = require('@rudderstack/integrations-lib'); const { FilteredEventsError } = require('../../v0/util/errorTypes'); -const { logger } = require('../../logger'); +const logger = require('../../logger'); const pkg = require('../../../package.json'); const { diff --git a/src/util/errorNotifier/default.js b/src/util/errorNotifier/default.js index 28557a22f2..18f04f055d 100644 --- a/src/util/errorNotifier/default.js +++ b/src/util/errorNotifier/default.js @@ -3,7 +3,7 @@ const logger = require('../../logger'); function init() {} function notify(err, context, metadata) { - logger.error(err, context, metadata); + logger.error(err, { context, metadata }); } module.exports = { diff --git a/src/util/eventValidation.js b/src/util/eventValidation.js index 68d895dcc5..9f3ecd859d 100644 --- a/src/util/eventValidation.js +++ b/src/util/eventValidation.js @@ -126,7 +126,7 @@ async function validate(event) { trackingPlanId, trackingPlanVersion, event.message.type, - event.message.event, + event.message.type === 'track' ? event.message.event : '', workspaceId, ); diff --git a/src/util/fetchDestinationHandlers.ts b/src/util/fetchDestinationHandlers.ts new file mode 100644 index 0000000000..2661ef2e68 --- /dev/null +++ b/src/util/fetchDestinationHandlers.ts @@ -0,0 +1,42 @@ +import * as V0MarketoBulkUploadFileUpload from '../v0/destinations/marketo_bulk_upload/fileUpload'; +import * as V0MarketoBulkUploadPollStatus from '../v0/destinations/marketo_bulk_upload/poll'; +import * as V0MarketoBulkUploadJobStatus from '../v0/destinations/marketo_bulk_upload/fetchJobStatus'; + +const fileUploadHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadFileUpload, + }, +}; + +const pollStatusHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadPollStatus, + }, +}; + +const jobStatusHandlers = { + v0: { + marketo_bulk_upload: V0MarketoBulkUploadJobStatus, + }, +}; + +export const getDestFileUploadHandler = (version, dest) => { + if (fileUploadHandlers[version] && fileUploadHandlers[version][dest]) { + return fileUploadHandlers[version][dest]; + } + return undefined; +}; + +export const getPollStatusHandler = (version, dest) => { + if (pollStatusHandlers[version] && pollStatusHandlers[version][dest]) { + return pollStatusHandlers[version][dest]; + } + return undefined; +}; + +export const getJobStatusHandler = (version, dest) => { + if (jobStatusHandlers[version] && jobStatusHandlers[version][dest]) { + return jobStatusHandlers[version][dest]; + } + return undefined; +}; diff --git a/src/util/ivmFactory.js b/src/util/ivmFactory.js index 9a6419295d..625591964c 100644 --- a/src/util/ivmFactory.js +++ b/src/util/ivmFactory.js @@ -1,6 +1,6 @@ const ivm = require('isolated-vm'); const fetch = require('node-fetch'); -const _ = require('lodash'); +const { isNil, isObject, camelCase } = require('lodash'); const { getLibraryCodeV1, getRudderLibByImportName } = require('./customTransforrmationsStore-v1'); const { extractStackTraceUptoLastSubstringMatch } = require('./utils'); @@ -30,7 +30,16 @@ async function loadModule(isolateInternal, contextInternal, moduleName, moduleCo return module; } -async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) { +async function createIvm( + code, + libraryVersionIds, + transformationId, + workspaceId, + credentials, + secrets, + testMode, +) { + const trTags = { identifier: 'V1', transformationId, workspaceId }; const createIvmStartTime = new Date(); const logs = []; const libraries = await Promise.all( @@ -51,7 +60,7 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) // TODO: Check if this should this be && libraries.forEach((library) => { - const libHandleName = _.camelCase(library.name); + const libHandleName = camelCase(library.name); if (extractedLibraries.includes(libHandleName)) { librariesMap[libHandleName] = library.code; } @@ -178,9 +187,9 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) new ivm.Reference(async (resolve, ...args) => { try { const fetchStartTime = new Date(); - const res = await fetchWithDnsWrapper(versionId, ...args); + const res = await fetchWithDnsWrapper(trTags, ...args); const data = await res.json(); - stats.timing('fetch_call_duration', fetchStartTime, { versionId }); + stats.timing('fetch_call_duration', fetchStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(data).copyInto()]); } catch (error) { resolve.applyIgnored(undefined, [new ivm.ExternalCopy('ERROR').copyInto()]); @@ -193,7 +202,7 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) new ivm.Reference(async (resolve, reject, ...args) => { try { const fetchStartTime = new Date(); - const res = await fetchWithDnsWrapper(versionId, ...args); + const res = await fetchWithDnsWrapper(trTags, ...args); const headersContent = {}; res.headers.forEach((value, header) => { headersContent[header] = value; @@ -209,7 +218,7 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) data.body = JSON.parse(data.body); } catch (e) {} - stats.timing('fetchV2_call_duration', fetchStartTime, { versionId }); + stats.timing('fetchV2_call_duration', fetchStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(data).copyInto()]); } catch (error) { const err = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); @@ -234,7 +243,7 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) throw new Error(`request to fetch geolocation failed with status code: ${res.status}`); } const geoData = await res.json(); - stats.timing('geo_call_duration', geoStartTime, { versionId }); + stats.timing('geo_call_duration', geoStartTime, trTags); resolve.applyIgnored(undefined, [new ivm.ExternalCopy(geoData).copyInto()]); } catch (error) { const err = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); @@ -243,6 +252,20 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) }), ); + await jail.set('_credential', function (key) { + if (isNil(credentials) || !isObject(credentials)) { + logger.error( + `Error fetching credentials map for transformationID: ${transformationId} and workspaceId: ${workspaceId}`, + ); + stats.increment('credential_error_total', trTags); + return undefined; + } + if (key === null || key === undefined) { + throw new TypeError('Key should be valid and defined'); + } + return credentials[key]; + }); + await jail.set('_rsSecrets', function (...args) { if (args.length == 0 || !secrets || !secrets[args[0]]) return 'ERROR'; return secrets[args[0]]; @@ -321,6 +344,13 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) ]); }; + let credential = _credential; + delete _credential; + global.credential = function(...args) { + const key = args[0]; + return credential(new ivm.ExternalCopy(key).copyInto()); + }; + return new ivm.Reference(function forwardMainPromise( fnRef, resolve, @@ -386,7 +416,7 @@ async function createIvm(code, libraryVersionIds, versionId, secrets, testMode) reference: true, }); const fName = availableFuncNames[0]; - stats.timing('createivm_duration', createIvmStartTime); + stats.timing('createivm_duration', createIvmStartTime, trTags); // TODO : check if we can resolve this // eslint-disable-next-line no-async-promise-executor @@ -411,10 +441,26 @@ async function compileUserLibrary(code) { return evaluateModule(isolate, context, code); } -async function getFactory(code, libraryVersionIds, versionId, secrets, testMode) { +async function getFactory( + code, + libraryVersionIds, + transformationId, + workspaceId, + credentials, + secrets, + testMode, +) { const factory = { create: async () => { - return createIvm(code, libraryVersionIds, versionId, secrets, testMode); + return createIvm( + code, + libraryVersionIds, + transformationId, + workspaceId, + credentials, + secrets, + testMode, + ); }, destroy: async (client) => { client.fnRef.release(); diff --git a/src/util/openfaas/faasApi.js b/src/util/openfaas/faasApi.js index 4db5e2a81c..b932b70032 100644 --- a/src/util/openfaas/faasApi.js +++ b/src/util/openfaas/faasApi.js @@ -1,11 +1,20 @@ const axios = require('axios'); const { RespStatusError, RetryRequestError } = require('../utils'); +const logger = require('../../logger'); + const OPENFAAS_GATEWAY_URL = process.env.OPENFAAS_GATEWAY_URL || 'http://localhost:8080'; +const OPENFAAS_GATEWAY_USERNAME = process.env.OPENFAAS_GATEWAY_USERNAME || ''; +const OPENFAAS_GATEWAY_PASSWORD = process.env.OPENFAAS_GATEWAY_PASSWORD || ''; + +const basicAuth = { + username: OPENFAAS_GATEWAY_USERNAME, + password: OPENFAAS_GATEWAY_PASSWORD, +}; const parseAxiosError = (error) => { if (error.response) { - const status = error.response.status || 400; + const status = error.response.status || 500; const errorData = error.response?.data; const message = (errorData && (errorData.message || errorData.error || errorData)) || error.message; @@ -21,7 +30,7 @@ const deleteFunction = async (functionName) => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; axios - .delete(url, { data: { functionName } }) + .delete(url, { data: { functionName }, auth: basicAuth }) .then(() => resolve()) .catch((err) => reject(parseAxiosError(err))); }); @@ -30,7 +39,7 @@ const getFunction = async (functionName) => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/function/${functionName}`; axios - .get(url) + .get(url, { auth: basicAuth }) .then((resp) => resolve(resp.data)) .catch((err) => reject(parseAxiosError(err))); }); @@ -39,7 +48,7 @@ const getFunctionList = async () => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; axios - .get(url) + .get(url, { auth: basicAuth }) .then((resp) => resolve(resp.data)) .catch((err) => reject(parseAxiosError(err))); }); @@ -48,30 +57,56 @@ const invokeFunction = async (functionName, payload) => new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/function/${functionName}`; axios - .post(url, payload) + .post(url, payload, { auth: basicAuth }) .then((resp) => resolve(resp.data)) .catch((err) => reject(parseAxiosError(err))); }); -const checkFunctionHealth = async (functionName) => - new Promise((resolve, reject) => { +const checkFunctionHealth = async (functionName) => { + logger.debug(`Checking function health: ${functionName}`); + + return new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/function/${functionName}`; axios - .get(url, { - headers: { 'X-REQUEST-TYPE': 'HEALTH-CHECK' }, - }) + .get( + url, + { + headers: { 'X-REQUEST-TYPE': 'HEALTH-CHECK' }, + }, + { auth: basicAuth }, + ) .then((resp) => resolve(resp)) .catch((err) => reject(parseAxiosError(err))); }); +}; -const deployFunction = async (payload) => - new Promise((resolve, reject) => { +const deployFunction = async (payload) => { + logger.debug(`Deploying function: ${payload?.name}`); + + return new Promise((resolve, reject) => { const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; axios - .post(url, payload) + .post(url, payload, { auth: basicAuth }) .then((resp) => resolve(resp.data)) - .catch((err) => reject(parseAxiosError(err))); + .catch((err) => { + reject(parseAxiosError(err)); + }); }); +}; + +const updateFunction = async (fnName, payload) => { + logger.debug(`Updating function: ${fnName}`); + + return new Promise((resolve, reject) => { + const url = `${OPENFAAS_GATEWAY_URL}/system/functions`; + axios + .put(url, payload, { auth: basicAuth }) + .then((resp) => resolve(resp.data)) + .catch((err) => { + reject(parseAxiosError(err)); + }); + }); +}; module.exports = { deleteFunction, @@ -80,4 +115,5 @@ module.exports = { getFunctionList, invokeFunction, checkFunctionHealth, + updateFunction, }; diff --git a/src/util/openfaas/index.js b/src/util/openfaas/index.js index 878fa706d9..c0369deb81 100644 --- a/src/util/openfaas/index.js +++ b/src/util/openfaas/index.js @@ -4,6 +4,7 @@ const { deployFunction, invokeFunction, checkFunctionHealth, + updateFunction, } = require('./faasApi'); const logger = require('../../logger'); const { RetryRequestError, RespStatusError } = require('../utils'); @@ -11,6 +12,11 @@ const stats = require('../stats'); const { getMetadata, getTransformationMetadata } = require('../../v0/util'); const { HTTP_STATUS_CODES } = require('../../v0/util/constant'); +const FAAS_SCALE_TYPE = process.env.FAAS_SCALE_TYPE || 'capacity'; +const FAAS_SCALE_TARGET = process.env.FAAS_SCALE_TARGET || '4'; +const FAAS_SCALE_TARGET_PROPORTION = process.env.FAAS_SCALE_TARGET_PROPORTION || '0.70'; +const FAAS_SCALE_ZERO = process.env.FAAS_SCALE_ZERO || 'false'; +const FAAS_SCALE_ZERO_DURATION = process.env.FAAS_SCALE_ZERO_DURATION || '15m'; const FAAS_BASE_IMG = process.env.FAAS_BASE_IMG || 'rudderlabs/openfaas-flask:main'; const FAAS_MAX_PODS_IN_TEXT = process.env.FAAS_MAX_PODS_IN_TEXT || '40'; const FAAS_MIN_PODS_IN_TEXT = process.env.FAAS_MIN_PODS_IN_TEXT || '1'; @@ -27,6 +33,8 @@ const FAAS_AST_VID = 'ast'; const FAAS_AST_FN_NAME = 'fn-ast'; const CUSTOM_NETWORK_POLICY_WORKSPACE_IDS = process.env.CUSTOM_NETWORK_POLICY_WORKSPACE_IDS || ''; const customNetworkPolicyWorkspaceIds = CUSTOM_NETWORK_POLICY_WORKSPACE_IDS.split(','); +const CUSTOMER_TIER = process.env.CUSTOMER_TIER || 'shared'; +const DISABLE_RECONCILE_FN = process.env.DISABLE_RECONCILE_FN == 'true' || false; // Initialise node cache const functionListCache = new NodeCache(); @@ -61,6 +69,8 @@ const awaitFunctionReadiness = async ( maxWaitInMs = 22000, waitBetweenIntervalsInMs = 250, ) => { + logger.debug(`Awaiting function readiness: ${functionName}`); + const executionPromise = new Promise(async (resolve) => { try { await callWithRetry( @@ -115,7 +125,7 @@ const invalidateFnCache = () => { functionListCache.set(FUNC_LIST_KEY, []); }; -const deployFaasFunction = async ( +const updateFaasFunction = async ( functionName, code, versionId, @@ -124,64 +134,50 @@ const deployFaasFunction = async ( trMetadata = {}, ) => { try { - logger.debug('[Faas] Deploying a faas function'); - let envProcess = 'python index.py'; - - const lvidsString = libraryVersionIDs.join(','); + logger.debug(`Updating faas fn: ${functionName}`); - if (!testMode) { - envProcess = `${envProcess} --vid ${versionId} --config-backend-url ${CONFIG_BACKEND_URL} --lvids "${lvidsString}"`; - } else { - envProcess = `${envProcess} --code "${code}" --config-backend-url ${CONFIG_BACKEND_URL} --lvids "${lvidsString}"`; - } - - const envVars = {}; - if (FAAS_ENABLE_WATCHDOG_ENV_VARS.trim().toLowerCase() === 'true') { - envVars.max_inflight = FAAS_MAX_INFLIGHT; - envVars.exec_timeout = FAAS_EXEC_TIMEOUT; - } - if (GEOLOCATION_URL) { - envVars.geolocation_url = GEOLOCATION_URL; - } - // labels - const labels = { - 'openfaas-fn': 'true', - 'parent-component': 'openfaas', - 'com.openfaas.scale.max': FAAS_MAX_PODS_IN_TEXT, - 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT, - transformationId: trMetadata.transformationId, - workspaceId: trMetadata.workspaceId, - }; - if ( - trMetadata.workspaceId && - customNetworkPolicyWorkspaceIds.includes(trMetadata.workspaceId) - ) { - labels['custom-network-policy'] = 'true'; + const payload = buildOpenfaasFn( + functionName, + code, + versionId, + libraryVersionIDs, + testMode, + trMetadata, + ); + await updateFunction(functionName, payload); + // wait for function to be ready and then set it in cache + await awaitFunctionReadiness(functionName); + setFunctionInCache(functionName); + } catch (error) { + // 404 is statuscode returned from openfaas community edition + // when the function don't exist, so we can safely ignore this error + // and let the function be created in the next step. + if (error.statusCode !== 404) { + throw error; } + } +}; - // TODO: investigate and add more required labels and annotations - const payload = { - service: functionName, - name: functionName, - image: FAAS_BASE_IMG, - envProcess, - envVars, - labels, - annotations: { - 'prometheus.io.scrape': 'true', - }, - limits: { - memory: FAAS_LIMITS_MEMORY, - cpu: FAAS_LIMITS_CPU, - }, - requests: { - memory: FAAS_REQUESTS_MEMORY, - cpu: FAAS_REQUESTS_CPU, - }, - }; +const deployFaasFunction = async ( + functionName, + code, + versionId, + libraryVersionIDs, + testMode, + trMetadata = {}, +) => { + try { + logger.debug(`Deploying faas fn: ${functionName}`); + const payload = buildOpenfaasFn( + functionName, + code, + versionId, + libraryVersionIDs, + testMode, + trMetadata, + ); await deployFunction(payload); - logger.debug('[Faas] Deployed a faas function'); } catch (error) { logger.error(`[Faas] Error while deploying ${functionName}: ${error.message}`); // To handle concurrent create requests, @@ -231,6 +227,95 @@ async function setupFaasFunction( } } +// reconcileFn runs everytime the service boot's up +// trying to update the functions which are not in cache to the +// latest label and envVars +const reconcileFn = async (name, versionId, libraryVersionIDs, trMetadata) => { + if (DISABLE_RECONCILE_FN) { + return; + } + + logger.debug(`Reconciling faas function: ${name}`); + try { + if (isFunctionDeployed(name)) { + return; + } + await updateFaasFunction(name, null, versionId, libraryVersionIDs, false, trMetadata); + } catch (error) { + logger.error( + `unexpected error occurred when reconciling the function ${name}: ${error.message}`, + ); + throw error; + } +}; + +// buildOpenfaasFn is helper function to build openfaas fn CRUD payload +function buildOpenfaasFn(name, code, versionId, libraryVersionIDs, testMode, trMetadata = {}) { + logger.debug(`Building faas fn: ${name}`); + + let envProcess = 'python index.py'; + const lvidsString = libraryVersionIDs.join(','); + + if (!testMode) { + envProcess = `${envProcess} --vid ${versionId} --config-backend-url ${CONFIG_BACKEND_URL} --lvids "${lvidsString}"`; + } else { + envProcess = `${envProcess} --code "${code}" --config-backend-url ${CONFIG_BACKEND_URL} --lvids "${lvidsString}"`; + } + + const envVars = {}; + + if (FAAS_ENABLE_WATCHDOG_ENV_VARS.trim().toLowerCase() === 'true') { + envVars.max_inflight = FAAS_MAX_INFLIGHT; + envVars.exec_timeout = FAAS_EXEC_TIMEOUT; + } + + if (GEOLOCATION_URL) { + envVars.geolocation_url = GEOLOCATION_URL; + } + + const labels = { + 'openfaas-fn': 'true', + 'parent-component': 'openfaas', + 'com.openfaas.scale.max': FAAS_MAX_PODS_IN_TEXT, + 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT, + 'com.openfaas.scale.zero': FAAS_SCALE_ZERO, + 'com.openfaas.scale.zero-duration': FAAS_SCALE_ZERO_DURATION, + 'com.openfaas.scale.target': FAAS_SCALE_TARGET, + 'com.openfaas.scale.target-proportion': FAAS_SCALE_TARGET_PROPORTION, + 'com.openfaas.scale.type': FAAS_SCALE_TYPE, + transformationId: trMetadata.transformationId, + workspaceId: trMetadata.workspaceId, + team: 'data-management', + service: 'openfaas-fn', + customer: 'shared', + 'customer-tier': CUSTOMER_TIER, + }; + + if (trMetadata.workspaceId && customNetworkPolicyWorkspaceIds.includes(trMetadata.workspaceId)) { + labels['custom-network-policy'] = 'true'; + } + + return { + service: name, + name: name, + image: FAAS_BASE_IMG, + envProcess, + envVars, + labels, + annotations: { + 'prometheus.io.scrape': 'true', + }, + limits: { + memory: FAAS_LIMITS_MEMORY, + cpu: FAAS_LIMITS_CPU, + }, + requests: { + memory: FAAS_REQUESTS_MEMORY, + cpu: FAAS_REQUESTS_CPU, + }, + }; +} + const executeFaasFunction = async ( name, events, @@ -245,7 +330,11 @@ const executeFaasFunction = async ( let errorRaised; try { - if (testMode) await awaitFunctionReadiness(name); + if (testMode) { + await awaitFunctionReadiness(name); + } else { + await reconcileFn(name, versionId, libraryVersionIDs, trMetadata); + } return await invokeFunction(name, events); } catch (error) { logger.error(`Error while invoking ${name}: ${error.message}`); @@ -253,6 +342,7 @@ const executeFaasFunction = async ( if (error.statusCode === 404 && error.message.includes(`error finding function ${name}`)) { removeFunctionFromCache(name); + await setupFaasFunction(name, null, versionId, libraryVersionIDs, testMode, trMetadata); throw new RetryRequestError(`${name} not found`); } @@ -290,6 +380,7 @@ const executeFaasFunction = async ( stats.counter('user_transform_function_input_events', events.length, tags); stats.timing('user_transform_function_latency', startTime, tags); + stats.timingSummary('user_transform_function_latency_summary', startTime, tags); } }; @@ -298,6 +389,8 @@ module.exports = { executeFaasFunction, setupFaasFunction, invalidateFnCache, + buildOpenfaasFn, FAAS_AST_VID, FAAS_AST_FN_NAME, + setFunctionInCache, }; diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 0fa17dc9bd..860c266565 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -11,7 +11,7 @@ function appendPrefix(name) { } class Prometheus { - constructor() { + constructor(enableSummaryMetrics = true) { this.prometheusRegistry = new prometheusClient.Registry(); this.prometheusRegistry.setDefaultLabels(defaultLabels); prometheusClient.collectDefaultMetrics({ @@ -21,7 +21,7 @@ class Prometheus { prometheusClient.AggregatorRegistry.setRegistries(this.prometheusRegistry); this.aggregatorRegistry = new prometheusClient.AggregatorRegistry(); - this.createMetrics(); + this.createMetrics(enableSummaryMetrics); } async metricsController(ctx) { @@ -56,11 +56,22 @@ class Prometheus { return gauge; } - newSummaryStat(name, help, labelNames) { + newSummaryStat( + name, + help, + labelNames, + percentiles = [0.5, 0.9, 0.99], + maxAgeSeconds = 300, + ageBuckets = 5, + ) { + // we enable a 5 minute sliding window and calculate the 50th, 90th, and 99th percentiles by default const summary = new prometheusClient.Summary({ name, help, labelNames, + percentiles, + maxAgeSeconds, + ageBuckets, }); this.prometheusRegistry.registerMetric(summary); return summary; @@ -117,6 +128,21 @@ class Prometheus { } } + timingSummary(name, start, tags = {}) { + try { + let metric = this.prometheusRegistry.getSingleMetric(appendPrefix(name)); + if (!metric) { + logger.warn( + `Prometheus: summary metric ${name} not found in the registry. Creating a new one`, + ); + metric = this.newSummaryStat(name, name, Object.keys(tags)); + } + metric.observe(tags, (new Date() - start) / 1000); + } catch (e) { + logger.error(`Prometheus: Summary metric ${name} failed with error ${e}`); + } + } + histogram(name, value, tags = {}) { try { let metric = this.prometheusRegistry.getSingleMetric(appendPrefix(name)); @@ -166,7 +192,7 @@ class Prometheus { } } - createMetrics() { + createMetrics(enableSummaryMetrics) { const metrics = [ // Counters { @@ -213,79 +239,12 @@ class Prometheus { 'implementation', ], }, - { - name: 'tp_violation_type', - help: 'tp_violation_type', - type: 'counter', - labelNames: ['violationType', 'sourceType', 'destinationType', 'k8_namespace'], - }, - { - name: 'tp_propagated_events', - help: 'tp_propagated_events', - type: 'counter', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], - }, - { - name: 'tp_errors', - help: 'tp_errors', - type: 'counter', - labelNames: [ - 'sourceType', - 'destinationType', - 'k8_namespace', - 'workspaceId', - 'trackingPlanId', - ], - }, - { - name: 'tp_events_count', - help: 'tp_events_count', - type: 'counter', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], - }, - { - name: 'user_transform_function_group_size', - help: 'user_transform_function_group_size', - type: 'counter', - labelNames: ['processSessions'], - }, - { - name: 'user_transform_errors', - help: 'user_transform_errors', - type: 'counter', - labelNames: [ - 'workspaceId', - 'transformationId', - 'status', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'c2', - help: 'h2', - type: 'counter', - labelNames: [ - 'transformationVersionId', - 'processSessions', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'dest_transform_requests', help: 'dest_transform_requests', type: 'counter', labelNames: ['destination', 'version', 'sourceType', 'destinationType', 'k8_namespace'], }, - { - name: 'user_transform_requests', - help: 'user_transform_requests', - type: 'counter', - labelNames: ['processSessions'], - }, { name: 'source_transform_requests', help: 'source_transform_requests', @@ -316,56 +275,6 @@ class Prometheus { type: 'counter', labelNames: ['success'], }, - { - name: 'create_zip_error', - help: 'create_zip_error', - type: 'counter', - labelNames: ['fileName'], - }, - { - name: 'delete_zip_error', - help: 'delete_zip_error', - type: 'counter', - labelNames: ['functionName'], - }, - { - name: 'hv_metrics', - help: 'hv_metrics', - type: 'counter', - labelNames: [ - 'destination', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - 'dropped', - 'violationType', - ], - }, - { - name: 'events_into_vm', - help: 'events_into_vm', - type: 'counter', - labelNames: [ - 'transformerVersionId', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'missing_handle', - help: 'missing_handle', - type: 'counter', - labelNames: [ - 'transformerVersionId', - 'language', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'proxy_test_error', help: 'proxy_test_error', @@ -469,18 +378,6 @@ class Prometheus { type: 'counter', labelNames: ['destinationId'], }, - { - name: 'get_eventSchema_error', - help: 'get_eventSchema_error', - type: 'counter', - labelNames: [], - }, - { - name: 'get_tracking_plan_error', - help: 'get_tracking_plan_error', - type: 'counter', - labelNames: [], - }, { name: 'redis_error', help: 'redis_error', @@ -511,18 +408,6 @@ class Prometheus { type: 'counter', labelNames: ['writeKey', 'source'], }, - { - name: 'get_transformation_code', - help: 'get_transformation_code', - type: 'counter', - labelNames: ['versionId', 'version', 'success'], - }, - { - name: 'get_libraries_code', - help: 'get_libraries_code', - type: 'counter', - labelNames: ['libraryVersionId', 'version', 'type', 'success'], - }, { name: 'invalid_shopify_event', help: 'invalid_shopify_event', @@ -541,6 +426,11 @@ class Prometheus { 'statusCode', 'requestMethod', 'module', + 'workspaceId', + 'destinationId', + 'module', + 'implementation', + 'sourceId', ], }, @@ -575,49 +465,66 @@ class Prometheus { type: 'gauge', labelNames: ['destination_id'], }, - - // Histograms { - name: 'outgoing_request_latency', - help: 'Outgoing HTTP requests duration in seconds', - type: 'histogram', - labelNames: ['feature', 'destType', 'endpointPath', 'requestMethod', 'module'], + name: 'braze_alias_failure_count', + help: 'braze_alias_failure_count', + type: 'counter', + labelNames: ['destination_id'], }, { - name: 'http_request_duration', - help: 'Incoming HTTP requests duration in seconds', - type: 'histogram', - labelNames: ['method', 'route', 'code'], + name: 'braze_alias_missconfigured_count', + help: 'braze_alias_missconfigured_count', + type: 'counter', + labelNames: ['destination_id'], }, { - name: 'tp_request_size', - help: 'tp_request_size', - type: 'histogram', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], + name: 'mixpanel_batch_engage_pack_size', + help: 'mixpanel_batch_engage_pack_size', + type: 'gauge', + labelNames: ['destination_id'], + }, + { + name: 'mixpanel_batch_group_pack_size', + help: 'mixpanel_batch_group_pack_size', + type: 'gauge', + labelNames: ['destination_id'], }, { - name: 'tp_request_latency', - help: 'tp_request_latency', + name: 'mixpanel_batch_import_pack_size', + help: 'mixpanel_batch_import_pack_size', + type: 'gauge', + labelNames: ['destination_id'], + }, + + // Histograms + { + name: 'outgoing_request_latency', + help: 'Outgoing HTTP requests duration in seconds', type: 'histogram', labelNames: [ - 'sourceType', - 'destinationType', - 'k8_namespace', + 'feature', + 'destType', + 'endpointPath', + 'requestMethod', + 'module', 'workspaceId', - 'trackingPlanId', + 'destinationId', + 'module', + 'implementation', + 'sourceId', ], }, { - name: 'cdk_events_latency', - help: 'cdk_events_latency', + name: 'http_request_duration', + help: 'Incoming HTTP requests duration in seconds', type: 'histogram', - labelNames: ['destination', 'sourceType', 'destinationType', 'k8_namespace'], + labelNames: ['method', 'route', 'code', 'destType'], }, { - name: 'tp_event_latency', - help: 'tp_event_latency', + name: 'cdk_events_latency', + help: 'cdk_events_latency', type: 'histogram', - labelNames: ['sourceType', 'destinationType', 'k8_namespace'], + labelNames: ['destination', 'sourceType', 'destinationType', 'k8_namespace'], }, { name: 'regulation_worker_requests_dest_latency', @@ -625,31 +532,6 @@ class Prometheus { type: 'histogram', labelNames: ['feature', 'implementation', 'destType'], }, - { - name: 'dest_transform_request_latency', - help: 'dest_transform_request_latency', - type: 'histogram', - labelNames: [ - 'destination', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - 'feature', - ], - }, - { - name: 'user_transform_request_latency', - help: 'user_transform_request_latency', - type: 'histogram', - labelNames: [ - 'workspaceId', - 'transformationId', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'source_transform_request_latency', help: 'source_transform_request_latency', @@ -668,94 +550,6 @@ class Prometheus { type: 'histogram', labelNames: ['destination', 'version'], }, - { - name: 'creation_time', - help: 'creation_time', - type: 'histogram', - labelNames: ['transformerVersionId', 'language', 'identifier', 'publish', 'testMode'], - }, - { name: 'get_tracking_plan', help: 'get_tracking_plan', type: 'histogram', labelNames: [] }, - { name: 'createivm_duration', help: 'createivm_duration', type: 'histogram', labelNames: [] }, - { - name: 'fetchV2_call_duration', - help: 'fetchV2_call_duration', - type: 'histogram', - labelNames: ['versionId'], - }, - { - name: 'fetch_call_duration', - help: 'fetch_call_duration', - type: 'histogram', - labelNames: ['versionId'], - }, - { - name: 'fetch_dns_resolve_time', - help: 'fetch_dns_resolve_time', - type: 'histogram', - labelNames: ['transformerVersionId', 'error'], - }, - { - name: 'geo_call_duration', - help: 'geo_call_duration', - type: 'histogram', - labelNames: ['versionId'], - }, - { - name: 'get_transformation_code_time', - help: 'get_transformation_code_time', - type: 'histogram', - labelNames: ['versionId', 'version'], - }, - { - name: 'get_libraries_code_time', - help: 'get_libraries_code_time', - type: 'histogram', - labelNames: ['libraryVersionId', 'versionId', 'type'], - }, - { - name: 'isolate_cpu_time', - help: 'isolate_cpu_time', - type: 'histogram', - labelNames: [ - 'transformerVersionId', - 'identifier', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'isolate_wall_time', - help: 'isolate_wall_time', - type: 'histogram', - labelNames: [ - 'transformerVersionId', - 'identifier', - 'version', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, - { - name: 'lambda_test_time', - help: 'lambda_test_time', - type: 'histogram', - labelNames: ['transformerVersionId', 'language', 'publish'], - }, - { - name: 'lambda_invoke_time', - help: 'lambda_invoke_time', - type: 'histogram', - labelNames: [ - 'transformerVersionId', - 'language', - 'sourceType', - 'destinationType', - 'k8_namespace', - ], - }, { name: 'marketo_bulk_upload_process_time', help: 'marketo_bulk_upload_process_time', @@ -881,20 +675,6 @@ class Prometheus { labelNames: ['destination', 'version', 'sourceType', 'destinationType', 'k8_namespace'], buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], }, - { - name: 'user_transform_input_events', - help: 'Number of input events to user transform', - type: 'histogram', - labelNames: ['processSessions'], - buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], - }, - { - name: 'user_transform_output_events', - help: 'user_transform_output_events', - type: 'histogram', - labelNames: ['processSessions'], - buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], - }, { name: 'marketo_bulk_upload_create_header_time', help: 'marketo_bulk_upload_create_header_time', @@ -931,6 +711,117 @@ class Prometheus { type: 'histogram', labelNames: [], }, + // tracking plan metrics: + // counter + { + name: 'hv_metrics', + help: 'hv_metrics', + type: 'counter', + labelNames: [ + 'destination', + 'version', + 'sourceType', + 'destinationType', + 'k8_namespace', + 'dropped', + 'violationType', + ], + }, + { + name: 'get_eventSchema_error', + help: 'get_eventSchema_error', + type: 'counter', + labelNames: [], + }, + { + name: 'get_tracking_plan_error', + help: 'get_tracking_plan_error', + type: 'counter', + labelNames: [], + }, + // histogram + { + name: 'tp_batch_size', + help: 'Size of batch of events for tracking plan validation', + type: 'histogram', + buckets: [ + 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, + 524288000, + ], + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + ], + }, + { + name: 'tp_event_validation_latency', + help: 'Latency of validating tracking plan at event level', + type: 'histogram', + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + 'status', + 'exception', + ], + }, + { + name: 'tp_batch_validation_latency', + help: 'Latency of validating tracking plan at batch level', + type: 'histogram', + labelNames: [ + 'sourceType', + 'destinationType', + 'k8_namespace', + 'workspaceId', + 'trackingPlanId', + ], + }, + { + name: 'tp_event_latency', + help: 'tp_event_latency', + type: 'histogram', + labelNames: ['sourceType', 'destinationType', 'k8_namespace'], + }, + { name: 'get_tracking_plan', help: 'get_tracking_plan', type: 'histogram', labelNames: [] }, + // User transform metrics + // counter + { + name: 'user_transform_function_group_size', + help: 'user_transform_function_group_size', + type: 'counter', + labelNames: [], + }, + { + name: 'user_transform_errors', + help: 'user_transform_errors', + type: 'counter', + labelNames: [ + 'workspaceId', + 'transformationId', + 'status', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, + { + name: 'user_transform_requests', + help: 'user_transform_requests', + type: 'counter', + labelNames: [], + }, + { + name: 'credential_error_total', + help: 'Error in fetching credentials count', + type: 'counter', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, { name: 'user_transform_function_input_events', help: 'user_transform_function_input_events', @@ -947,6 +838,85 @@ class Prometheus { 'workspaceId', ], }, + // histogram + { + name: 'user_transform_request_latency', + help: 'user_transform_request_latency', + type: 'histogram', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, + { + name: 'user_transform_batch_size', + help: 'user_transform_batch_size', + type: 'histogram', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + buckets: [ + 1024, 102400, 524288, 1048576, 10485760, 20971520, 52428800, 104857600, 209715200, + 524288000, + ], // 1KB, 100KB, 0.5MB, 1MB, 10MB, 20MB, 50MB, 100MB, 200MB, 500MB + }, + { + name: 'creation_time', + help: 'creation_time', + type: 'histogram', + labelNames: ['transformationId', 'identifier', 'testMode'], + }, + { + name: 'createivm_duration', + help: 'createivm_duration', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, + { + name: 'fetchV2_call_duration', + help: 'fetchV2_call_duration', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, + { + name: 'fetch_call_duration', + help: 'fetch_call_duration', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, + { + name: 'fetch_dns_resolve_time', + help: 'fetch_dns_resolve_time', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId', 'error'], + }, + { + name: 'geo_call_duration', + help: 'geo_call_duration', + type: 'histogram', + labelNames: ['identifier', 'transformationId', 'workspaceId'], + }, + { + name: 'user_transform_input_events', + help: 'Number of input events to user transform', + type: 'histogram', + labelNames: [], + buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], + }, + { + name: 'user_transform_output_events', + help: 'user_transform_output_events', + type: 'histogram', + labelNames: [], + buckets: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200], + }, { name: 'user_transform_function_latency', help: 'user_transform_function_latency', @@ -963,6 +933,47 @@ class Prometheus { 'workspaceId', ], }, + // summary + { + name: 'user_transform_request_latency_summary', + help: 'user_transform_request_latency_summary', + type: 'summary', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, + { + name: 'user_transform_batch_size_summary', + help: 'user_transform_batch_size_summary', + type: 'summary', + labelNames: [ + 'workspaceId', + 'transformationId', + 'sourceType', + 'destinationType', + 'k8_namespace', + ], + }, + { + name: 'user_transform_function_latency_summary', + help: 'user_transform_function_latency_summary', + type: 'summary', + labelNames: [ + 'identifier', + 'testMode', + 'sourceType', + 'destinationType', + 'k8_namespace', + 'errored', + 'statusCode', + 'transformationId', + 'workspaceId', + ], + }, ]; metrics.forEach((metric) => { @@ -978,6 +989,17 @@ class Prometheus { metric.labelNames, metric.buckets, ); + } else if (metric.type === 'summary') { + if (enableSummaryMetrics) { + this.newSummaryStat( + appendPrefix(metric.name), + metric.help, + metric.labelNames, + metric.percentiles, + metric.maxAge, + metric.ageBuckets, + ); + } } else { logger.error( `Prometheus: Metric creation failed. Name: ${metric.name}. Invalid type: ${metric.type}`, diff --git a/src/util/redis/redisConnector.js b/src/util/redis/redisConnector.js index 84d578d3b3..7dc20a305d 100644 --- a/src/util/redis/redisConnector.js +++ b/src/util/redis/redisConnector.js @@ -1,7 +1,7 @@ const Redis = require('ioredis'); const { RedisError } = require('@rudderstack/integrations-lib'); -const log = require('../../logger'); const stats = require('../stats'); +const logger = require('../../logger'); const timeoutPromise = () => new Promise((_, reject) => { @@ -29,13 +29,13 @@ const RedisDB = { stats.increment('redis_error', { operation: 'redis_down', }); - log.error(`Redis is down at ${this.host}:${this.port}`); + logger.error(`Redis is down at ${this.host}:${this.port}`); return false; // stop retrying }, tls: {}, }); this.client.on('ready', () => { - log.info(`Connected to redis at ${this.host}:${this.port}`); + logger.info(`Connected to redis at ${this.host}:${this.port}`); }); } }, @@ -89,7 +89,7 @@ const RedisDB = { stats.increment('redis_error', { operation: 'get', }); - log.error(`Error getting value from Redis: ${e}`); + logger.error(`Error getting value from Redis: ${e}`); throw new RedisError(`Error getting value from Redis: ${e}`); } }, @@ -124,13 +124,13 @@ const RedisDB = { stats.increment('redis_error', { operation: 'set', }); - log.error(`Error setting value in Redis due ${e}`); + logger.error(`Error setting value in Redis due ${e}`); throw new RedisError(`Error setting value in Redis due ${e}`); } }, async disconnect() { if (process.env.USE_REDIS_DB && process.env.USE_REDIS_DB !== 'false') { - log.info(`Disconnecting from redis at ${this.host}:${this.port}`); + logger.info(`Disconnecting from redis at ${this.host}:${this.port}`); this.client.disconnect(); } }, diff --git a/src/util/redis/redisConnector.test.js b/src/util/redis/redisConnector.test.js index 840f222e37..659b32925b 100644 --- a/src/util/redis/redisConnector.test.js +++ b/src/util/redis/redisConnector.test.js @@ -3,6 +3,7 @@ const path = require('path'); const version = 'v0'; const { RedisDB } = require('./redisConnector'); jest.mock('ioredis', () => require('../../../test/__mocks__/redis')); + const sourcesList = ['shopify']; process.env.USE_REDIS_DB = 'true'; @@ -70,7 +71,7 @@ describe(`Redis Class Get Tests`, () => { data.forEach((dataPoint, index) => { it(`${index}. Redis Get- ${dataPoint.description}`, async () => { try { - const output = await RedisDB.getVal(dataPoint.input.value, (isObjExpected = false)); + const output = await RedisDB.getVal(dataPoint.input.value, false); expect(output).toEqual(dataPoint.output); } catch (error) { expect(error.message).toEqual(dataPoint.output.error); diff --git a/src/util/redis/testData/shopify_source.json b/src/util/redis/testData/shopify_source.json index 53c6047298..2120475baf 100644 --- a/src/util/redis/testData/shopify_source.json +++ b/src/util/redis/testData/shopify_source.json @@ -5,7 +5,8 @@ "user_id": "rudder01", "id": "shopify_test_get_items_fail", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test_get_items_fail", "email": "test@rudderstack.com", @@ -64,11 +65,8 @@ } }, "output": { - "outputToSource": { - "body": "T0s=", - "contentType": "text/plain" - }, - "statusCode": 200 + "error": "Error: Error setting value in Redis due Error: Connection is Closed", + "statusCode": 500 } }, { @@ -115,7 +113,8 @@ "input": { "cart_token": "shopifyGetAnonymousId", "query_parameters": { - "topic": ["checkouts_delete"] + "topic": ["checkouts_delete"], + "writeKey": ["wr"] }, "line_items": [], "note": null, @@ -154,7 +153,8 @@ "input": { "id": "shopify_test3", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test3", "line_items": [], @@ -252,7 +252,8 @@ "user_id": "rudder01", "id": "shopify_test_cart", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test_cart", "email": "test@rudderstack.com", @@ -1256,7 +1257,8 @@ "input": { "id": "shopify_test4", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test4", "line_items": [], diff --git a/src/util/stats.js b/src/util/stats.js index 9a32fd1de3..0aa13fc85c 100644 --- a/src/util/stats.js +++ b/src/util/stats.js @@ -4,6 +4,8 @@ const logger = require('../logger'); const enableStats = process.env.ENABLE_STATS !== 'false'; const statsClientType = process.env.STATS_CLIENT || 'statsd'; +// summary metrics are enabled by default. To disable set ENABLE_SUMMARY_METRICS='false'. +const enableSummaryMetrics = process.env.ENABLE_SUMMARY_METRICS !== 'false'; let statsClient; function init() { @@ -19,7 +21,7 @@ function init() { case 'prometheus': logger.info('setting up prometheus client'); - statsClient = new prometheus.Prometheus(); + statsClient = new prometheus.Prometheus(enableSummaryMetrics); break; default: @@ -38,6 +40,15 @@ const timing = (name, start, tags = {}) => { statsClient.timing(name, start, tags); }; +// timingSummary is used to record observations for a summary metric +const timingSummary = (name, start, tags = {}) => { + if (!enableStats || !statsClient || !enableSummaryMetrics) { + return; + } + + statsClient.timingSummary(name, start, tags); +}; + const increment = (name, tags = {}) => { if (!enableStats || !statsClient) { return; @@ -88,4 +99,13 @@ async function metricsController(ctx) { init(); -module.exports = { init, timing, increment, counter, gauge, histogram, metricsController }; +module.exports = { + init, + timing, + timingSummary, + increment, + counter, + gauge, + histogram, + metricsController, +}; diff --git a/src/util/statsd.js b/src/util/statsd.js index a32a6f6f30..7613de7975 100644 --- a/src/util/statsd.js +++ b/src/util/statsd.js @@ -21,6 +21,11 @@ class Statsd { this.statsdClient.timing(name, start, tags); } + // timingSummary is just a wrapper around timing for statsd.For prometheus, we will have to implement a different function. + timingSummary(name, start, tags = {}) { + this.statsdClient.timing(name, start, tags); + } + increment(name, tags = {}) { this.statsdClient.increment(name, 1, tags); } diff --git a/src/util/trackingPlan.js b/src/util/trackingPlan.js index a77265a5b8..ebfbc6049f 100644 --- a/src/util/trackingPlan.js +++ b/src/util/trackingPlan.js @@ -55,6 +55,11 @@ async function getEventSchema(tpId, tpVersion, eventType, eventName, workspaceId let eventSchema; const tp = await getTrackingPlan(tpId, tpVersion, workspaceId); + if (Object.hasOwn(tp, 'events')) { + const ev = tp.events.find((e) => e.name === eventName && e.eventType === eventType); + return ev?.rules; + } + if (eventType !== 'track') { if (Object.prototype.hasOwnProperty.call(tp.rules, eventType)) { eventSchema = tp.rules[eventType]; diff --git a/src/util/utils.js b/src/util/utils.js index 0ba6008368..eb5a011444 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -16,20 +16,21 @@ const LOCAL_HOST_NAMES_LIST = ['localhost', '127.0.0.1', '[::]', '[::1]']; const LOCALHOST_OCTET = '127.'; const RECORD_TYPE_A = 4; // ipv4 -const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { +const staticLookup = (transformationTags) => async (hostname, _, cb) => { let ips; const resolveStartTime = new Date(); try { ips = await resolver.resolve4(hostname); } catch (error) { + logger.error(`DNS Error Code: ${error.code} | Message : ${error.message}`); stats.timing('fetch_dns_resolve_time', resolveStartTime, { - transformerVersionId, + ...transformationTags, error: 'true', }); cb(null, `unable to resolve IP address for ${hostname}`, RECORD_TYPE_A); return; } - stats.timing('fetch_dns_resolve_time', resolveStartTime, { transformerVersionId }); + stats.timing('fetch_dns_resolve_time', resolveStartTime, transformationTags); if (ips.length === 0) { cb(null, `resolved empty list of IP address for ${hostname}`, RECORD_TYPE_A); @@ -47,9 +48,9 @@ const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { cb(null, ips[0], RECORD_TYPE_A); }; -const httpAgentWithDnsLookup = (scheme, transformerVersionId) => { +const httpAgentWithDnsLookup = (scheme, transformationTags) => { const httpModule = scheme === 'http' ? http : https; - return new httpModule.Agent({ lookup: staticLookup(transformerVersionId) }); + return new httpModule.Agent({ lookup: staticLookup(transformationTags) }); }; const blockLocalhostRequests = (url) => { @@ -73,7 +74,7 @@ const blockInvalidProtocolRequests = (url) => { } }; -const fetchWithDnsWrapper = async (transformerVersionId, ...args) => { +const fetchWithDnsWrapper = async (transformationTags, ...args) => { if (process.env.DNS_RESOLVE_FETCH_HOST !== 'true') { return await fetch(...args); } @@ -87,7 +88,7 @@ const fetchWithDnsWrapper = async (transformerVersionId, ...args) => { const fetchOptions = args[1] || {}; const schemeName = fetchURL.startsWith('https') ? 'https' : 'http'; // assign resolved agent to fetch - fetchOptions.agent = httpAgentWithDnsLookup(schemeName, transformerVersionId); + fetchOptions.agent = httpAgentWithDnsLookup(schemeName, transformationTags); return await fetch(fetchURL, fetchOptions); }; @@ -169,7 +170,8 @@ function processInfo() { } function logProcessInfo() { - logger.error(`Process info: `, util.inspect(processInfo(), false, null, true)); + const inspectedInfo = util.inspect(processInfo(), false, Infinity, true); + logger.error(`Process info: ${inspectedInfo}`); } // stringLiterals expected to be an array of strings. A line in trace should contain diff --git a/src/v0/destinations/active_campaign/data/ACIdentify.json b/src/v0/destinations/active_campaign/data/ACIdentify.json index 6cee9e72c6..134955d52e 100644 --- a/src/v0/destinations/active_campaign/data/ACIdentify.json +++ b/src/v0/destinations/active_campaign/data/ACIdentify.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": true }, diff --git a/src/v0/destinations/af/config.js b/src/v0/destinations/af/config.js index bc49706959..eb3b191950 100644 --- a/src/v0/destinations/af/config.js +++ b/src/v0/destinations/af/config.js @@ -57,6 +57,7 @@ const Event = { }; const ENDPOINT = 'https://api2.appsflyer.com/inappevent/'; +const ENDPOINT_V2 = 'https://api3.appsflyer.com/inappevent/'; const mappingConfig = getMappingConfig(ConfigCategory, __dirname); @@ -69,6 +70,7 @@ events.forEach((event) => { module.exports = { ConfigCategory, ENDPOINT, + ENDPOINT_V2, Event, mappingConfig, nameToEventMap, diff --git a/src/v0/destinations/af/transform.js b/src/v0/destinations/af/transform.js index d6c41937a1..a611dcc249 100644 --- a/src/v0/destinations/af/transform.js +++ b/src/v0/destinations/af/transform.js @@ -17,20 +17,32 @@ const { simpleProcessRouterDest, } = require('../../util'); -const { Event, ENDPOINT, ConfigCategory, mappingConfig, nameToEventMap } = require('./config'); +const { + Event, + ENDPOINT, + ENDPOINT_V2, + ConfigCategory, + mappingConfig, + nameToEventMap, +} = require('./config'); const { JSON_MIME_TYPE } = require('../../util/constant'); function responseBuilderSimple(payload, message, destination) { - const { androidAppId, appleAppId } = destination.Config; + const { androidAppId, appleAppId, sharingFilter, devKey, s2sKey, authVersion } = + destination.Config; let endpoint; const os = get(message, 'context.os.name'); // if ((os && os.toLowerCase() === "android") || (os && isAppleFamily(os))){ // if() // } + + const finalEndPoint = + isDefinedAndNotNull(authVersion) && authVersion === 'v2' ? ENDPOINT_V2 : ENDPOINT; + if (os && os.toLowerCase() === 'android' && androidAppId) { - endpoint = `${ENDPOINT}${androidAppId}`; + endpoint = `${finalEndPoint}${androidAppId}`; } else if (os && isAppleFamily(os) && appleAppId) { - endpoint = `${ENDPOINT}id${appleAppId}`; + endpoint = `${finalEndPoint}id${appleAppId}`; } else { throw new ConfigurationError( 'os name is required along with the respective appId eg. (os->android & Android App Id is required) or (os->ios & Apple App Id is required)', @@ -87,16 +99,19 @@ function responseBuilderSimple(payload, message, destination) { updatedPayload.bundleIdentifier = bundleIdentifier; } - const { sharingFilter, devKey } = destination.Config; + // const { sharingFilter, devKey } = destination.Config; if (isDefinedAndNotNullAndNotEmpty(sharingFilter)) { updatedPayload.sharing_filter = sharingFilter; } + const finalAuthentication = + isDefinedAndNotNull(authVersion) && authVersion === 'v2' ? s2sKey : devKey; + const response = defaultRequestConfig(); response.endpoint = endpoint; response.headers = { 'Content-Type': JSON_MIME_TYPE, - authentication: devKey, + authentication: finalAuthentication, }; response.method = defaultPostRequestConfig.requestMethod; response.body.JSON = removeUndefinedAndNullValues(updatedPayload); @@ -113,13 +128,9 @@ function getEventValueForUnIdentifiedTrackEvent(message) { return { eventValue }; } -function getEventValueMapFromMappingJson( - message, - mappingJson, - isMultiSupport, - addPropertiesAtRoot, -) { +function getEventValueMapFromMappingJson(message, mappingJson, isMultiSupport, config) { let eventValue = {}; + const { addPropertiesAtRoot, afCurrencyAtRoot } = config; if (addPropertiesAtRoot) { eventValue = message.properties; @@ -152,6 +163,9 @@ function getEventValueMapFromMappingJson( af_price: prices, }; } + if (afCurrencyAtRoot) { + eventValue.af_currency = message.properties.currency; + } eventValue = removeUndefinedValues(eventValue); if (Object.keys(eventValue).length > 0) { eventValue = JSON.stringify(eventValue); @@ -171,7 +185,7 @@ function processNonTrackEvents(message, eventName) { return payload; } -function processEventTypeTrack(message, addPropertiesAtRoot) { +function processEventTypeTrack(message, config) { let isMultiSupport = true; const evType = message.event && message.event.toLowerCase(); let category = ConfigCategory.DEFAULT; @@ -195,7 +209,7 @@ function processEventTypeTrack(message, addPropertiesAtRoot) { message, mappingConfig[category.name], isMultiSupport, - addPropertiesAtRoot, + config, ); payload.eventName = message.event; payload.eventCurrency = message?.properties?.currency; @@ -204,16 +218,29 @@ function processEventTypeTrack(message, addPropertiesAtRoot) { } function processSingleMessage(message, destination) { + const { devKey, s2sKey, authVersion, useRichEventName } = destination.Config; + + if (!isDefinedAndNotNull(authVersion) && !isDefinedAndNotNull(devKey)) { + throw new ConfigurationError('No authentication key is present. Aborting.'); + } + + if (isDefinedAndNotNull(authVersion) && authVersion === 'v2' && !isDefinedAndNotNull(s2sKey)) { + throw new ConfigurationError('s2s key is mandatory for v2 authorization. Aborting.'); + } + + if (isDefinedAndNotNull(authVersion) && authVersion === 'v1' && !isDefinedAndNotNull(devKey)) { + throw new ConfigurationError('dev key is mandatory for v1 authorization. Aborting.'); + } const messageType = message.type.toLowerCase(); let payload; switch (messageType) { case EventType.TRACK: { - payload = processEventTypeTrack(message, destination.Config.addPropertiesAtRoot); + payload = processEventTypeTrack(message, destination.Config); break; } case EventType.SCREEN: { let eventName; - if (destination.Config.useRichEventName === true) { + if (useRichEventName === true) { eventName = `Viewed ${ message.name || message.event || get(message, 'properties.name') || '' } Screen`; @@ -225,7 +252,7 @@ function processSingleMessage(message, destination) { } case EventType.PAGE: { let eventName; - if (destination.Config.useRichEventName === true) { + if (useRichEventName === true) { eventName = `Viewed ${message.name || get(message, 'properties.name') || ''} Page`; } else { eventName = EventType.PAGE; diff --git a/src/v0/destinations/algolia/config.js b/src/v0/destinations/algolia/config.js index 11b4ec99f2..4e20294dd2 100644 --- a/src/v0/destinations/algolia/config.js +++ b/src/v0/destinations/algolia/config.js @@ -5,6 +5,7 @@ const CONFIG_CATEGORIES = { TRACK: { type: 'track', name: 'AlgoliaTrack' }, }; const EVENT_TYPES = ['click', 'view', 'conversion']; +const ALLOWED_EVENT_SUBTYPES = ['addToCart', 'purchase']; const MAX_BATCH_SIZE = 1000; const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); module.exports = { @@ -12,4 +13,5 @@ module.exports = { MAX_BATCH_SIZE, EVENT_TYPES, trackMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK.name], + ALLOWED_EVENT_SUBTYPES, }; diff --git a/src/v0/destinations/algolia/data/AlgoliaTrack.json b/src/v0/destinations/algolia/data/AlgoliaTrack.json index bdc3449147..41f43af9cb 100644 --- a/src/v0/destinations/algolia/data/AlgoliaTrack.json +++ b/src/v0/destinations/algolia/data/AlgoliaTrack.json @@ -34,5 +34,15 @@ "destKey": "positions", "sourceKeys": "properties.positions", "required": false + }, + { + "destKey": "value", + "sourceKeys": "properties.value", + "required": false + }, + { + "destKey": "currency", + "sourceKeys": "properties.currency", + "required": false } ] diff --git a/src/v0/destinations/algolia/transform.js b/src/v0/destinations/algolia/transform.js index 8e9cd57e8b..33ae6f2101 100644 --- a/src/v0/destinations/algolia/transform.js +++ b/src/v0/destinations/algolia/transform.js @@ -16,7 +16,7 @@ const { handleRtTfSingleEventError, } = require('../../util/index'); -const { ENDPOINT, MAX_BATCH_SIZE, trackMapping } = require('./config'); +const { ENDPOINT, MAX_BATCH_SIZE, trackMapping, ALLOWED_EVENT_SUBTYPES } = require('./config'); const { genericpayloadValidator, @@ -38,6 +38,12 @@ const trackResponseBuilder = (message, { Config }) => { const eventMapping = eventTypeMapping(Config); payload.eventName = event; payload.eventType = getValueFromMessage(message, 'properties.eventType') || eventMapping[event]; + if ( + payload.eventType === 'conversion' && + ALLOWED_EVENT_SUBTYPES.includes(getValueFromMessage(message, 'properties.eventSubtype')) + ) { + payload.eventSubtype = getValueFromMessage(message, 'properties.eventSubtype'); + } if (!payload.eventType) { throw new InstrumentationError('eventType is mandatory for track call'); @@ -47,9 +53,13 @@ const trackResponseBuilder = (message, { Config }) => { if (event === 'product list viewed' || event === 'order completed') { const products = getValueFromMessage(message, 'properties.products'); if (products) { - const { objectList, positionList } = createObjectArray(products, payload.eventType); + const { objectList, positionList, objectData } = createObjectArray( + products, + payload.eventType, + ); const objLen = objectList.length; const posLen = positionList.length; + const objDataLen = objectData.length; if (objLen > 0) { payload.objectIDs = objectList; payload.objectIDs.splice(20); @@ -58,10 +68,20 @@ const trackResponseBuilder = (message, { Config }) => { payload.positions = positionList; payload.positions.splice(20); } + + if (objDataLen > 0) { + payload.objectData = objectData; + } // making size of object list and position list equal if (posLen > 0 && objLen > 0 && posLen !== objLen) { throw new InstrumentationError('length of objectId and position should be equal'); } + + if (objDataLen > 0 && objLen > 0 && objDataLen !== objLen) { + throw new InstrumentationError( + 'length of objectId and properties.products array should be equal', + ); + } } } // for all events either filter or objectID should be there diff --git a/src/v0/destinations/algolia/util.js b/src/v0/destinations/algolia/util.js index eddb4dc16d..b10097bbee 100644 --- a/src/v0/destinations/algolia/util.js +++ b/src/v0/destinations/algolia/util.js @@ -1,4 +1,8 @@ -const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + InstrumentationError, + isDefined, + removeUndefinedAndNullValues, +} = require('@rudderstack/integrations-lib'); const logger = require('../../../logger'); const { EVENT_TYPES } = require('./config'); @@ -66,6 +70,8 @@ const genericpayloadValidator = (payload) => { const createObjectArray = (objects, eventType) => { const objectList = []; const positionList = []; + // eslint-disable-next-line sonarjs/no-unused-collection + const objectData = []; if (objects.length > 0) { objects.forEach((object, index) => { if (object.objectId) { @@ -80,13 +86,22 @@ const createObjectArray = (objects, eventType) => { } } else { objectList.push(object.objectId); + if (eventType === 'conversion') { + const singleObjData = { + queryID: isDefined(object.queryID) ? `${object.queryID}` : null, + price: isDefined(object.price) ? `${object.price}` : null, + quantity: object.quantity, + discount: isDefined(object.discount) ? `${object.discount}` : null, + }; + objectData.push(removeUndefinedAndNullValues(singleObjData)); + } } } else { logger.error(`object at index ${index} dropped. objectId is required.`); } }); } - return { objectList, positionList }; + return { objectList, positionList, objectData }; }; const clickPayloadValidator = (payload) => { @@ -128,9 +143,28 @@ const clickPayloadValidator = (payload) => { return updatedPayload; }; +// ref : https://www.algolia.com/doc/rest-api/insights/#method-param-objectdata-2:~:text=currency-,%23,currency%20as%20ISO%2D4217%20currency%20code%2C%20such%20as%20USD%20or%20EUR.,-ObjectData +function validatePayload(payload) { + if (payload.objectData && Array.isArray(payload.objectData)) { + const hasRelevantFields = payload.objectData.some( + (obj) => + obj.hasOwnProperty('price') || + obj.hasOwnProperty('quantity') || + obj.hasOwnProperty('discount'), + ); + + if (hasRelevantFields && !payload.currency) { + throw new InstrumentationError( + 'Currency missing when objectData fields has price informations.', + ); + } + } +} + module.exports = { genericpayloadValidator, createObjectArray, eventTypeMapping, clickPayloadValidator, + validatePayload, }; diff --git a/src/v0/destinations/algolia/util.test.js b/src/v0/destinations/algolia/util.test.js new file mode 100644 index 0000000000..850d93f1c6 --- /dev/null +++ b/src/v0/destinations/algolia/util.test.js @@ -0,0 +1,58 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { validatePayload } = require('./util'); + +describe('validatePayload', () => { + // When payload is valid and contains relevant fields and currency + it('should validate payload when it is valid and contains relevant fields and currency', () => { + const payload = { + objectData: [ + { price: 10, quantity: 2, discount: 0.1 }, + { price: 20, quantity: 1, discount: 0 }, + ], + currency: 'USD', + }; + + expect(() => validatePayload(payload)).not.toThrow(); + }); + + // When payload contains objects with missing relevant fields + it('should throw an error when payload contains objects with missing relevant fields', () => { + const payload = { + objectData: [ + { price: 10, quantity: 2 }, + { price: 20, discount: 0 }, + ], + }; + + expect(() => validatePayload(payload)).toThrow(InstrumentationError); + }); + + // When payload is valid and contains relevant fields but no currency + it('should throw an InstrumentationError when currency is missing', () => { + const payload = { + objectData: [ + { price: 10, quantity: 2, discount: 0.1 }, + { price: 20, quantity: 1, discount: 0 }, + ], + }; + + expect(() => validatePayload(payload)).toThrow(InstrumentationError); + }); + + // When payload is valid but does not contain relevant fields + it('should not throw an error when payload does not contain relevant fields', () => { + const payload = { + objectData: [{ position: 'Product A' }, { position: 'Product B' }], + currency: 'USD', + }; + + expect(() => validatePayload(payload)).not.toThrow(); + }); + + // When payload is empty + it('should not throw an error when payload is empty', () => { + const payload = {}; + + expect(() => validatePayload(payload)).not.toThrow(); + }); +}); diff --git a/src/v0/destinations/am/transform.js b/src/v0/destinations/am/transform.js index bc08315fa8..ce157e7674 100644 --- a/src/v0/destinations/am/transform.js +++ b/src/v0/destinations/am/transform.js @@ -336,7 +336,6 @@ const getDefaultResponseData = (message, rawPayload, evType, groupInfo) => { return { groups, rawPayload }; }; - const getResponseData = (evType, destination, rawPayload, message, groupInfo) => { let groups; @@ -526,6 +525,9 @@ const responseBuilderSimple = ( ...campaign, }; + // we are updating the payload with skip_user_properties_sync + AMUtils.updateWithSkipAttribute(message, rawPayload); + const respData = getResponseData(evType, destination, rawPayload, message, groupInfo); const { groups, rawPayload: updatedRawPayload } = respData; diff --git a/src/v0/destinations/am/util.test.js b/src/v0/destinations/am/util.test.js index 723ff3a302..498980d182 100644 --- a/src/v0/destinations/am/util.test.js +++ b/src/v0/destinations/am/util.test.js @@ -1,4 +1,9 @@ -const { getUnsetObj, validateEventType, userPropertiesPostProcess } = require('./utils'); +const { + getUnsetObj, + validateEventType, + userPropertiesPostProcess, + updateWithSkipAttribute, +} = require('./utils'); describe('getUnsetObj', () => { it("should return undefined when 'message.integrations.Amplitude.fieldsToUnset' is not array", () => { @@ -164,3 +169,31 @@ describe('userPropertiesPostProcess', () => { }); }); }); + +describe('updateWithSkipAttribute', () => { + // when 'skipUserPropertiesSync ' is present in 'integrations.Amplitude', return the original payload. + it("should return the original payload when 'skipUserPropertiesSync' is present", () => { + const message = { integrations: { Amplitude: { skipUserPropertiesSync: true } } }; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value', $skip_user_properties_sync: true }; + updateWithSkipAttribute(message, payload); + expect(expectedPayload).toEqual(payload); + }); + + // When 'skipUserPropertiesSync' is not present in 'integrations.Amplitude', return the original payload. + it("should return the original payload when 'skipUserPropertiesSync' is not present", () => { + const message = { integrations: { Amplitude: {} } }; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value' }; + updateWithSkipAttribute(message, payload); + expect(payload).toEqual(expectedPayload); + }); + // When 'message' is null, return null. + it("should return null when 'message' is null", () => { + const message = null; + const payload = { key: 'value' }; + const expectedPayload = { key: 'value' }; + updateWithSkipAttribute(message, payload); + expect(payload).toEqual(expectedPayload); + }); +}); diff --git a/src/v0/destinations/am/utils.js b/src/v0/destinations/am/utils.js index 4d4fd5dc37..8de899182b 100644 --- a/src/v0/destinations/am/utils.js +++ b/src/v0/destinations/am/utils.js @@ -11,6 +11,7 @@ const get = require('get-value'); const uaParser = require('@amplitude/ua-parser-js'); const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const set = require('set-value'); const logger = require('../../../logger'); const { isDefinedAndNotNull } = require('../../util'); @@ -110,6 +111,13 @@ const getUnsetObj = (message) => { return unsetObject; }; +const updateWithSkipAttribute = (message, payload) => { + const skipAttribute = get(message, 'integrations.Amplitude.skipUserPropertiesSync'); + if (skipAttribute) { + set(payload, '$skip_user_properties_sync', true); + } +}; + /** * Check for evType as in some cases, like when the page name is absent, * either the template depends only on the event.name or there is no template provided by user @@ -123,7 +131,6 @@ const validateEventType = (evType) => { } }; - const userPropertiesPostProcess = (rawPayload) => { const operationList = [ '$setOnce', @@ -187,5 +194,6 @@ module.exports = { getEventId, getUnsetObj, validateEventType, - userPropertiesPostProcess + userPropertiesPostProcess, + updateWithSkipAttribute, }; diff --git a/src/v0/destinations/awin/transform.js b/src/v0/destinations/awin/transform.js index 49a115c1ff..0e1e220548 100644 --- a/src/v0/destinations/awin/transform.js +++ b/src/v0/destinations/awin/transform.js @@ -2,10 +2,13 @@ const { InstrumentationError, ConfigurationError } = require('@rudderstack/integ const { BASE_URL, ConfigCategory, mappingConfig } = require('./config'); const { defaultRequestConfig, constructPayload, simpleProcessRouterDest } = require('../../util'); -const { getParams } = require('./utils'); +const { getParams, trackProduct, populateCustomTransactionProperties } = require('./utils'); +const { FilteredEventsError } = require('../../util/errorTypes'); const responseBuilder = (message, { Config }) => { - const { advertiserId, eventsToTrack } = Config; + const { advertiserId, eventsToTrack, customFieldMap } = Config; + const { event, properties } = message; + let finalParams = {}; const payload = constructPayload(message, mappingConfig[ConfigCategory.TRACK.name]); @@ -17,17 +20,28 @@ const responseBuilder = (message, { Config }) => { }); // if the event is present in eventsList - if (eventsList.includes(message.event)) { + if (eventsList.includes(event)) { params = getParams(payload.params, advertiserId); + const productTrackObject = trackProduct(properties, advertiserId, params.parts); + const customTransactionProperties = populateCustomTransactionProperties( + properties, + customFieldMap, + ); + + finalParams = { + ...params, + ...productTrackObject, + ...customTransactionProperties, + }; } else { - throw new InstrumentationError( - "Event is not present in 'Events to Track' list. Aborting message.", - 400, + throw new FilteredEventsError( + "Event is not present in 'Events to Track' list. Dropping the event.", + 298, ); } } const response = defaultRequestConfig(); - response.params = params; + response.params = finalParams; response.endpoint = BASE_URL; return response; diff --git a/src/v0/destinations/awin/utils.js b/src/v0/destinations/awin/utils.js index 1616227c55..715fb5818d 100644 --- a/src/v0/destinations/awin/utils.js +++ b/src/v0/destinations/awin/utils.js @@ -1,3 +1,6 @@ +const { getHashFromArray } = require('@rudderstack/integrations-lib'); +const lodash = require('lodash'); + /** * Returns final params * @param {*} params @@ -24,6 +27,77 @@ const getParams = (parameters, advertiserId) => { return params; }; +const areAllValuesDefined = (obj) => + lodash.every(lodash.values(obj), (value) => !lodash.isUndefined(value)); + +const buildProductPayloadString = (payload) => { + // URL-encode each value, and join back with the same key. + const encodedPayload = Object.entries(payload).reduce((acc, [key, value]) => { + // Encode each value. Assuming that all values are either strings or can be + // safely converted to strings. + acc[key] = encodeURIComponent(value); + return acc; + }, {}); + + return `AW:P|${encodedPayload.advertiserId}|${encodedPayload.orderReference}|${encodedPayload.productId}|${encodedPayload.productName}|${encodedPayload.productItemPrice}|${encodedPayload.productQuantity}|${encodedPayload.productSku}|${encodedPayload.commissionGroupCode}|${encodedPayload.productCategory}`; +}; + +// ref: https://wiki.awin.com/index.php/Advertiser_Tracking_Guide/Product_Level_Tracking#PLT_Via_Conversion_Pixel +const trackProduct = (properties, advertiserId, commissionParts) => { + const transformedProductInfoObj = {}; + if ( + properties?.products && + Array.isArray(properties?.products) && + properties.products.length > 0 + ) { + const productsArray = properties.products; + let productIndex = 0; + productsArray.forEach((product) => { + const productPayloadNew = { + advertiserId, + orderReference: + properties.order_id || + properties.orderId || + properties.orderReference || + properties.order_reference, + productId: product.product_id || product.productId, + productName: product.name, + productItemPrice: product.price, + productQuantity: product.quantity, + productSku: product.sku || '', + commissionGroupCode: commissionParts || 'DEFAULT', + productCategory: product.category || '', + }; + if (areAllValuesDefined(productPayloadNew)) { + transformedProductInfoObj[`bd[${productIndex}]`] = + buildProductPayloadString(productPayloadNew); + productIndex += 1; + } + }); + } + return transformedProductInfoObj; +}; + +// ref: https://wiki.awin.com/index.php/Advertiser_Tracking_Guide/Product_Level_Tracking#PLT_Via_Conversion_Pixel +const populateCustomTransactionProperties = (properties, customFieldMap) => { + const customObject = {}; + const customPropertyPattern = '^\\s*p\\d+\\s*$'; + const regex = new RegExp(customPropertyPattern, 'i'); + const propertyMap = getHashFromArray(customFieldMap, 'from', 'to', false); + Object.entries(propertyMap).forEach(([rudderProperty, awinProperty]) => { + if (regex.test(awinProperty)) { + const fieldValue = properties[rudderProperty]; + if (fieldValue) { + customObject[awinProperty] = fieldValue; + } + } + }); + return customObject; +}; + module.exports = { getParams, + trackProduct, + buildProductPayloadString, + populateCustomTransactionProperties, }; diff --git a/src/v0/destinations/awin/utils.test.js b/src/v0/destinations/awin/utils.test.js new file mode 100644 index 0000000000..ca7d079b1b --- /dev/null +++ b/src/v0/destinations/awin/utils.test.js @@ -0,0 +1,194 @@ +const { + buildProductPayloadString, + trackProduct, + populateCustomTransactionProperties, +} = require('./utils'); + +describe('buildProductPayloadString', () => { + // Should correctly build the payload string with all fields provided + it('should correctly build the payload string with all fields provided', () => { + const payload = { + advertiserId: '123', + orderReference: 'order123', + productId: 'prod123', + productName: 'Product 1', + productItemPrice: '10.99', + productQuantity: '2', + productSku: 'sku123', + commissionGroupCode: 'DEFAULT', + productCategory: 'Category 1', + }; + + const expected = 'AW:P|123|order123|prod123|Product%201|10.99|2|sku123|DEFAULT|Category%201'; + const result = buildProductPayloadString(payload); + + expect(result).toBe(expected); + }); + + // Should correctly handle extremely long string values for all fields + it('should correctly handle extremely long string values for all fields', () => { + const payload = { + advertiserId: '123', + orderReference: 'order123', + productId: 'prod123', + productName: 'Product 1'.repeat(100000), + productItemPrice: '10.99'.repeat(100000), + productQuantity: '2'.repeat(100000), + productSku: 'sku123'.repeat(100000), + commissionGroupCode: 'DEFAULT', + productCategory: 'Category 1'.repeat(100000), + }; + + const expected = `AW:P|123|order123|prod123|${encodeURIComponent('Product 1'.repeat(100000))}|${encodeURIComponent('10.99'.repeat(100000))}|${encodeURIComponent('2'.repeat(100000))}|${encodeURIComponent('sku123'.repeat(100000))}|DEFAULT|${encodeURIComponent('Category 1'.repeat(100000))}`; + const result = buildProductPayloadString(payload); + + expect(result).toBe(expected); + }); +}); + +describe('trackProduct', () => { + // Given a valid 'properties' object with a non-empty 'products' array, it should transform each product into a valid payload string and return an object with the transformed products. + it("should transform each product into a valid payload string and return an object with the transformed products when given a valid 'properties' object with a non-empty 'products' array", () => { + // Arrange + const properties = { + products: [ + { + product_id: '123', + name: 'Product 1', + price: 10, + quantity: 1, + sku: 'SKU123', + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: 'Category 2', + }, + ], + order_id: 'order123', + }; + const advertiserId = 'advertiser123'; + const commissionParts = 'COMMISSION'; + + // Act + const result = trackProduct(properties, advertiserId, commissionParts); + + // Assert + expect(result).toEqual({ + 'bd[0]': 'AW:P|advertiser123|order123|123|Product%201|10|1|SKU123|COMMISSION|Category%201', + 'bd[1]': 'AW:P|advertiser123|order123|456|Product%202|20|2|SKU456|COMMISSION|Category%202', + }); + }); + + // Given an invalid 'properties' object, it should return an empty object. + it("should return an empty object when given an invalid 'properties' object", () => { + // Arrange + const properties = {}; + const advertiserId = 'advertiser123'; + const commissionParts = 'COMMISSION'; + + // Act + const result = trackProduct(properties, advertiserId, commissionParts); + + // Assert + expect(result).toEqual({}); + }); + + it('should ignore the product which has missing properties', () => { + // Arrange + const properties = { + products: [ + { + price: 10, + quantity: 1, + sku: 'SKU123', + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: 'Category 2', + }, + ], + order_id: 'order123', + }; + const advertiserId = 'advertiser123'; + const commissionParts = 'COMMISSION'; + + // Act + const result = trackProduct(properties, advertiserId, commissionParts); + + // Assert + expect(result).toEqual({ + 'bd[0]': 'AW:P|advertiser123|order123|456|Product%202|20|2|SKU456|COMMISSION|Category%202', + }); + }); + + it('category and sku if undefined we put blank', () => { + // Arrange + const properties = { + products: [ + { + product_id: '123', + name: 'Product 1', + price: 10, + quantity: 1, + sku: undefined, + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: undefined, + }, + ], + order_id: 'order123', + }; + const advertiserId = 'advertiser123'; + const commissionParts = 'COMMISSION'; + + // Act + const result = trackProduct(properties, advertiserId, commissionParts); + + // Assert + expect(result).toEqual({ + 'bd[0]': 'AW:P|advertiser123|order123|123|Product%201|10|1||COMMISSION|Category%201', + 'bd[1]': 'AW:P|advertiser123|order123|456|Product%202|20|2|SKU456|COMMISSION|', + }); + }); +}); + +describe('populateCustomTransactionProperties', () => { + // The function should correctly map properties from the input object to the output object based on the customFieldMap. + it('should correctly map properties from the input object to the output object based on the customFieldMap', () => { + const properties = { + rudderProperty1: 'value1', + rudderProperty2: 123, + rudderProperty3: 'value3', + rudderProperty4: 234, + }; + const customFieldMap = [ + { from: 'rudderProperty1', to: 'p1' }, + { from: 'rudderProperty2', to: 'p2' }, + { from: 'rudderProperty4', to: 'anotherp2' }, + ]; + const expectedOutput = { + p1: 'value1', + p2: 123, + }; + + const result = populateCustomTransactionProperties(properties, customFieldMap); + + expect(result).toEqual(expectedOutput); + }); +}); diff --git a/src/v0/destinations/azure_datalake/transform.js b/src/v0/destinations/azure_datalake/transform.js index 8d29c70e06..6c97e8671f 100644 --- a/src/v0/destinations/azure_datalake/transform.js +++ b/src/v0/destinations/azure_datalake/transform.js @@ -1,25 +1,24 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const azureDatalake = 'azure_datalake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'azure_datalake'; function getDataTypeOverride() {} function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = azureDatalake; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, getDataTypeOverride, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } -exports.process = process; +module.exports = { + provider, + process, +}; diff --git a/src/v0/destinations/azure_synapse/transform.js b/src/v0/destinations/azure_synapse/transform.js index d98f269475..a80ad7cbdc 100644 --- a/src/v0/destinations/azure_synapse/transform.js +++ b/src/v0/destinations/azure_synapse/transform.js @@ -1,28 +1,25 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const azureSynapse = 'azure_synapse'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'azure_synapse'; function getDataTypeOverride() {} function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = azureSynapse; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, getDataTypeOverride, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/blueshift/data/blueshiftGroupConfig.json b/src/v0/destinations/blueshift/data/blueshiftGroupConfig.json index 41e846c509..e246a059d9 100644 --- a/src/v0/destinations/blueshift/data/blueshiftGroupConfig.json +++ b/src/v0/destinations/blueshift/data/blueshiftGroupConfig.json @@ -13,7 +13,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/blueshift/data/blueshiftIdentifyConfig.json b/src/v0/destinations/blueshift/data/blueshiftIdentifyConfig.json index cd099b2cc3..506c313aa1 100644 --- a/src/v0/destinations/blueshift/data/blueshiftIdentifyConfig.json +++ b/src/v0/destinations/blueshift/data/blueshiftIdentifyConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": true }, diff --git a/src/v0/destinations/blueshift/data/blueshiftTrackConfig.json b/src/v0/destinations/blueshift/data/blueshiftTrackConfig.json index 5fb1be691a..9841450754 100644 --- a/src/v0/destinations/blueshift/data/blueshiftTrackConfig.json +++ b/src/v0/destinations/blueshift/data/blueshiftTrackConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/bq/transform.js b/src/v0/destinations/bq/transform.js index e9e496f6a4..8c8be140a9 100644 --- a/src/v0/destinations/bq/transform.js +++ b/src/v0/destinations/bq/transform.js @@ -1,10 +1,6 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const bigquery = 'bq'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'bq'; function getDataTypeOverride() {} @@ -13,8 +9,7 @@ function process(event) { const whIDResolve = event.request.query.whIDResolve === 'true' || false; const whStoreEvent = event.destination.Config.storeFullEvent === true; const destJsonPaths = event.destination?.Config?.jsonPaths || ''; - const provider = bigquery; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -23,10 +18,12 @@ function process(event) { provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, destJsonPaths, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/braze/braze.util.test.js b/src/v0/destinations/braze/braze.util.test.js index 7b6a93d359..460f1db565 100644 --- a/src/v0/destinations/braze/braze.util.test.js +++ b/src/v0/destinations/braze/braze.util.test.js @@ -1,6 +1,6 @@ const _ = require('lodash'); const { handleHttpRequest } = require('../../../adapters/network'); -const { BrazeDedupUtility, addAppId, getPurchaseObjs } = require('./util'); +const { BrazeDedupUtility, addAppId, getPurchaseObjs, setAliasObject } = require('./util'); const { processBatch } = require('./util'); const { removeUndefinedAndNullValues, @@ -739,7 +739,7 @@ describe('dedup utility tests', () => { }); }); - test('returns null if all keys are in BRAZE_NON_BILLABLE_ATTRIBUTES', () => { + test('returns only non-billable attribute if there is key of BRAZE_NON_BILLABLE_ATTRIBUTES', () => { const userData = { external_id: '123', country: 'US', @@ -757,7 +757,7 @@ describe('dedup utility tests', () => { }; store.set('123', storeData); const result = BrazeDedupUtility.deduplicate(userData, store); - expect(result).toBeNull(); + expect(result).toEqual({ country: 'US', external_id: '123', language: 'en' }); }); test('returns null if all keys have $add, $update, or $remove properties', () => { @@ -1421,3 +1421,120 @@ describe('getPurchaseObjs', () => { ]); }); }); + +describe('setAliasObject function', () => { + // Test when integrationsObj has both alias_name and alias_label + test('should set user_alias from integrationsObj if alias_name and alias_label are defined', () => { + const payload = {}; + const result = setAliasObject(payload, { + anonymousId: '12345', + integrations: { + BRAZE: { + alias: { + alias_name: 'user123', + alias_label: 'customer_id', + }, + }, + }, + }); + + expect(result).toEqual({ + user_alias: { + alias_name: 'user123', + alias_label: 'customer_id', + }, + }); + }); + + // Test when integrationsObj is missing alias_name or alias_label + test('should set user_alias with anonymousId as alias_name and "rudder_id" as alias_label if integrationsObj does not have alias_name or alias_label', () => { + const message = { + anonymousId: '12345', + }; + const payload = {}; + const result = setAliasObject(payload, message); + + expect(result).toEqual({ + user_alias: { + alias_name: '12345', + alias_label: 'rudder_id', + }, + }); + }); + + // Test when message has no anonymousId and integrationsObj is missing + test('should return payload unchanged if message has no anonymousId and integrationsObj is missing', () => { + const message = {}; + const payload = {}; + const result = setAliasObject(payload, message); + + expect(result).toEqual(payload); + }); + + test('should set user_alias from integrationsObj if alias_name and alias_label are defined', () => { + const payload = {}; + const result = setAliasObject(payload, { + anonymousId: '12345', + integrations: { + BRAZE: { + alias: { + alias_name: 'user123', + alias_label: 'customer_id', + }, + }, + }, + }); + + expect(result).toEqual({ + user_alias: { + alias_name: 'user123', + alias_label: 'customer_id', + }, + }); + }); + + test('should set user_alias from integrationsObj if alias_name and alias_label either is not defined', () => { + const payload = {}; + const result = setAliasObject(payload, { + anonymousId: '12345', + integrations: { + BRAZE: { + alias: { + alias_name: null, + alias_label: 'customer_id', + }, + }, + }, + }); + + expect(result).toEqual({ + user_alias: { + alias_name: '12345', + alias_label: 'rudder_id', + }, + }); + }); + + test('should set user_alias from integrationsObj if alias_name and alias_label either is not defined', () => { + const payload = {}; + const result = setAliasObject(payload, { + anonymousId: '12345', + userID: 'user123', + integrations: { + BRAZE: { + alias: { + alias_name: 'rudder_id-123', + alias_label: 'customer_id', + }, + }, + }, + }); + + expect(result).toEqual({ + user_alias: { + alias_name: 'rudder_id-123', + alias_label: 'customer_id', + }, + }); + }); +}); diff --git a/src/v0/destinations/braze/transform.js b/src/v0/destinations/braze/transform.js index d45640272e..3d6a99d424 100644 --- a/src/v0/destinations/braze/transform.js +++ b/src/v0/destinations/braze/transform.js @@ -12,7 +12,9 @@ const { setExternalIdOrAliasObject, getPurchaseObjs, setExternalId, - setAliasObjectWithAnonId, + setAliasObject, + collectStatsForAliasFailure, + collectStatsForAliasMissConfigurations, } = require('./util'); const tags = require('../../util/tags'); const { EventType, MappedToDestinationKey } = require('../../../constants'); @@ -26,6 +28,7 @@ const { simpleProcessRouterDestSync, simpleProcessRouterDest, isNewStatusCodesAccepted, + getDestinationExternalID, } = require('../../util'); const { ConfigCategory, @@ -81,7 +84,7 @@ function buildResponse(message, destination, properties, endpoint) { function getIdentifyPayload(message) { let payload = {}; - payload = setAliasObjectWithAnonId(payload, message); + payload = setAliasObject(payload, message); payload = setExternalId(payload, message); return { aliases_to_identify: [payload], merge_behavior: 'merge' }; } @@ -201,12 +204,6 @@ function getUserAttributesObject(message, mappingJson, destination) { * @param {*} destination */ async function processIdentify(message, destination) { - // override userId with externalId in context(if present) and event is mapped to destination - const mappedToDestination = get(message, MappedToDestinationKey); - if (mappedToDestination) { - adduserIdFromExternalId(message); - } - const identifyPayload = getIdentifyPayload(message); const identifyEndpoint = getIdentifyEndpoint(getEndpointFromConfig(destination)); const { processedResponse: brazeIdentifyResp } = await handleHttpRequest( @@ -228,6 +225,7 @@ async function processIdentify(message, destination) { endpointPath: '/users/identify', }, ); + if (!isHttpStatusSuccess(brazeIdentifyResp.status)) { throw new NetworkError( `Braze identify failed - ${JSON.stringify(brazeIdentifyResp.response)}`, @@ -238,6 +236,8 @@ async function processIdentify(message, destination) { brazeIdentifyResp.response, ); } + + collectStatsForAliasFailure(brazeIdentifyResp.response, destination.ID); } function processTrackWithUserAttributes( @@ -507,10 +507,19 @@ async function process(event, processParams = { userStore: new Map() }, reqMetad processParams, ); break; - case EventType.IDENTIFY: + case EventType.IDENTIFY: { category = ConfigCategory.IDENTIFY; - if (message.anonymousId) { + // override userId with externalId in context(if present) and event is mapped to destination + const mappedToDestination = get(message, MappedToDestinationKey); + if (mappedToDestination) { + adduserIdFromExternalId(message); + } + const brazeExternalID = + getDestinationExternalID(message, 'brazeExternalId') || message.userId; + if (message.anonymousId && brazeExternalID) { await processIdentify(message, destination); + } else { + collectStatsForAliasMissConfigurations(destination.ID); } response = processTrackWithUserAttributes( message, @@ -520,6 +529,7 @@ async function process(event, processParams = { userStore: new Map() }, reqMetad reqMetadata, ); break; + } case EventType.GROUP: response = processGroup(message, destination); break; diff --git a/src/v0/destinations/braze/util.js b/src/v0/destinations/braze/util.js index 5f1f1e6205..45063d0ba2 100644 --- a/src/v0/destinations/braze/util.js +++ b/src/v0/destinations/braze/util.js @@ -1,6 +1,7 @@ /* eslint-disable */ const _ = require('lodash'); const get = require('get-value'); +const logger = require('../../../logger'); const stats = require('../../../util/stats'); const { handleHttpRequest } = require('../../../adapters/network'); const { @@ -26,7 +27,7 @@ const { const { JSON_MIME_TYPE, HTTP_STATUS_CODES } = require('../../util/constant'); const { isObject } = require('../../util'); const { removeUndefinedValues, getIntegrationsObj } = require('../../util'); -const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { InstrumentationError, isDefined } = require('@rudderstack/integrations-lib'); const getEndpointFromConfig = (destination) => { // Init -- mostly for test cases @@ -284,12 +285,17 @@ const BrazeDedupUtility = { return true; }); - if (keys.length === 0) { - return null; + if (keys.length > 0) { + keys.forEach((key) => { + if (!_.isEqual(userData[key], storedUserData[key])) { + deduplicatedUserData[key] = userData[key]; + } + }); } - keys.forEach((key) => { - if (!_.isEqual(userData[key], storedUserData[key])) { + // add non billable attributes back to the deduplicated user object + BRAZE_NON_BILLABLE_ATTRIBUTES.forEach((key) => { + if (isDefined(userData[key])) { deduplicatedUserData[key] = userData[key]; } }); @@ -305,13 +311,6 @@ const BrazeDedupUtility = { const identifier = external_id || user_alias?.alias_name; store.set(identifier, { ...storedUserData, ...deduplicatedUserData }); - // add non billable attributes back to the deduplicated user object - BRAZE_NON_BILLABLE_ATTRIBUTES.forEach((key) => { - if (isDefinedAndNotNull(userData[key])) { - deduplicatedUserData[key] = userData[key]; - } - }); - return removeUndefinedValues(deduplicatedUserData); }, }; @@ -520,8 +519,18 @@ function setExternalId(payload, message) { return payload; } -function setAliasObjectWithAnonId(payload, message) { - if (message.anonymousId) { +function setAliasObject(payload, message) { + const integrationsObj = getIntegrationsObj(message, 'BRAZE'); + if ( + isDefinedAndNotNull(integrationsObj?.alias?.alias_name) && + isDefinedAndNotNull(integrationsObj?.alias?.alias_label) + ) { + const { alias_name, alias_label } = integrationsObj.alias; + payload.user_alias = { + alias_name, + alias_label, + }; + } else if (message.anonymousId) { payload.user_alias = { alias_name: message.anonymousId, alias_label: 'rudder_id', @@ -538,7 +547,7 @@ function setExternalIdOrAliasObject(payload, message) { // eslint-disable-next-line no-underscore-dangle payload._update_existing_only = false; - return setAliasObjectWithAnonId(payload, message); + return setAliasObject(payload, message); } function addMandatoryPurchaseProperties(productId, price, currencyCode, quantity, timestamp) { @@ -561,7 +570,8 @@ function getPurchaseObjs(message, config) { 'Invalid Order Completed event: Properties object is missing in the message', ); } - const { products, currency: currencyCode } = properties; + const { currency: currencyCode } = properties; + let { products } = properties; if (!products) { throw new InstrumentationError( 'Invalid Order Completed event: Products array is missing in the message', @@ -572,6 +582,7 @@ function getPurchaseObjs(message, config) { throw new InstrumentationError('Invalid Order Completed event: Products is not an array'); } + products = products.filter((product) => isDefinedAndNotNull(product)); if (products.length === 0) { throw new InstrumentationError('Invalid Order Completed event: Products array is empty'); } @@ -657,6 +668,44 @@ function getPurchaseObjs(message, config) { return purchaseObjs; } +const collectStatsForAliasFailure = (brazeResponse, destinationId) => { + /** + * Braze Response for Alias failure + * { + * "aliases_processed": 0, + * "message": "success", + * "errors": [ + * { + * "type": "'external_id' is required", + * "input_array": "user_identifiers", + * "index": 0 + * } + * ] + * } + */ + + /** + * Braze Response for Alias success + * { + * "aliases_processed": 1, + * "message": "success" + * } + */ + + // Should not happen but still checking for unhandled exceptions + if (!isDefinedAndNotNull(brazeResponse)) { + return; + } + const { aliases_processed: aliasesProcessed, errors } = brazeResponse; + if (aliasesProcessed === 0) { + stats.increment('braze_alias_failure_count', { destination_id: destinationId }); + } +}; + +const collectStatsForAliasMissConfigurations = (destinationId) => { + stats.increment('braze_alias_missconfigured_count', { destination_id: destinationId }); +}; + module.exports = { BrazeDedupUtility, CustomAttributeOperationUtil, @@ -667,6 +716,8 @@ module.exports = { getPurchaseObjs, setExternalIdOrAliasObject, setExternalId, - setAliasObjectWithAnonId, + setAliasObject, addMandatoryPurchaseProperties, + collectStatsForAliasFailure, + collectStatsForAliasMissConfigurations, }; diff --git a/src/v0/destinations/campaign_manager/config.js b/src/v0/destinations/campaign_manager/config.js index b3a9531347..5ea1972a84 100644 --- a/src/v0/destinations/campaign_manager/config.js +++ b/src/v0/destinations/campaign_manager/config.js @@ -7,6 +7,10 @@ const ConfigCategories = { type: 'track', name: 'CampaignManagerTrackConfig', }, + ENHANCED_CONVERSION: { + type: 'track', + name: 'CampaignManagerEnhancedConversionConfig', + }, }; const MAX_BATCH_CONVERSATIONS_SIZE = 1000; diff --git a/src/v0/destinations/campaign_manager/data/CampaignManagerEnhancedConversionConfig.json b/src/v0/destinations/campaign_manager/data/CampaignManagerEnhancedConversionConfig.json new file mode 100644 index 0000000000..0bce0019dd --- /dev/null +++ b/src/v0/destinations/campaign_manager/data/CampaignManagerEnhancedConversionConfig.json @@ -0,0 +1,59 @@ +[ + { + "destKey": "hashedEmail", + "sourceKeys": "emailOnly", + "sourceFromGenericMap": true + }, + { + "destKey": "hashedPhoneNumber", + "sourceKeys": "phone", + "sourceFromGenericMap": true + }, + { + "destKey": "addressInfo.hashedFirstName", + "sourceKeys": "firstName", + "sourceFromGenericMap": true + }, + { + "destKey": "addressInfo.hashedLastName", + "sourceKeys": "lastName", + "sourceFromGenericMap": true + }, + { + "destKey": "addressInfo.hashedStreetAddress", + "sourceKeys": "street", + "sourceFromGenericMap": true + }, + { + "destKey": "addressInfo.city", + "sourceKeys": [ + "traits.city", + "traits.address.city", + "context.traits.city", + "context.traits.address.city" + ] + }, + { + "destKey": "addressInfo.state", + "sourceKeys": [ + "traits.state", + "traits.address.state", + "context.traits.state", + "context.traits.address.state" + ] + }, + { + "destKey": "addressInfo.countryCode", + "sourceKeys": [ + "traits.country", + "traits.address.country", + "context.traits.country", + "context.traits.address.country" + ] + }, + { + "destKey": "addressInfo.postalCode", + "sourceKeys": "zipcode", + "sourceFromGenericMap": true + } +] diff --git a/src/v0/destinations/campaign_manager/transform.js b/src/v0/destinations/campaign_manager/transform.js index 14bc6d2c19..7f007ef633 100644 --- a/src/v0/destinations/campaign_manager/transform.js +++ b/src/v0/destinations/campaign_manager/transform.js @@ -12,6 +12,7 @@ const { handleRtTfSingleEventError, getAccessToken, } = require('../../util'); +const { CommonUtils } = require('../../../util/common'); const { ConfigCategories, @@ -22,7 +23,7 @@ const { MAX_BATCH_CONVERSATIONS_SIZE, } = require('./config'); -const { convertToMicroseconds } = require('./util'); +const { convertToMicroseconds, prepareUserIdentifiers } = require('./util'); const { JSON_MIME_TYPE } = require('../../util/constant'); function isEmptyObject(obj) { @@ -105,6 +106,18 @@ function processTrack(message, metadata, destination) { } } + if ( + destination.Config.enableEnhancedConversions && + message.properties.requestType === 'batchupdate' + ) { + const userIdentifiers = CommonUtils.toArray( + prepareUserIdentifiers(message, destination.Config.isHashingRequired ?? true), + ); + if (userIdentifiers.length > 0) { + requestJson.userIdentifiers = userIdentifiers; + } + } + const endpointUrl = prepareUrl(message, destination); return buildResponse( requestJson, diff --git a/src/v0/destinations/campaign_manager/util.js b/src/v0/destinations/campaign_manager/util.js index 434322440f..fee4c8188d 100644 --- a/src/v0/destinations/campaign_manager/util.js +++ b/src/v0/destinations/campaign_manager/util.js @@ -1,3 +1,14 @@ +const { parsePhoneNumber } = require('libphonenumber-js'); +const sha256 = require('sha256'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + constructPayload, + isDefinedAndNotNull, + removeUndefinedAndNullValues, + isEmptyObject, +} = require('../../util'); +const { ConfigCategories, mappingConfig } = require('./config'); + function convertToMicroseconds(input) { const timestamp = Date.parse(input); @@ -28,6 +39,101 @@ function convertToMicroseconds(input) { return timestamp; } +const normalizeEmail = (email) => { + const domains = ['@gmail.com', '@googlemail.com']; + + const matchingDomain = domains.find((domain) => email.endsWith(domain)); + + if (matchingDomain) { + const localPart = email.split('@')[0].replace(/\./g, ''); + return `${localPart}${matchingDomain}`; + } + + return email; +}; + +const normalizePhone = (phone, countryCode) => { + const phoneNumberObject = parsePhoneNumber(phone, countryCode); + if (phoneNumberObject && phoneNumberObject.isValid()) { + return phoneNumberObject.format('E.164'); + } + throw new InstrumentationError('Invalid phone number'); +}; + +// ref:- https://developers.google.com/doubleclick-advertisers/guides/conversions_ec#hashing +const normalizeAndHash = (key, value, options) => { + if (!isDefinedAndNotNull(value)) return value; + + let normalizedValue; + const trimmedValue = value.trim().toLowerCase(); + switch (key) { + case 'hashedEmail': + normalizedValue = normalizeEmail(trimmedValue); + break; + case 'hashedPhoneNumber': + normalizedValue = normalizePhone(trimmedValue, options.countryCode); + break; + case 'hashedFirstName': + case 'hashedLastName': + case 'hashedStreetAddress': + normalizedValue = trimmedValue; + break; + default: + return value; + } + return sha256(normalizedValue); +}; + +const prepareUserIdentifiers = (message, isHashingRequired) => { + const payload = constructPayload( + message, + mappingConfig[ConfigCategories.ENHANCED_CONVERSION.name], + ); + + if (isHashingRequired) { + payload.hashedEmail = normalizeAndHash('hashedEmail', payload.hashedEmail); + payload.hashedPhoneNumber = normalizeAndHash('hashedPhoneNumber', payload.hashedPhoneNumber, { + options: payload.addressInfo?.countryCode, + }); + + if (!isEmptyObject(payload.addressInfo)) { + payload.addressInfo.hashedFirstName = normalizeAndHash( + 'hashedFirstName', + payload.addressInfo.hashedFirstName, + ); + + payload.addressInfo.hashedLastName = normalizeAndHash( + 'hashedLastName', + payload.addressInfo.hashedLastName, + ); + + payload.addressInfo.hashedStreetAddress = normalizeAndHash( + 'hashedStreetAddress', + payload.addressInfo.hashedStreetAddress, + ); + } + } + + const userIdentifiers = []; + if (isDefinedAndNotNull(payload.hashedEmail)) { + userIdentifiers.push({ hashedEmail: payload.hashedEmail }); + } + if (isDefinedAndNotNull(payload.hashedPhoneNumber)) { + userIdentifiers.push({ hashedPhoneNumber: payload.hashedPhoneNumber }); + } + + payload.addressInfo = removeUndefinedAndNullValues(payload.addressInfo); + if (!isEmptyObject(payload.addressInfo)) { + userIdentifiers.push({ addressInfo: payload.addressInfo }); + } + + return userIdentifiers; +}; + module.exports = { convertToMicroseconds, + normalizeEmail, + normalizePhone, + normalizeAndHash, + prepareUserIdentifiers, }; diff --git a/src/v0/destinations/campaign_manager/util.test.js b/src/v0/destinations/campaign_manager/util.test.js index 8f69b57a9f..bda63f5806 100644 --- a/src/v0/destinations/campaign_manager/util.test.js +++ b/src/v0/destinations/campaign_manager/util.test.js @@ -1,4 +1,10 @@ -const { convertToMicroseconds } = require('./util'); +const sha256 = require('sha256'); +const { + convertToMicroseconds, + normalizeEmail, + normalizePhone, + normalizeAndHash, +} = require('./util'); describe('convertToMicroseconds utility test', () => { it('ISO 8601 input', () => { @@ -21,3 +27,31 @@ describe('convertToMicroseconds utility test', () => { expect(convertToMicroseconds('1697013935000')).toEqual(1697013935000000); }); }); + +describe('normalizeEmail', () => { + it('should remove dots from the local part for gmail.com addresses', () => { + const email = 'example.user@gmail.com'; + const normalized = normalizeEmail(email); + expect(normalized).toBe('exampleuser@gmail.com'); + }); + + it('should return the same email if no google domain is present', () => { + const email = 'exampleuser@exampl.com'; + const normalized = normalizeEmail(email); + expect(normalized).toBe('exampleuser@exampl.com'); + }); +}); + +describe('normalizePhone', () => { + it('should return a valid E.164 formatted phone number when provided with correct inputs', () => { + const validPhone = '4155552671'; + const countryCode = 'US'; + expect(normalizePhone(validPhone, countryCode)).toBe('+14155552671'); + }); + + it('should throw an InstrumentationError when the phone number is too short or too long', () => { + const invalidPhone = '123'; + const countryCode = 'US'; + expect(() => normalizePhone(invalidPhone, countryCode)).toThrow('Invalid phone number'); + }); +}); diff --git a/src/v0/destinations/clevertap/data/CleverTapIdentify.json b/src/v0/destinations/clevertap/data/CleverTapIdentify.json index 577e13c339..cdc4b28d93 100644 --- a/src/v0/destinations/clevertap/data/CleverTapIdentify.json +++ b/src/v0/destinations/clevertap/data/CleverTapIdentify.json @@ -1,7 +1,7 @@ [ { "destKey": "Email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/clickhouse/transform.js b/src/v0/destinations/clickhouse/transform.js index 24158cc41f..491475419c 100644 --- a/src/v0/destinations/clickhouse/transform.js +++ b/src/v0/destinations/clickhouse/transform.js @@ -1,53 +1,44 @@ const { processWarehouseMessage } = require('../../../warehouse'); const { getDataType } = require('../../../warehouse/index'); -const clickhouse = 'clickhouse'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'clickhouse'; function getDataTypeOverride(key, val, options) { if (options.chEnableArraySupport === 'false') { return 'string'; } - if (Array.isArray(val)) { - // for now returning it as string. confirm this case - if (val.length === 0) { - return 'string'; - } - // check for different data types in the array. if there are different then return array(string) - const firstValueDataType = getDataType(key, val[0], {}); - let finalDataType = firstValueDataType; - for (let i = 1; i < val.length; i += 1) { - const dataType = getDataType(key, val[i], {}); - if (finalDataType !== dataType) { - if (finalDataType === 'string') { - break; - } - if (dataType === 'float' && finalDataType === 'int') { - finalDataType = 'float'; - // eslint-disable-next-line no-continue - continue; - } - if (dataType === 'int' && finalDataType === 'float') { - // eslint-disable-next-line no-continue - continue; - } - finalDataType = 'string'; + if (!Array.isArray(val) || val.length === 0) { + return 'string'; + } + + // check for different data types in the array. if there are different -> return array(string) + let finalDataType = getDataType(key, val[0], {}); + for (let i = 1; i < val.length; i += 1) { + const dataType = getDataType(key, val[i], {}); + if (finalDataType !== dataType) { + if (finalDataType === 'string') { + break; + } + if (dataType === 'float' && finalDataType === 'int') { + finalDataType = 'float'; + // eslint-disable-next-line no-continue + continue; + } + if (dataType === 'int' && finalDataType === 'float') { + // eslint-disable-next-line no-continue + continue; } + finalDataType = 'string'; } - return `array(${finalDataType})`; } - return 'string'; + return `array(${finalDataType})`; } function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = clickhouse; const chEnableArraySupport = event.request.query.chEnableArraySupport || 'false'; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -55,10 +46,12 @@ function process(event) { provider, chEnableArraySupport, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json b/src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json index 4a7a785e1d..00a8ea0584 100644 --- a/src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json +++ b/src/v0/destinations/custify/data/CUSTIFYIdentifyConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/custify/data/CUSTIFYTrackConfig.json b/src/v0/destinations/custify/data/CUSTIFYTrackConfig.json index f68e9f18ac..1751c84230 100644 --- a/src/v0/destinations/custify/data/CUSTIFYTrackConfig.json +++ b/src/v0/destinations/custify/data/CUSTIFYTrackConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/delighted/util.js b/src/v0/destinations/delighted/util.js index c690bf5f5c..53f416b48d 100644 --- a/src/v0/destinations/delighted/util.js +++ b/src/v0/destinations/delighted/util.js @@ -1,14 +1,10 @@ -const { - NetworkInstrumentationError, - InstrumentationError, - NetworkError, -} = require('@rudderstack/integrations-lib'); -const myAxios = require('../../../util/myAxios'); +const { InstrumentationError, NetworkError } = require('@rudderstack/integrations-lib'); const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); const { getValueFromMessage } = require('../../util'); const { ENDPOINT } = require('./config'); const tags = require('../../util/tags'); const { JSON_MIME_TYPE } = require('../../util/constant'); +const { handleHttpRequest } = require('../../../adapters/network'); const isValidEmail = (email) => { const re = @@ -41,6 +37,30 @@ const isValidUserIdOrError = (channel, userId) => { }; }; +/** + * Returns final status + * @param {*} status + * @returns + */ +const getErrorStatus = (status) => { + let errStatus; + switch (status) { + case 422: + case 401: + case 406: + case 403: + errStatus = 400; + break; + case 500: + case 503: + errStatus = 500; + break; + default: + errStatus = status; + } + return errStatus; +}; + const userValidity = async (channel, Config, userId) => { const paramsdata = {}; if (channel === 'email') { @@ -50,53 +70,38 @@ const userValidity = async (channel, Config, userId) => { } const basicAuth = Buffer.from(Config.apiKey).toString('base64'); - let response; - try { - response = await myAxios.get( - `${ENDPOINT}`, - { - headers: { - Authorization: `Basic ${basicAuth}`, - 'Content-Type': JSON_MIME_TYPE, - }, - params: paramsdata, + const { processedResponse } = await handleHttpRequest( + 'get', + `${ENDPOINT}`, + { + headers: { + Authorization: `Basic ${basicAuth}`, + 'Content-Type': JSON_MIME_TYPE, }, - { - destType: 'delighted', - feature: 'transformation', - requestMethod: 'GET', - endpointPath: '/people.json', - module: 'router', - }, - ); - if (response && response.data && response.status === 200 && Array.isArray(response.data)) { - return response.data.length > 0; - } - throw new NetworkInstrumentationError('Invalid response'); - } catch (error) { - let errMsg = ''; - let errStatus = 400; - if (error.response && error.response.data) { - errMsg = JSON.stringify(error.response.data); - switch (error.response.status) { - case 422: - case 401: - case 406: - case 403: - errStatus = 400; - break; - case 500: - case 503: - errStatus = 500; - break; - default: - errStatus = 400; - } - } - throw new NetworkError(`Error occurred while checking user : ${errMsg}`, errStatus, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(errStatus), - }); + params: paramsdata, + }, + { + destType: 'delighted', + feature: 'transformation', + requestMethod: 'GET', + endpointPath: '/people.json', + module: 'router', + }, + ); + + if (processedResponse.status === 200 && Array.isArray(processedResponse?.response)) { + return processedResponse.response.length > 0; } + + const errStatus = getErrorStatus(processedResponse.status); + throw new NetworkError( + `Error occurred while checking user: ${JSON.stringify(processedResponse?.response || 'Invalid response')}`, + errStatus, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(errStatus), + }, + processedResponse, + ); }; const eventValidity = (Config, message) => { const event = getValueFromMessage(message, 'event'); diff --git a/src/v0/destinations/deltalake/transform.js b/src/v0/destinations/deltalake/transform.js index 49d40131d4..637b64cf36 100644 --- a/src/v0/destinations/deltalake/transform.js +++ b/src/v0/destinations/deltalake/transform.js @@ -1,25 +1,22 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const deltalake = 'deltalake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} - -function getDataTypeOverride() {} +const provider = 'deltalake'; function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = deltalake; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, - getDataTypeOverride, + getDataTypeOverride: () => {}, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } -exports.process = process; +module.exports = { + provider, + process, +}; diff --git a/src/v0/destinations/emarsys/deleteUsers.js b/src/v0/destinations/emarsys/deleteUsers.js new file mode 100644 index 0000000000..c6ca746217 --- /dev/null +++ b/src/v0/destinations/emarsys/deleteUsers.js @@ -0,0 +1,93 @@ +const { + NetworkError, + isDefinedAndNotNull, + ConfigurationAuthError, +} = require('@rudderstack/integrations-lib'); +const { httpPOST } = require('../../../adapters/network'); +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../util'); +const { executeCommonValidations } = require('../../util/regulation-api'); +const tags = require('../../util/tags'); +const { getCustomIdBatches } = require('../../util/deleteUserUtils'); +const { + buildHeader, + deduceCustomIdentifier, + findRudderPropertyByEmersysProperty, +} = require('../../../cdk/v2/destinations/emarsys/utils'); + +/** + * This function will help to delete the users one by one from the userAttributes array. + * @param {*} userAttributes Array of objects with userId, email and phone + * @param {*} config Destination.Config provided in dashboard + * @returns + */ +const userDeletionHandler = async (userAttributes, config) => { + const endpoint = 'https://api.emarsys.net/api/v2/contact/delete'; + const headers = buildHeader(config); + const customIdentifier = deduceCustomIdentifier({}, config.emersysCustomIdentifier); + const configuredPayloadProperty = findRudderPropertyByEmersysProperty( + customIdentifier, + config.fieldMapping, + ); + if (!isDefinedAndNotNull(config.defaultContactList)) { + throw new ConfigurationAuthError('No audience list is configured. Aborting'); + } + /** + * identifierBatches = [[u1,u2,u3,...batchSize],[u1,u2,u3,...batchSize]...] + * Ref doc : https://dev.emarsys.com/docs/core-api-reference/szmq945esac90-delete-contacts + */ + const identifierBatches = getCustomIdBatches(userAttributes, configuredPayloadProperty, 1000); + // Note: we will only get 400 status code when no user deletion is present for given userIds so we will not throw error in that case + // eslint-disable-next-line no-restricted-syntax + for (const curBatch of identifierBatches) { + const deleteContactPayload = { + key_id: customIdentifier, + contact_list_id: config.defaultContactList, + }; + deleteContactPayload[`${customIdentifier}`] = curBatch; + // eslint-disable-next-line no-await-in-loop + const deletionResponse = await httpPOST( + endpoint, + { + ...deleteContactPayload, + }, + { + headers, + }, + { + destType: 'emarsys', + feature: 'deleteUsers', + endpointPath: '/contact/delete', + requestMethod: 'POST', + module: 'deletion', + }, + ); + const handledDelResponse = processAxiosResponse(deletionResponse); + if (!isHttpStatusSuccess(handledDelResponse.status) && handledDelResponse.status !== 400) { + throw new NetworkError( + 'User deletion request failed', + handledDelResponse.status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(handledDelResponse.status), + [tags.TAG_NAMES.STATUS]: handledDelResponse.status, + }, + handledDelResponse, + ); + } + } + + return { + statusCode: 200, + status: 'successful', + }; +}; +const processDeleteUsers = async (event) => { + const { userAttributes, config } = event; + executeCommonValidations(userAttributes); + const resp = await userDeletionHandler(userAttributes, config); + return resp; +}; +module.exports = { processDeleteUsers }; diff --git a/src/v0/destinations/facebook_conversions/transform.js b/src/v0/destinations/facebook_conversions/transform.js index 1bb97b2672..e4aee9c503 100644 --- a/src/v0/destinations/facebook_conversions/transform.js +++ b/src/v0/destinations/facebook_conversions/transform.js @@ -1,6 +1,5 @@ /* eslint-disable no-param-reassign */ const get = require('get-value'); -const moment = require('moment'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const { CONFIG_CATEGORIES, @@ -33,6 +32,7 @@ const { fetchUserData, formingFinalResponse, } = require('../../util/facebookUtils'); +const { verifyEventDuration } = require('../facebook_pixel/utils'); const responseBuilderSimple = (message, category, destination) => { const { Config, ID } = destination; @@ -121,19 +121,7 @@ const processEvent = (message, destination) => { } const timeStamp = getFieldValueFromMessage(message, 'timestamp'); - if (timeStamp) { - const start = moment.unix(moment(timeStamp).format('X')); - const current = moment.unix(moment().format('X')); - // calculates past event in days - const deltaDay = Math.ceil(moment.duration(current.diff(start)).asDays()); - // calculates future event in minutes - const deltaMin = Math.ceil(moment.duration(start.diff(current)).asMinutes()); - if (deltaDay > 7 || deltaMin > 1) { - throw new InstrumentationError( - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - ); - } - } + verifyEventDuration(message, destination, timeStamp); const { datasetId, accessToken } = destination.Config; if (!datasetId) { diff --git a/src/v0/destinations/facebook_conversions/utils.js b/src/v0/destinations/facebook_conversions/utils.js index c6e3993e33..87fb0ea606 100644 --- a/src/v0/destinations/facebook_conversions/utils.js +++ b/src/v0/destinations/facebook_conversions/utils.js @@ -134,7 +134,7 @@ const populateCustomDataBasedOnCategory = (customData, message, category, catego const { contentIds, contents } = populateContentsAndContentIDs([message.properties]); eventTypeCustomData = { ...eventTypeCustomData, - content_ids: contentIds, + content_ids: contentIds.length === 1 ? contentIds[0] : contentIds, contents, content_type: contentType, content_category: getContentCategory(contentCategory), diff --git a/src/v0/destinations/facebook_offline_conversions/data/FbOfflineConversionsTrackConfig.json b/src/v0/destinations/facebook_offline_conversions/data/FbOfflineConversionsTrackConfig.json index fc5f6c48fa..3f2fa1875f 100644 --- a/src/v0/destinations/facebook_offline_conversions/data/FbOfflineConversionsTrackConfig.json +++ b/src/v0/destinations/facebook_offline_conversions/data/FbOfflineConversionsTrackConfig.json @@ -39,7 +39,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/facebook_offline_conversions/utils.js b/src/v0/destinations/facebook_offline_conversions/utils.js index c48de4e0b9..460ef71176 100644 --- a/src/v0/destinations/facebook_offline_conversions/utils.js +++ b/src/v0/destinations/facebook_offline_conversions/utils.js @@ -396,7 +396,7 @@ const preparePayload = (facebookOfflineConversionsPayload, destination) => { const keys = Object.keys(facebookOfflineConversionsPayload); keys.forEach((key) => { if (isHashRequired && HASHING_REQUIRED_KEYS.includes(key)) { - payload[key] = sha256(facebookOfflineConversionsPayload[key]); + payload[key] = sha256(facebookOfflineConversionsPayload[key].trim()); } else { payload[key] = facebookOfflineConversionsPayload[key]; } @@ -407,8 +407,8 @@ const preparePayload = (facebookOfflineConversionsPayload, destination) => { ? facebookOfflineConversionsPayload.name.split(' ') : null; if (split !== null && Array.isArray(split) && split.length === 2) { - payload.fn = isHashRequired ? sha256(split[0]) : split[0]; - payload.ln = isHashRequired ? sha256(split[1]) : split[1]; + payload.fn = isHashRequired ? sha256(split[0].trim()) : split[0]; + payload.ln = isHashRequired ? sha256(split[1].trim()) : split[1]; } delete payload.name; } diff --git a/src/v0/destinations/facebook_pixel/data/FBPIXELCommonConfig.json b/src/v0/destinations/facebook_pixel/data/FBPIXELCommonConfig.json index 11a81a20ab..1ba6c4c82f 100644 --- a/src/v0/destinations/facebook_pixel/data/FBPIXELCommonConfig.json +++ b/src/v0/destinations/facebook_pixel/data/FBPIXELCommonConfig.json @@ -33,5 +33,10 @@ "context.traits.action_source", "properties.action_source" ] + }, + { + "destKey": "advertiser_tracking_enabled", + "sourceKeys": "context.device.adTrackingEnabled", + "required": false } ] diff --git a/src/v0/destinations/facebook_pixel/transform.js b/src/v0/destinations/facebook_pixel/transform.js index 8a63998b45..d44a38aa69 100644 --- a/src/v0/destinations/facebook_pixel/transform.js +++ b/src/v0/destinations/facebook_pixel/transform.js @@ -1,8 +1,6 @@ /* eslint-disable no-param-reassign */ const get = require('get-value'); -const moment = require('moment'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); -const stats = require('../../../util/stats'); const { VERSION, CONFIG_CATEGORIES, @@ -31,6 +29,7 @@ const { handleOrder, populateCustomDataBasedOnCategory, getCategoryFromEvent, + verifyEventDuration, } = require('./utils'); const { @@ -170,23 +169,7 @@ const processEvent = (message, destination) => { } const timeStamp = message.timestamp || message.originalTimestamp; - if (timeStamp) { - const start = moment.unix(moment(timeStamp).format('X')); - const current = moment.unix(moment().format('X')); - // calculates past event in days - const deltaDay = Math.ceil(moment.duration(current.diff(start)).asDays()); - // calculates future event in minutes - const deltaMin = Math.ceil(moment.duration(start.diff(current)).asMinutes()); - if (deltaDay > 7 || deltaMin > 1) { - // TODO: Remove after testing in mirror transformer - stats.increment('fb_pixel_timestamp_error', { - destinationId: destination.ID, - }); - throw new InstrumentationError( - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - ); - } - } + verifyEventDuration(message, destination, timeStamp); let eventsToEvents; if (Array.isArray(destination.Config.eventsToEvents)) { diff --git a/src/v0/destinations/facebook_pixel/transform.test.js b/src/v0/destinations/facebook_pixel/transform.test.js index 25332d770c..d3c5baa070 100644 --- a/src/v0/destinations/facebook_pixel/transform.test.js +++ b/src/v0/destinations/facebook_pixel/transform.test.js @@ -24,6 +24,23 @@ const getTestMessage = () => { return message; }; +const getTestMessageWithoutProductIdAndCategory = () => { + let message = { + properties: { + currency: 'CAD', + quantity: 1, + price: 24.75, + value: 30, + name: 'my product 1', + testDimension: true, + testMetric: true, + position: 4.5, + query: 'HDMI Cable', + }, + }; + return message; +}; + const getTestCategoryToContent = () => { let categoryToContent = [ { @@ -52,6 +69,17 @@ describe('Unit test cases for facebook_pixel handle search', () => { expect(handleSearch(getTestMessage())).toEqual(expectedOutput); }); + it('should return content with content_ids and content fields as empty array', async () => { + const expectedOutput = { + content_ids: [], + content_category: '', + value: 30, + search_string: 'HDMI Cable', + contents: [], + }; + expect(handleSearch(getTestMessageWithoutProductIdAndCategory())).toEqual(expectedOutput); + }); + it("mapping 'product_id' with contentId", async () => { let message = getTestMessage(); message.properties.product_id = 'prd-123'; diff --git a/src/v0/destinations/facebook_pixel/utils.js b/src/v0/destinations/facebook_pixel/utils.js index cfa625ee3d..74d5240f2a 100644 --- a/src/v0/destinations/facebook_pixel/utils.js +++ b/src/v0/destinations/facebook_pixel/utils.js @@ -1,4 +1,6 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const get = require('get-value'); +const moment = require('moment'); const { isObject } = require('../../util'); const { ACTION_SOURCES_VALUES, @@ -339,6 +341,29 @@ const getCategoryFromEvent = (eventName) => { return category; }; +const verifyEventDuration = (message, destination, timeStamp) => { + const actionSource = + get(message, 'traits.action_source') || + get(message, 'context.traits.action_source') || + get(message, 'properties.action_source'); + + const start = moment.unix(moment(timeStamp).format('X')); + const current = moment.unix(moment().format('X')); + // calculates past event in days + const deltaDay = Math.ceil(moment.duration(current.diff(start)).asDays()); + // calculates future event in minutes + const deltaMin = Math.ceil(moment.duration(start.diff(current)).asMinutes()); + let defaultSupportedDelta = 7; + if (actionSource === 'physical_store') { + defaultSupportedDelta = 62; + } + if (deltaDay > defaultSupportedDelta || deltaMin > 1) { + throw new InstrumentationError( + `Events must be sent within ${defaultSupportedDelta} days of their occurrence or up to one minute in the future.`, + ); + } +}; + module.exports = { formatRevenue, getActionSource, @@ -348,4 +373,5 @@ module.exports = { handleOrder, populateCustomDataBasedOnCategory, getCategoryFromEvent, + verifyEventDuration, }; diff --git a/src/v0/destinations/facebook_pixel/utils.test.js b/src/v0/destinations/facebook_pixel/utils.test.js new file mode 100644 index 0000000000..fa17aebd76 --- /dev/null +++ b/src/v0/destinations/facebook_pixel/utils.test.js @@ -0,0 +1,231 @@ +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + getActionSource, + formatRevenue, + getCategoryFromEvent, + verifyEventDuration, +} = require('./utils'); +const { CONFIG_CATEGORIES, OTHER_STANDARD_EVENTS } = require('./config'); + +Date.now = jest.fn(() => new Date('2022-01-20T00:00:00Z')); +describe('Test Facebook Pixel Utils', () => { + describe('getActionSource', () => { + // Returns 'other' if payload.action_source is not defined and channel is neither 'web' nor 'mobile' + it('should return "other" when payload.action_source is not defined and channel is neither "web" nor "mobile"', () => { + const payload = {}; + const channel = 'email'; + const result = getActionSource(payload, channel); + expect(result).toBe('other'); + }); + + // Returns payload.action_source if it is defined and is a valid value from ACTION_SOURCES_VALUES + it('should return payload.action_source when it is defined and is a valid value from ACTION_SOURCES_VALUES', () => { + const payload = { action_source: 'website' }; + const channel = 'email'; + const result = getActionSource(payload, channel); + expect(result).toBe('website'); + }); + + // Returns 'website' if channel is 'web' and payload.action_source is not defined + it('should return "website" when channel is "web" and payload.action_source is not defined', () => { + const payload = {}; + const channel = 'web'; + const result = getActionSource(payload, channel); + expect(result).toBe('website'); + }); + + // Throws an InstrumentationError if payload.action_source is defined but not a valid value from ACTION_SOURCES_VALUES + it('should throw an InstrumentationError when payload.action_source is defined but not a valid value from ACTION_SOURCES_VALUES', () => { + const payload = { action_source: 'invalid' }; + const channel = 'email'; + expect(() => { + getActionSource(payload, channel); + }).toThrow(InstrumentationError); + }); + }); + + describe('formatRevenue', () => { + // Returns a number with two decimal places when passed a valid revenue value. + it('should return a number with two decimal places when passed a valid revenue value', () => { + const revenue = '100.50'; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.5); + }); + + // Returns 0 when passed a null revenue value. + it('should return 0 when passed a null revenue value', () => { + const revenue = null; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(0); + }); + + // Returns 0 when passed an undefined revenue value. + it('should return 0 when passed an undefined revenue value', () => { + const revenue = undefined; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(0); + }); + + // Throws an InstrumentationError when passed a non-numeric string revenue value. + it('should throw an InstrumentationError when passed a non-numeric string revenue value', () => { + const revenue = 'abc'; + expect(() => { + formatRevenue(revenue); + }).toThrow(InstrumentationError); + }); + + // Returns a number with two decimal places when passed a numeric string revenue value with more than two decimal places. + it('should return a number with two decimal places when passed a numeric string revenue value with more than two decimal places', () => { + const revenue = '100.555'; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.56); + }); + + // Returns a number with two decimal places when passed a numeric value with more than two decimal places. + it('should return a number with two decimal places when passed a numeric value with more than two decimal places', () => { + const revenue = 100.555; + const formattedRevenue = formatRevenue(revenue); + expect(formattedRevenue).toBe(100.56); + }); + }); + + describe('getCategoryFromEvent', () => { + // The function correctly maps the eventName to its corresponding category. + it('should correctly map the eventName to its corresponding category', () => { + const eventName = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.type; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + }); + + // The function returns the correct category for a given eventName. + it('should return the correct category for a given eventName', () => { + const eventName = CONFIG_CATEGORIES.PRODUCT_VIEWED.type; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.PRODUCT_VIEWED); + }); + + // The function returns the default category if the eventName is not recognized. + it('should return the default category if the eventName is not recognized', () => { + const eventName = 'unknownEvent'; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles null or undefined eventName inputs. + it('should handle null or undefined eventName inputs', () => { + const eventName = null; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles empty string eventName inputs. + it('should handle empty string eventName inputs', () => { + const eventName = ''; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles eventName inputs that are not strings. + it('should handle eventName inputs that are not strings', () => { + const eventName = 123; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + + // The function handles multiple eventNames that map to the same category. + it('should correctly map multiple eventNames to the same category', () => { + const eventName1 = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.type; + const eventName2 = CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED.eventName; + const result1 = getCategoryFromEvent(eventName1); + const result2 = getCategoryFromEvent(eventName2); + expect(result1).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + expect(result2).toEqual(CONFIG_CATEGORIES.PRODUCT_LIST_VIEWED); + }); + + // The function handles eventNames that are included in the OTHER_STANDARD_EVENTS list. + it('should correctly handle eventNames included in the OTHER_STANDARD_EVENTS list', () => { + const eventName = OTHER_STANDARD_EVENTS[0]; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.OTHER_STANDARD); + expect(result.eventName).toEqual(eventName); + }); + + // The function handles eventNames that are not recognized and not in the OTHER_STANDARD_EVENTS list. + it('should correctly handle unrecognized eventNames', () => { + const eventName = 'unrecognizedEvent'; + const result = getCategoryFromEvent(eventName); + expect(result).toEqual(CONFIG_CATEGORIES.SIMPLE_TRACK); + }); + }); + + describe('verifyEventDuration', () => { + it('should not throw an InstrumentationError when event duration is less than 8 days after the event occurred', () => { + const message = { + traits: { + action_source: 'some_action_source', + }, + context: { + traits: { + action_source: 'some_action_source', + }, + }, + properties: { + action_source: 'some_action_source', + }, + }; + const destination = { + ID: 'some_destination_id', + }; + const timeStamp = '2022-01-20T00:00:00Z'; + expect(() => { + verifyEventDuration(message, destination, timeStamp); + }).not.toThrow(InstrumentationError); + }); + it('should throw an InstrumentationError when event duration is exactly 8 days after the event occurred', () => { + const message = { + traits: { + action_source: 'some_action_source', + }, + context: { + traits: { + action_source: 'some_action_source', + }, + }, + properties: { + action_source: 'some_action_source', + }, + }; + const destination = { + ID: 'some_destination_id', + }; + const timeStamp = '2022-01-12T00:00:00Z'; + + expect(() => { + verifyEventDuration(message, destination, timeStamp); + }).toThrow(InstrumentationError); + }); + it('should not throw an InstrumentationError when event duration is greater than 8 days after the event occurred and action_source is physical_store', () => { + const message = { + traits: { + action_source: 'physical_store', + }, + context: { + traits: { + action_source: 'some_action_source', + }, + }, + properties: { + action_source: 'some_action_source', + }, + }; + const destination = { + ID: 'some_destination_id', + }; + const timeStamp = '2022-01-12T00:00:00Z'; + + expect(() => { + verifyEventDuration(message, destination, timeStamp); + }).not.toThrow(InstrumentationError); + }); + }); +}); diff --git a/src/v0/destinations/fb/transform.js b/src/v0/destinations/fb/transform.js index e6f8e986cf..1160cef407 100644 --- a/src/v0/destinations/fb/transform.js +++ b/src/v0/destinations/fb/transform.js @@ -90,7 +90,7 @@ function sanityCheckPayloadForTypesAndModifications(updatedEvent) { clonedUpdatedEvent[prop] !== '' ) { isUDSet = true; - clonedUpdatedEvent[prop] = sha256(clonedUpdatedEvent[prop].toLowerCase()); + clonedUpdatedEvent[prop] = sha256(clonedUpdatedEvent[prop].trim().toLowerCase()); } break; case 'ud[zp]': @@ -113,7 +113,7 @@ function sanityCheckPayloadForTypesAndModifications(updatedEvent) { } else { isUDSet = true; clonedUpdatedEvent[prop] = sha256( - clonedUpdatedEvent[prop].toLowerCase() === 'female' ? 'f' : 'm', + clonedUpdatedEvent[prop].trim().toLowerCase() === 'female' ? 'f' : 'm', ); } } @@ -128,7 +128,7 @@ function sanityCheckPayloadForTypesAndModifications(updatedEvent) { if (clonedUpdatedEvent[prop] && clonedUpdatedEvent[prop] !== '') { isUDSet = true; clonedUpdatedEvent[prop] = sha256( - clonedUpdatedEvent[prop].toLowerCase().replace(/ /g, ''), + clonedUpdatedEvent[prop].trim().toLowerCase().replace(/ /g, ''), ); } break; diff --git a/src/v0/destinations/fb_custom_audience/recordTransform.js b/src/v0/destinations/fb_custom_audience/recordTransform.js new file mode 100644 index 0000000000..62d4bd568b --- /dev/null +++ b/src/v0/destinations/fb_custom_audience/recordTransform.js @@ -0,0 +1,240 @@ +/* eslint-disable no-const-assign */ +const lodash = require('lodash'); +const get = require('get-value'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { schemaFields } = require('./config'); +const { MappedToDestinationKey } = require('../../../constants'); +const stats = require('../../../util/stats'); +const { + getDestinationExternalIDInfoForRetl, + isDefinedAndNotNullAndNotEmpty, + checkSubsetOfArray, + returnArrayOfSubarrays, + getSuccessRespEvents, +} = require('../../util'); +const { getErrorResponse, createFinalResponse } = require('../../util/recordUtils'); +const { + ensureApplicableFormat, + getUpdatedDataElement, + getSchemaForEventMappedToDest, + batchingWithPayloadSize, + responseBuilderSimple, + getDataSource, +} = require('./util'); + +const processRecordEventArray = ( + recordChunksArray, + userSchema, + isHashRequired, + disableFormat, + paramsPayload, + prepareParams, + destination, + operation, + operationAudienceId, +) => { + const toSendEvents = []; + const metadata = []; + recordChunksArray.forEach((recordArray) => { + const data = []; + recordArray.forEach((input) => { + const { fields } = input.message; + let dataElement = []; + let nullUserData = true; + + userSchema.forEach((eachProperty) => { + const userProperty = fields[eachProperty]; + let updatedProperty = userProperty; + + if (isHashRequired && !disableFormat) { + updatedProperty = ensureApplicableFormat(eachProperty, userProperty); + } + + dataElement = getUpdatedDataElement( + dataElement, + isHashRequired, + eachProperty, + updatedProperty, + ); + + if (dataElement[dataElement.length - 1]) { + nullUserData = false; + } + }); + + if (nullUserData) { + stats.increment('fb_custom_audience_event_having_all_null_field_values_for_a_user', { + destinationId: destination.ID, + nullFields: userSchema, + }); + } + data.push(dataElement); + metadata.push(input.metadata); + }); + + const prepareFinalPayload = lodash.cloneDeep(paramsPayload); + prepareFinalPayload.schema = userSchema; + prepareFinalPayload.data = data; + const payloadBatches = batchingWithPayloadSize(prepareFinalPayload); + + payloadBatches.forEach((payloadBatch) => { + const response = { + ...prepareParams, + payload: payloadBatch, + }; + + const wrappedResponse = { + responseField: response, + operationCategory: operation, + }; + + const builtResponse = responseBuilderSimple(wrappedResponse, operationAudienceId); + + toSendEvents.push(builtResponse); + }); + }); + + const response = getSuccessRespEvents(toSendEvents, metadata, destination, true); + + return response; +}; + +async function processRecordInputs(groupedRecordInputs) { + const { destination } = groupedRecordInputs[0]; + const { message } = groupedRecordInputs[0]; + const { + isHashRequired, + accessToken, + disableFormat, + type, + subType, + isRaw, + maxUserCount, + audienceId, + } = destination.Config; + const prepareParams = { + access_token: accessToken, + }; + + // maxUserCount validation + const maxUserCountNumber = parseInt(maxUserCount, 10); + if (Number.isNaN(maxUserCountNumber)) { + throw new ConfigurationError('Batch size must be an Integer.'); + } + + // audience id validation + let operationAudienceId = audienceId; + const mappedToDestination = get(message, MappedToDestinationKey); + if (mappedToDestination) { + const { objectType } = getDestinationExternalIDInfoForRetl(message, 'FB_CUSTOM_AUDIENCE'); + operationAudienceId = objectType; + } + if (!isDefinedAndNotNullAndNotEmpty(operationAudienceId)) { + throw new ConfigurationError('Audience ID is a mandatory field'); + } + + // user schema validation + let { userSchema } = destination.Config; + if (mappedToDestination) { + userSchema = getSchemaForEventMappedToDest(message); + } + if (!Array.isArray(userSchema)) { + userSchema = [userSchema]; + } + if (!checkSubsetOfArray(schemaFields, userSchema)) { + throw new ConfigurationError('One or more of the schema fields are not supported'); + } + + const paramsPayload = {}; + + if (isRaw) { + paramsPayload.is_raw = isRaw; + } + + const dataSource = getDataSource(type, subType); + if (Object.keys(dataSource).length > 0) { + paramsPayload.data_source = dataSource; + } + + const groupedRecordsByAction = lodash.groupBy(groupedRecordInputs, (record) => + record.message.action?.toLowerCase(), + ); + + let insertResponse; + let deleteResponse; + let updateResponse; + + if (groupedRecordsByAction.delete) { + const deleteRecordChunksArray = returnArrayOfSubarrays( + groupedRecordsByAction.delete, + maxUserCountNumber, + ); + deleteResponse = processRecordEventArray( + deleteRecordChunksArray, + userSchema, + isHashRequired, + disableFormat, + paramsPayload, + prepareParams, + destination, + 'remove', + operationAudienceId, + ); + } + + if (groupedRecordsByAction.insert) { + const insertRecordChunksArray = returnArrayOfSubarrays( + groupedRecordsByAction.insert, + maxUserCountNumber, + ); + + insertResponse = processRecordEventArray( + insertRecordChunksArray, + userSchema, + isHashRequired, + disableFormat, + paramsPayload, + prepareParams, + destination, + 'add', + operationAudienceId, + ); + } + + if (groupedRecordsByAction.update) { + const updateRecordChunksArray = returnArrayOfSubarrays( + groupedRecordsByAction.update, + maxUserCountNumber, + ); + updateResponse = processRecordEventArray( + updateRecordChunksArray, + userSchema, + isHashRequired, + disableFormat, + paramsPayload, + prepareParams, + destination, + 'add', + operationAudienceId, + ); + } + + const errorResponse = getErrorResponse(groupedRecordsByAction); + + const finalResponse = createFinalResponse( + deleteResponse, + insertResponse, + updateResponse, + errorResponse, + ); + if (finalResponse.length === 0) { + throw new InstrumentationError( + 'Missing valid parameters, unable to generate transformed payload', + ); + } + return finalResponse; +} + +module.exports = { + processRecordInputs, +}; diff --git a/src/v0/destinations/fb_custom_audience/transform.js b/src/v0/destinations/fb_custom_audience/transform.js index 9320a3476b..c5c340c043 100644 --- a/src/v0/destinations/fb_custom_audience/transform.js +++ b/src/v0/destinations/fb_custom_audience/transform.js @@ -1,14 +1,7 @@ const lodash = require('lodash'); const get = require('get-value'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const { - InstrumentationError, - TransformationError, - ConfigurationError, -} = require('@rudderstack/integrations-lib'); -const { - defaultRequestConfig, - defaultPostRequestConfig, - defaultDeleteRequestConfig, checkSubsetOfArray, isDefinedAndNotNullAndNotEmpty, returnArrayOfSubarrays, @@ -20,40 +13,28 @@ const { prepareDataField, getSchemaForEventMappedToDest, batchingWithPayloadSize, + generateAppSecretProof, + responseBuilderSimple, + getDataSource, } = require('./util'); -const { - getEndPoint, - schemaFields, - USER_ADD, - USER_DELETE, - typeFields, - subTypeFields, -} = require('./config'); +const { schemaFields, USER_ADD, USER_DELETE } = require('./config'); const { MappedToDestinationKey } = require('../../../constants'); +const { processRecordInputs } = require('./recordTransform'); +const logger = require('../../../logger'); -const responseBuilderSimple = (payload, audienceId) => { - if (payload) { - const responseParams = payload.responseField; - const response = defaultRequestConfig(); - response.endpoint = getEndPoint(audienceId); - - if (payload.operationCategory === 'add') { - response.method = defaultPostRequestConfig.requestMethod; +function checkForUnsupportedEventTypes(dictionary, keyList) { + const unsupportedEventTypes = []; + // eslint-disable-next-line no-restricted-syntax + for (const key in dictionary) { + if (!keyList.includes(key)) { + unsupportedEventTypes.push(key); } - if (payload.operationCategory === 'remove') { - response.method = defaultDeleteRequestConfig.requestMethod; - } - - response.params = responseParams; - return response; } - // fail-safety for developer error - throw new TransformationError(`Payload could not be constructed`); -}; + return unsupportedEventTypes; +} // Function responsible prepare the payload field of every event parameter - const preparePayload = ( userUpdateList, userSchema, @@ -88,7 +69,7 @@ const prepareResponse = ( userSchema, isHashRequired = true, ) => { - const { accessToken, disableFormat, type, subType, isRaw } = destination.Config; + const { accessToken, disableFormat, type, subType, isRaw, appSecret } = destination.Config; const mappedToDestination = get(message, MappedToDestinationKey); @@ -101,23 +82,22 @@ const prepareResponse = ( const prepareParams = {}; // creating the parameters field const paramsPayload = {}; - const dataSource = {}; prepareParams.access_token = accessToken; + if (isDefinedAndNotNullAndNotEmpty(appSecret)) { + const dateNow = Date.now(); + prepareParams.appsecret_time = Math.floor(dateNow / 1000); // Get current Unix time in seconds + prepareParams.appsecret_proof = generateAppSecretProof(accessToken, appSecret, dateNow); + } + // creating the payload field for parameters if (isRaw) { paramsPayload.is_raw = isRaw; } // creating the data_source block - if (type && type !== 'NA' && typeFields.includes(type)) { - dataSource.type = type; - } - - if (subType && subType !== 'NA' && subTypeFields.includes(subType)) { - dataSource.sub_type = subType; - } + const dataSource = getDataSource(type, subType); if (Object.keys(dataSource).length > 0) { paramsPayload.data_source = dataSource; } @@ -243,6 +223,7 @@ const processEvent = (message, destination) => { ), ); } + toSendEvents.forEach((sendEvent) => { respList.push(responseBuilderSimple(sendEvent, operationAudienceId)); }); @@ -258,7 +239,31 @@ const processEvent = (message, destination) => { const process = (event) => processEvent(event.message, event.destination); const processRouterDest = async (inputs, reqMetadata) => { - const respList = await simpleProcessRouterDest(inputs, process, reqMetadata); + const respList = []; + const groupedInputs = lodash.groupBy(inputs, (input) => input.message.type?.toLowerCase()); + let transformedRecordEvent = []; + let transformedAudienceEvent = []; + + const eventTypes = ['record', 'audiencelist']; + const unsupportedEventList = checkForUnsupportedEventTypes(groupedInputs, eventTypes); + if (unsupportedEventList.length > 0) { + logger.info(`unsupported events found ${unsupportedEventList}`); + throw new ConfigurationError('unsupported events present in the event'); + } + + if (groupedInputs.record) { + transformedRecordEvent = await processRecordInputs(groupedInputs.record, reqMetadata); + } + + if (groupedInputs.audiencelist) { + transformedAudienceEvent = await simpleProcessRouterDest( + groupedInputs.audiencelist, + process, + reqMetadata, + ); + } + + respList.push(...transformedRecordEvent, ...transformedAudienceEvent); return flattenMap(respList); }; diff --git a/src/v0/destinations/fb_custom_audience/util.js b/src/v0/destinations/fb_custom_audience/util.js index 6c53ed2814..401b601869 100644 --- a/src/v0/destinations/fb_custom_audience/util.js +++ b/src/v0/destinations/fb_custom_audience/util.js @@ -1,8 +1,16 @@ const lodash = require('lodash'); const sha256 = require('sha256'); +const crypto = require('crypto'); const get = require('get-value'); const jsonSize = require('json-size'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { TransformationError } = require('@rudderstack/integrations-lib'); +const { typeFields, subTypeFields, getEndPoint } = require('./config'); +const { + defaultRequestConfig, + defaultPostRequestConfig, + defaultDeleteRequestConfig, +} = require('../../util'); const stats = require('../../../util/stats'); const { isDefinedAndNotNull } = require('../../util'); @@ -92,11 +100,11 @@ const ensureApplicableFormat = (userProperty, userInformation) => { case 'FN': case 'FI': if (userProperty !== 'FI') { - updatedProperty = stringifiedUserInformation.toLowerCase().replace(/[!#$%&@A-Za-z]/g, ''); + updatedProperty = stringifiedUserInformation.toLowerCase().replace(/[^#$%&'*+/a-z]/g, ''); } else { updatedProperty = stringifiedUserInformation .toLowerCase() - .replace(/[^!#$%&,.?@A-Za-z]/g, ''); + .replace(/[^!"#$%&'()*+,-./a-z]/g, ''); } break; case 'MADID': @@ -206,4 +214,57 @@ const prepareDataField = ( return data; }; -module.exports = { prepareDataField, getSchemaForEventMappedToDest, batchingWithPayloadSize }; +// ref: https://developers.facebook.com/docs/facebook-login/security/#generate-the-proof +const generateAppSecretProof = (accessToken, appSecret, dateNow) => { + const currentTime = Math.floor(dateNow / 1000); // Get current Unix time in seconds + const data = `${accessToken}|${currentTime}`; + + // Creating a HMAC SHA-256 hash with the app_secret as the key + const hmac = crypto.createHmac('sha256', appSecret); + hmac.update(data); + const appsecretProof = hmac.digest('hex'); + + return appsecretProof; +}; + +const getDataSource = (type, subType) => { + const dataSource = {}; + if (type && type !== 'NA' && typeFields.includes(type)) { + dataSource.type = type; + } + if (subType && subType !== 'NA' && subTypeFields.includes(subType)) { + dataSource.sub_type = subType; + } + return dataSource; +}; + +const responseBuilderSimple = (payload, audienceId) => { + if (payload) { + const responseParams = payload.responseField; + const response = defaultRequestConfig(); + response.endpoint = getEndPoint(audienceId); + + if (payload.operationCategory === 'add') { + response.method = defaultPostRequestConfig.requestMethod; + } + if (payload.operationCategory === 'remove') { + response.method = defaultDeleteRequestConfig.requestMethod; + } + + response.params = responseParams; + return response; + } + // fail-safety for developer error + throw new TransformationError(`Payload could not be constructed`); +}; + +module.exports = { + prepareDataField, + getSchemaForEventMappedToDest, + batchingWithPayloadSize, + ensureApplicableFormat, + getUpdatedDataElement, + generateAppSecretProof, + responseBuilderSimple, + getDataSource, +}; diff --git a/src/v0/destinations/fb_custom_audience/util.test.js b/src/v0/destinations/fb_custom_audience/util.test.js new file mode 100644 index 0000000000..60e0aff464 --- /dev/null +++ b/src/v0/destinations/fb_custom_audience/util.test.js @@ -0,0 +1,122 @@ +const { getDataSource, responseBuilderSimple, getUpdatedDataElement } = require('./util'); + +const basePayload = { + responseField: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + ], + }, + }, +}; + +const baseResponse = { + version: '1', + type: 'REST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, +}; + +describe('FB_custom_audience utils test', () => { + describe('getDataSource function tests', () => { + it('Should return empty datasource if type and subType are both NA', () => { + const expectedDataSource = {}; + const dataSource = getDataSource('NA', 'NA'); + expect(dataSource).toEqual(expectedDataSource); + }); + it('Should set subType and type if value present in destination config macthes with preset list', () => { + const expectedDataSource = { + type: 'EVENT_BASED', + }; + const dataSource = getDataSource('EVENT_BASED', 'something'); + expect(dataSource).toEqual(expectedDataSource); + }); + }); + + describe('responseBuilderSimple function tests', () => { + it('Should return correct response for add payload', () => { + const payload = basePayload; + payload.operationCategory = 'add'; + const expectedResponse = baseResponse; + expectedResponse.method = 'POST'; + const response = responseBuilderSimple(payload, '23848494844100489'); + expect(response).toEqual(expectedResponse); + }); + + it('Should return correct response for delete payload', () => { + const payload = basePayload; + payload.operationCategory = 'remove'; + const expectedResponse = baseResponse; + expectedResponse.method = 'DELETE'; + const response = responseBuilderSimple(payload, '23848494844100489'); + expect(response).toEqual(expectedResponse); + }); + + it('Should throw error if payload is empty', () => { + try { + const response = responseBuilderSimple(payload, ''); + expect(response).toEqual(); + } catch (error) { + expect(error.message).toEqual(`payload is not defined`); + } + }); + }); + + describe('getUpdatedDataElement function tests', () => { + it('Should hash field if isHashRequired is set to true', () => { + const expectedDataElement = [ + '59107c750fd5ee2758d1988f2bf12d9f110439221ebdb7997e70d6a2c1c5afda', + ]; + let dataElement = []; + dataElement = getUpdatedDataElement(dataElement, true, 'FN', 'some-name'); + expect(dataElement).toEqual(expectedDataElement); + }); + + it('Should not hash field if isHashRequired is set to false', () => { + const expectedDataElement = ['some-name']; + let dataElement = []; + dataElement = getUpdatedDataElement(dataElement, false, 'FN', 'some-name'); + expect(dataElement).toEqual(expectedDataElement); + }); + + it('Should not hash MADID or EXTERN_ID and just pass value', () => { + const expectedDataElement = ['some-id', 'some-ext-id']; + let dataElement = []; + dataElement = getUpdatedDataElement(dataElement, true, 'MADID', 'some-id'); + dataElement = getUpdatedDataElement(dataElement, true, 'EXTERN_ID', 'some-ext-id'); + expect(dataElement).toEqual(expectedDataElement); + }); + + it('Should not hash MADID or EXTERN_ID and just pass empty value if value does not exist', () => { + const expectedDataElement = ['', '']; + let dataElement = []; + dataElement = getUpdatedDataElement(dataElement, true, 'MADID', ''); + dataElement = getUpdatedDataElement(dataElement, true, 'EXTERN_ID', ''); + expect(dataElement).toEqual(expectedDataElement); + }); + }); +}); diff --git a/src/v0/destinations/freshmarketer/data/FRESHMARKETERIdentifyConfig.json b/src/v0/destinations/freshmarketer/data/FRESHMARKETERIdentifyConfig.json index da1a8bccbe..d8abb484b2 100644 --- a/src/v0/destinations/freshmarketer/data/FRESHMARKETERIdentifyConfig.json +++ b/src/v0/destinations/freshmarketer/data/FRESHMARKETERIdentifyConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "emails", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": true }, diff --git a/src/v0/destinations/freshsales/data/identifyConfig.json b/src/v0/destinations/freshsales/data/identifyConfig.json index 9be9d9d855..94a15fd43d 100644 --- a/src/v0/destinations/freshsales/data/identifyConfig.json +++ b/src/v0/destinations/freshsales/data/identifyConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "emails", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": true }, diff --git a/src/v0/destinations/ga4/transform.js b/src/v0/destinations/ga4/transform.js index d8fc531e92..e4dad80564 100644 --- a/src/v0/destinations/ga4/transform.js +++ b/src/v0/destinations/ga4/transform.js @@ -1,25 +1,15 @@ const get = require('get-value'); -const { - ConfigurationError, - InstrumentationError, - UnsupportedEventError, -} = require('@rudderstack/integrations-lib'); +const { InstrumentationError, UnsupportedEventError } = require('@rudderstack/integrations-lib'); const { EventType } = require('../../../constants'); const { isEmptyObject, constructPayload, getIntegrationsObj, isHybridModeEnabled, - isDefinedAndNotNull, - defaultRequestConfig, - defaultPostRequestConfig, - getDestinationExternalID, removeUndefinedAndNullValues, } = require('../../util'); const { - ENDPOINT, mappingConfig, - DEBUG_ENDPOINT, ConfigCategory, trackCommonConfig, VALID_ITEM_OR_PRODUCT_PROPERTIES, @@ -27,6 +17,7 @@ const { const { getItemsArray, validateEventName, + prepareUserConsents, removeInvalidParams, isReservedEventName, getGA4ExclusionList, @@ -35,33 +26,12 @@ const { GA4_PARAMETERS_EXCLUSION, GA4_RESERVED_PARAMETER_EXCLUSION, removeReservedParameterPrefixNames, + basicValidation, + addClientDetails, + buildDeliverablePayload, + basicConfigvalidaiton, } = require('./utils'); -const { JSON_MIME_TYPE } = require('../../util/constant'); - -/** - * returns client_id - * @param {*} message - * @returns - */ -const getGA4ClientId = (message, Config) => { - let clientId; - - if (isHybridModeEnabled(Config)) { - const integrationsObj = getIntegrationsObj(message, 'ga4'); - if (integrationsObj?.clientId) { - clientId = integrationsObj.clientId; - } - } - - if (!clientId) { - clientId = - getDestinationExternalID(message, 'ga4ClientId') || - get(message, 'anonymousId') || - get(message, 'rudderId'); - } - - return clientId; -}; +require('../../util/constant'); /** * Returns response for GA4 destination @@ -71,14 +41,9 @@ const getGA4ClientId = (message, Config) => { */ const responseBuilder = (message, { Config }) => { let event = get(message, 'event'); - if (!event) { - throw new InstrumentationError('Event name is required'); - } + basicValidation(event); // trim and replace spaces with "_" - if (typeof event !== 'string') { - throw new InstrumentationError('track:: event name should be string'); - } event = event.trim().replace(/\s+/g, '_'); // reserved event names are not allowed @@ -89,25 +54,7 @@ const responseBuilder = (message, { Config }) => { // get common top level rawPayload let rawPayload = constructPayload(message, trackCommonConfig); - switch (Config.typesOfClient) { - case 'gtag': - // gtag.js uses client_id - // GA4 uses it as an identifier to distinguish site visitors. - rawPayload.client_id = getGA4ClientId(message, Config); - if (!isDefinedAndNotNull(rawPayload.client_id)) { - throw new ConfigurationError('ga4ClientId, anonymousId or messageId must be provided'); - } - break; - case 'firebase': - // firebase uses app_instance_id - rawPayload.app_instance_id = getDestinationExternalID(message, 'ga4AppInstanceId'); - if (!isDefinedAndNotNull(rawPayload.app_instance_id)) { - throw new InstrumentationError('ga4AppInstanceId must be provided under externalId'); - } - break; - default: - throw new ConfigurationError('Invalid type of client'); - } + rawPayload = addClientDetails(rawPayload, message, Config); let payload = {}; const eventConfig = ConfigCategory[`${event.toUpperCase()}`]; @@ -238,65 +185,30 @@ const responseBuilder = (message, { Config }) => { rawPayload.user_properties = userProperties; } + // Prepare GA4 consents + const consents = prepareUserConsents(message); + if (!isEmptyObject(consents)) { + rawPayload.consent = consents; + } + payload = removeUndefinedAndNullValues(payload); rawPayload = { ...rawPayload, events: [payload] }; - // build response - const response = defaultRequestConfig(); - response.method = defaultPostRequestConfig.requestMethod; - // if debug_mode is true, we need to send the event to debug validation server - // ref: https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=firebase#sending_events_for_validation - if (Config.debugMode) { - response.endpoint = DEBUG_ENDPOINT; - } else { - response.endpoint = ENDPOINT; - } - response.headers = { - HOST: 'www.google-analytics.com', - 'Content-Type': JSON_MIME_TYPE, - }; - response.params = { - api_secret: Config.apiSecret, - }; - - // setting response params as per client type - switch (Config.typesOfClient) { - case 'gtag': - response.params.measurement_id = Config.measurementId; - break; - case 'firebase': - response.params.firebase_app_id = Config.firebaseAppId; - break; - default: - break; - } - - response.body.JSON = rawPayload; - return response; + return buildDeliverablePayload(rawPayload, Config); }; const process = (event) => { const { message, destination } = event; const { Config } = destination; - if (!Config.typesOfClient) { - throw new ConfigurationError('Client type not found. Aborting '); - } - if (!Config.apiSecret) { - throw new ConfigurationError('API Secret not found. Aborting '); - } - if (Config.typesOfClient === 'gtag' && !Config.measurementId) { - throw new ConfigurationError('measurementId must be provided. Aborting'); - } - if (Config.typesOfClient === 'firebase' && !Config.firebaseAppId) { - throw new ConfigurationError('firebaseAppId must be provided. Aborting'); - } - if (!message.type) { throw new InstrumentationError('Message Type is not present. Aborting message.'); } + basicConfigvalidaiton(Config); + const messageType = message.type.toLowerCase(); + let response; switch (messageType) { case EventType.TRACK: diff --git a/src/v0/destinations/ga4/utils.js b/src/v0/destinations/ga4/utils.js index e4db494727..77f78fbfdb 100644 --- a/src/v0/destinations/ga4/utils.js +++ b/src/v0/destinations/ga4/utils.js @@ -1,5 +1,8 @@ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-plusplus */ const get = require('get-value'); -const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); +const { cloneDeep } = require('lodash'); const { isEmpty, constructPayload, @@ -7,8 +10,15 @@ const { isEmptyObject, extractCustomFields, isDefinedAndNotNull, + getIntegrationsObj, + getDestinationExternalID, + isHybridModeEnabled, + defaultPostRequestConfig, + defaultRequestConfig, } = require('../../util'); -const { mappingConfig, ConfigCategory } = require('./config'); +const { mappingConfig, ConfigCategory, DEBUG_ENDPOINT, ENDPOINT } = require('./config'); +const { finaliseAnalyticsConsents } = require('../../util/googleUtils'); +const { JSON_MIME_TYPE } = require('../../util/constant'); /** * Reserved event names cannot be used @@ -432,16 +442,165 @@ const prepareUserProperties = (message, piiPropertiesToIgnore = []) => { return validatedUserProperties; }; +/** + * Returns user consents + * Ref : https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#payload_consent + * @param {*} message + * @returns + */ +const prepareUserConsents = (message) => { + const integrationObj = getIntegrationsObj(message, 'ga4') || {}; + const eventLevelConsentsData = integrationObj?.consents || {}; + const consentConfigMap = { + analyticsPersonalizationConsent: 'ad_user_data', + analyticsUserDataConsent: 'ad_personalization', + }; + + const consents = finaliseAnalyticsConsents(consentConfigMap, eventLevelConsentsData); + return consents; +}; + +const basicValidation = (event) => { + if (!event) { + throw new InstrumentationError('Event name is required'); + } + if (typeof event !== 'string') { + throw new InstrumentationError('track:: event name should be string'); + } +}; + +/** + * returns client_id + * @param {*} message + * @returns + */ +const getGA4ClientId = (message, Config) => { + let clientId; + + if (isHybridModeEnabled(Config)) { + const integrationsObj = getIntegrationsObj(message, 'ga4'); + if (integrationsObj?.clientId) { + clientId = integrationsObj.clientId; + } + } + + if (!clientId) { + clientId = + getDestinationExternalID(message, 'ga4ClientId') || + get(message, 'anonymousId') || + get(message, 'rudderId'); + } + + return clientId; +}; + +const addClientDetails = (payload, message, Config) => { + const { typesOfClient } = Config; + const rawPayload = cloneDeep(payload); + switch (typesOfClient) { + case 'gtag': + // gtag.js uses client_id + // GA4 uses it as an identifier to distinguish site visitors. + rawPayload.client_id = getGA4ClientId(message, Config); + if (!isDefinedAndNotNull(rawPayload.client_id)) { + throw new ConfigurationError('ga4ClientId, anonymousId or messageId must be provided'); + } + break; + case 'firebase': + // firebase uses app_instance_id + rawPayload.app_instance_id = getDestinationExternalID(message, 'ga4AppInstanceId'); + if (!isDefinedAndNotNull(rawPayload.app_instance_id)) { + throw new InstrumentationError('ga4AppInstanceId must be provided under externalId'); + } + break; + default: + throw new ConfigurationError('Invalid type of client'); + } + return rawPayload; +}; + +const buildDeliverablePayload = (payload, Config) => { + // build response + const response = defaultRequestConfig(); + response.method = defaultPostRequestConfig.requestMethod; + // if debug_mode is true, we need to send the event to debug validation server + // ref: https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=firebase#sending_events_for_validation + if (Config.debugMode) { + response.endpoint = DEBUG_ENDPOINT; + } else { + response.endpoint = ENDPOINT; + } + response.headers = { + HOST: 'www.google-analytics.com', + 'Content-Type': JSON_MIME_TYPE, + }; + response.params = { + api_secret: Config.apiSecret, + }; + + // setting response params as per client type + switch (Config.typesOfClient) { + case 'gtag': + response.params.measurement_id = Config.measurementId; + break; + case 'firebase': + response.params.firebase_app_id = Config.firebaseAppId; + break; + default: + break; + } + + response.body.JSON = payload; + return response; +}; + +const sanitizeUserProperties = (userProperties) => { + Object.keys(userProperties).forEach((key) => { + const propetyValue = userProperties[key]; + if ( + typeof propetyValue === 'string' || + typeof propetyValue === 'number' || + typeof propetyValue === 'boolean' + ) { + delete userProperties[key]; + userProperties[key] = { + value: propetyValue, + }; + } + }); +}; + +const basicConfigvalidaiton = (Config) => { + if (!Config.typesOfClient) { + throw new ConfigurationError('Client type not found. Aborting '); + } + if (!Config.apiSecret) { + throw new ConfigurationError('API Secret not found. Aborting '); + } + if (Config.typesOfClient === 'gtag' && !Config.measurementId) { + throw new ConfigurationError('measurementId must be provided. Aborting'); + } + if (Config.typesOfClient === 'firebase' && !Config.firebaseAppId) { + throw new ConfigurationError('firebaseAppId must be provided. Aborting'); + } +}; + module.exports = { + addClientDetails, + basicValidation, + buildDeliverablePayload, + basicConfigvalidaiton, getItem, getItemList, getItemsArray, validateEventName, + prepareUserConsents, removeInvalidParams, isReservedEventName, getGA4ExclusionList, prepareUserProperties, getGA4CustomParameters, + sanitizeUserProperties, GA4_PARAMETERS_EXCLUSION, isReservedWebCustomEventName, isReservedWebCustomPrefixName, diff --git a/src/v0/destinations/ga4/utils.test.js b/src/v0/destinations/ga4/utils.test.js index 18b3ab5766..501778910f 100644 --- a/src/v0/destinations/ga4/utils.test.js +++ b/src/v0/destinations/ga4/utils.test.js @@ -1,4 +1,9 @@ -const { validateEventName, prepareUserProperties, removeInvalidParams } = require('./utils'); +const { + validateEventName, + removeInvalidParams, + prepareUserConsents, + prepareUserProperties, +} = require('./utils'); const userPropertyData = [ { @@ -447,4 +452,84 @@ describe('Google Analytics 4 utils test', () => { expect(result).toEqual(expected); }); }); + + describe('prepareUserConsents function tests', () => { + it('Should return an empty object when no consents are given', () => { + const message = {}; + const result = prepareUserConsents(message); + expect(result).toEqual({}); + }); + + it('Should return an empty object when no consents are given', () => { + const message = { + integrations: { + GA4: {}, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({}); + }); + + it('Should return an empty object when no consents are given', () => { + const message = { + integrations: { + GA4: { + consents: {}, + }, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({}); + }); + + it('Should return a consents object when consents are given', () => { + const message = { + integrations: { + GA4: { + consents: { + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }, + }, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({ + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }); + }); + + it('Should return an empty object when invalid consents are given', () => { + const message = { + integrations: { + GA4: { + consents: { + ad_personalization: 'NOT_SPECIFIED', + ad_user_data: 'NOT_SPECIFIED', + }, + }, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({}); + }); + + it('Should return a valid consents values from consents object', () => { + const message = { + integrations: { + GA4: { + consents: { + ad_personalization: 'NOT_SPECIFIED', + ad_user_data: 'DENIED', + }, + }, + }, + }; + const result = prepareUserConsents(message); + expect(result).toEqual({ + ad_user_data: 'DENIED', + }); + }); + }); }); diff --git a/src/v0/destinations/ga4_v2/customMappingsHandler.js b/src/v0/destinations/ga4_v2/customMappingsHandler.js new file mode 100644 index 0000000000..1eb1c2c868 --- /dev/null +++ b/src/v0/destinations/ga4_v2/customMappingsHandler.js @@ -0,0 +1,165 @@ +const get = require('get-value'); +const { + validateEventName, + basicValidation, + isReservedEventName, + addClientDetails, + removeReservedParameterPrefixNames, + prepareUserConsents, + removeInvalidParams, + GA4_RESERVED_PARAMETER_EXCLUSION, + getGA4CustomParameters, + buildDeliverablePayload, + GA4_PARAMETERS_EXCLUSION, + prepareUserProperties, +} = require('../ga4/utils'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + removeUndefinedAndNullRecurse, + constructPayload, + isDefinedAndNotNull, + isEmptyObject, + removeUndefinedAndNullValues, + isHybridModeEnabled, + getIntegrationsObj, + applyCustomMappings, +} = require('../../util'); +const { trackCommonConfig, ConfigCategory, mappingConfig } = require('../ga4/config'); + +const findGA4Events = (eventsMapping, event) => { + // Find the event using destructuring and early return + + const validMappings = eventsMapping.filter( + (mapping) => + mapping.rsEventName?.trim().toLowerCase() === event.trim().toLowerCase() && + mapping.destEventName, + ); + // Return an empty object if event not found + return validMappings; +}; + +const handleCustomMappings = (message, Config) => { + const { eventsMapping } = Config; + + let rsEvent = ''; + if (message.type.toString().toLowerCase() === 'track') { + rsEvent = get(message, 'event'); + basicValidation(rsEvent); + } else { + const messageType = get(message, 'type'); + if (typeof messageType !== 'string') { + throw new InstrumentationError(`[GA4]:: Message type ${messageType} is not supported`); + } + // for events other than track we will search with $eventType + // example $track / $page + rsEvent = `$${messageType}`; + } + + const validMappings = findGA4Events(eventsMapping, rsEvent); + + if (validMappings.length === 0) { + // trim and replace spaces with "_" + rsEvent = rsEvent.trim().replace(/\s+/g, '_'); + // reserved event names are not allowed + if (isReservedEventName(rsEvent)) { + throw new InstrumentationError(`[GA4]:: Reserved event name: ${rsEvent} are not allowed`); + } + // validation for ga4 event name + validateEventName(rsEvent); + + // Default mapping + + let rawPayload = constructPayload(message, trackCommonConfig); + + const ga4EventPayload = {}; + + // take optional params parameters for custom events + ga4EventPayload.params = { + ...ga4EventPayload.params, + ...constructPayload(message, mappingConfig[ConfigCategory.TrackPageCommonParamsConfig.name]), + }; + + // all extra parameters passed is incorporated inside params + ga4EventPayload.params = getGA4CustomParameters( + message, + ['properties'], + GA4_RESERVED_PARAMETER_EXCLUSION.concat(GA4_PARAMETERS_EXCLUSION), + ga4EventPayload, + ); + + // Prepare GA4 user properties + const userProperties = prepareUserProperties(message, Config.piiPropertiesToIgnore); + if (!isEmptyObject(userProperties)) { + rawPayload.user_properties = userProperties; + } + + rawPayload = removeUndefinedAndNullValues(rawPayload); + rawPayload = { ...rawPayload, events: [ga4EventPayload] }; + + boilerplateOperations(rawPayload, message, Config, rsEvent); + + return buildDeliverablePayload(rawPayload, Config); + } + + const processedPayloads = validMappings.map((mapping) => { + const eventName = mapping.destEventName; + // reserved event names are not allowed + if (isReservedEventName(eventName)) { + throw new InstrumentationError(`[GA4]:: Reserved event name: ${eventName} are not allowed`); + } + // validation for ga4 event name + validateEventName(eventName); + + // Add common top level payload + let ga4BasicPayload = constructPayload(message, trackCommonConfig); + ga4BasicPayload = addClientDetails(ga4BasicPayload, message, Config); + + const eventPropertiesMappings = mapping.eventProperties || []; + + const ga4MappedPayload = applyCustomMappings(message, eventPropertiesMappings); + + removeUndefinedAndNullRecurse(ga4MappedPayload); + + boilerplateOperations(ga4MappedPayload, message, Config, eventName); + + if (isDefinedAndNotNull(ga4BasicPayload)) { + return { ...ga4BasicPayload, ...ga4MappedPayload }; + } else { + return ga4MappedPayload; + } + }); + + return processedPayloads.map((processedPayload) => + buildDeliverablePayload(processedPayload, Config), + ); +}; + +const boilerplateOperations = (ga4Payload, message, Config, eventName) => { + removeReservedParameterPrefixNames(ga4Payload.events[0].params); + ga4Payload.events[0].name = eventName; + const integrationsObj = getIntegrationsObj(message, 'ga4'); + + if (isHybridModeEnabled(Config) && integrationsObj?.sessionId) { + ga4Payload.events[0].params.session_id = integrationsObj.sessionId; + } + + if (ga4Payload.events[0].params) { + ga4Payload.events[0].params = removeInvalidParams( + removeUndefinedAndNullValues(ga4Payload.events[0].params), + ); + } + + if (isEmptyObject(ga4Payload.events[0].params)) { + delete ga4Payload.events[0].params; + } + + // Prepare GA4 consents + const consents = prepareUserConsents(message); + if (!isEmptyObject(consents)) { + ga4Payload.consent = consents; + } +}; + +module.exports = { + handleCustomMappings, +}; diff --git a/src/v0/destinations/ga4_v2/transform.ts b/src/v0/destinations/ga4_v2/transform.ts new file mode 100644 index 0000000000..76adc00e00 --- /dev/null +++ b/src/v0/destinations/ga4_v2/transform.ts @@ -0,0 +1,25 @@ +import { InstrumentationError, RudderStackEvent } from '@rudderstack/integrations-lib'; +import { ProcessorTransformationRequest } from '../../../types'; +import { handleCustomMappings } from './customMappingsHandler'; +import { process as ga4Process } from '../ga4/transform'; +import { basicConfigvalidaiton } from '../ga4/utils'; + +export function process(event: ProcessorTransformationRequest) { + const { message, destination } = event; + const { Config } = destination; + + const eventPayload = message as RudderStackEvent; + + if (!eventPayload.type) { + throw new InstrumentationError('Message Type is not present. Aborting message.'); + } + + if (eventPayload.type !== 'track') { + return ga4Process(event); + } + + basicConfigvalidaiton(Config); + + // custom mappings flow + return handleCustomMappings(message, Config); +} diff --git a/src/v0/destinations/gcs_datalake/transform.js b/src/v0/destinations/gcs_datalake/transform.js index 3e5f1dcfa3..366dcf3483 100644 --- a/src/v0/destinations/gcs_datalake/transform.js +++ b/src/v0/destinations/gcs_datalake/transform.js @@ -1,25 +1,22 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const gcsDatalake = 'gcs_datalake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} - -function getDataTypeOverride() {} +const provider = 'gcs_datalake'; function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = gcsDatalake; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, - getDataTypeOverride, + getDataTypeOverride: () => {}, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } -exports.process = process; +module.exports = { + provider, + process, +}; diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/config.js b/src/v0/destinations/google_adwords_enhanced_conversions/config.js index 8d194655f7..e8f486fb7a 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/config.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/config.js @@ -16,4 +16,5 @@ module.exports = { BASE_ENDPOINT, hashAttributes, CONVERSION_ACTION_ID_CACHE_TTL, + destType: 'google_adwords_enhanced_conversions', }; diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json b/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json index 562a77f0e8..c38b24598d 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json +++ b/src/v0/destinations/google_adwords_enhanced_conversions/data/trackConfig.json @@ -51,11 +51,11 @@ }, { "destKey": "conversionAdjustments[0].userIdentifiers[0].hashedEmail", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } }, { @@ -64,7 +64,7 @@ "sourceFromGenericMap": true, "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } }, { @@ -73,7 +73,7 @@ "sourceFromGenericMap": true, "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } }, { @@ -82,7 +82,7 @@ "sourceFromGenericMap": true, "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } }, { @@ -127,7 +127,7 @@ "sourceKeys": ["context.traits.streetAddress", "context.traits.address"], "required": false, "metadata": { - "type": "hashToSha256" + "type": ["trim", "hashToSha256"] } } ] diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js index 3ea985e773..d82349c04d 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js @@ -1,10 +1,12 @@ const { get, set } = require('lodash'); const sha256 = require('sha256'); const { NetworkError, NetworkInstrumentationError } = require('@rudderstack/integrations-lib'); +const SqlString = require('sqlstring'); const { prepareProxyRequest, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index'); -const { CONVERSION_ACTION_ID_CACHE_TTL } = require('./config'); +const { CONVERSION_ACTION_ID_CACHE_TTL, destType } = require('./config'); const Cache = require('../../util/cache'); +const logger = require('../../../logger'); const conversionActionIdCache = new Cache(CONVERSION_ACTION_ID_CACHE_TTL); @@ -26,14 +28,23 @@ const ERROR_MSG_PATH = 'response[0].error.message'; * @returns */ -const getConversionActionId = async (method, headers, params) => { +const getConversionActionId = async ({ method, headers, params, metadata }) => { const conversionActionIdKey = sha256(params.event + params.customerId).toString(); return conversionActionIdCache.get(conversionActionIdKey, async () => { + const queryString = SqlString.format( + 'SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = ?', + [params.event], + ); const data = { - query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = '${params.event}'`, + query: queryString, }; + const searchStreamEndpoint = `${BASE_ENDPOINT}/${params.customerId}/googleAds:searchStream`; + logger.requestLog(`[${destType.toUpperCase()}] get conversion action id request`, { + metadata, + requestDetails: { url: searchStreamEndpoint, body: data, method }, + }); const requestBody = { - url: `${BASE_ENDPOINT}/${params.customerId}/googleAds:searchStream`, + url: searchStreamEndpoint, data, headers, method, @@ -47,21 +58,31 @@ const getConversionActionId = async (method, headers, params) => { endpointPath: `/googleAds:searchStream`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }, ); - if (!isHttpStatusSuccess(gaecConversionActionIdResponse.status)) { + const { status, response, headers: responseHeaders } = gaecConversionActionIdResponse; + logger.responseLog(`[${destType.toUpperCase()}] get conversion action id response`, { + metadata, + responseDetails: { + response, + status, + headers: responseHeaders, + }, + }); + if (!isHttpStatusSuccess(status)) { throw new NetworkError( `"${JSON.stringify( get(gaecConversionActionIdResponse, ERROR_MSG_PATH, '') ? get(gaecConversionActionIdResponse, ERROR_MSG_PATH, '') - : gaecConversionActionIdResponse.response, + : response, )} during Google_adwords_enhanced_conversions response transformation"`, - gaecConversionActionIdResponse.status, + status, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(gaecConversionActionIdResponse.status), + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, - gaecConversionActionIdResponse.response, - getAuthErrCategoryFromStCode(gaecConversionActionIdResponse.status), + response, + getAuthErrCategoryFromStCode(status), ); } const conversionActionId = get( @@ -85,23 +106,41 @@ const getConversionActionId = async (method, headers, params) => { * @returns */ const ProxyRequest = async (request) => { - const { body, method, endpoint, params } = request; + const { body, method, endpoint, params, metadata } = request; const { headers } = request; - const conversionActionId = await getConversionActionId(method, headers, params); + const conversionActionId = await getConversionActionId({ method, headers, params, metadata }); set( body.JSON, 'conversionAdjustments[0].conversionAction', `customers/${params.customerId}/conversionActions/${conversionActionId}`, ); + logger.requestLog(`[${destType.toUpperCase()}] conversion enhancement request`, { + metadata, + requestDetails: { url: endpoint, body: body.JSON, method }, + }); const requestBody = { url: endpoint, data: body.JSON, headers, method }; - const { httpResponse: response } = await handleHttpRequest('constructor', requestBody, { - destType: 'google_adwords_enhanced_conversions', - feature: 'proxy', - endpointPath: `/googleAds:uploadOfflineUserData`, - requestMethod: 'POST', - module: 'dataDelivery', + const { httpResponse: response, processedResponse } = await handleHttpRequest( + 'constructor', + requestBody, + { + destType: 'google_adwords_enhanced_conversions', + feature: 'proxy', + endpointPath: `/googleAds:uploadOfflineUserData`, + requestMethod: 'POST', + module: 'dataDelivery', + metadata, + }, + ); + const { response: processedResp, status, headers: responseHeaders } = processedResponse; + logger.responseLog(`[${destType.toUpperCase()}] conversion enhancement response`, { + metadata, + responseDetails: { + response: processedResp, + status, + headers: responseHeaders, + }, }); return response; }; @@ -117,7 +156,7 @@ const responseHandler = (responseParams) => { // Ref - https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto if (partialFailureError && partialFailureError.code !== 0) { throw new NetworkError( - `[Google Ads Offline Conversions]:: partialFailureError - ${JSON.stringify( + `[Google Adwords Enhanced Conversions]:: partialFailureError - ${JSON.stringify( partialFailureError, )}`, 400, diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js index 0be7c3f0ee..55d0c16c8c 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js @@ -24,7 +24,7 @@ const { JSON_MIME_TYPE } = require('../../util/constant'); const updateMappingJson = (mapping) => { const newMapping = []; mapping.forEach((element) => { - if (get(element, 'metadata.type') && element.metadata.type === 'hashToSha256') { + if (get(element, 'metadata.type') && element.metadata.type.includes('hashToSha256')) { element.metadata.type = 'toString'; } newMapping.push(element); diff --git a/src/v0/destinations/google_adwords_offline_conversions/config.js b/src/v0/destinations/google_adwords_offline_conversions/config.js index a02732894f..6eec1068a6 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/config.js +++ b/src/v0/destinations/google_adwords_offline_conversions/config.js @@ -1,6 +1,6 @@ const { getMappingConfig } = require('../../util'); -const API_VERSION = 'v14'; +const API_VERSION = 'v16'; const BASE_ENDPOINT = `https://googleads.googleapis.com/${API_VERSION}/customers/:customerId`; @@ -42,7 +42,13 @@ const CONVERSION_CUSTOM_VARIABLE_CACHE_TTL = process.env.CONVERSION_CUSTOM_VARIA const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); +const consentConfigMap = { + personalizationConsent: 'adPersonalization', + userDataConsent: 'adUserData', +}; + module.exports = { + destType: 'google_adwords_offline_conversions', trackClickConversionsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_CLICK_CONVERSIONS_CONFIG.name], trackCallConversionsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_CALL_CONVERSIONS_CONFIG.name], @@ -58,4 +64,5 @@ module.exports = { MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_STORE_CONVERSION_CONFIG_ADD_CONVERSION.name], trackAddStoreAddressConversionsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.TRACK_STORE_ADDRESS_IDENTIFIER.name], + consentConfigMap, }; diff --git a/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js b/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js index 5541fd6e1e..51bc57d176 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js @@ -6,7 +6,7 @@ const { NetworkInstrumentationError, NetworkError, } = require('@rudderstack/integrations-lib'); -const { prepareProxyRequest, httpSend, httpPOST } = require('../../../adapters/network'); +const { prepareProxyRequest, httpPOST, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getHashFromArray, @@ -15,31 +15,49 @@ const { } = require('../../util'); const { getConversionActionId } = require('./utils'); const Cache = require('../../util/cache'); -const { CONVERSION_CUSTOM_VARIABLE_CACHE_TTL, SEARCH_STREAM } = require('./config'); +const { CONVERSION_CUSTOM_VARIABLE_CACHE_TTL, SEARCH_STREAM, destType } = require('./config'); const { processAxiosResponse, getDynamicErrorType, } = require('../../../adapters/utils/networkUtils'); const tags = require('../../util/tags'); +const logger = require('../../../logger'); const conversionCustomVariableCache = new Cache(CONVERSION_CUSTOM_VARIABLE_CACHE_TTL); -const createJob = async (endpoint, headers, payload) => { +const createJob = async ({ endpoint, headers, payload, metadata }) => { const endPoint = `${endpoint}:create`; + logger.requestLog(`[${destType.toUpperCase()}] job creation request`, { + metadata, + requestDetails: { + url: endpoint, + body: payload, + method: 'post', + }, + }); let createJobResponse = await httpPOST( endPoint, payload, { headers }, { - destType: 'google_adwords_offline_conversions', + destType, feature: 'proxy', endpointPath: `/create`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }, ); createJobResponse = processAxiosResponse(createJobResponse); - const { response, status } = createJobResponse; + const { response, status, headers: responseHeaders } = createJobResponse; + logger.responseLog(`[${destType.toUpperCase()}] create job`, { + metadata, + responseDetails: { + headers: responseHeaders, + status, + response, + }, + }); if (!isHttpStatusSuccess(status)) { throw new AbortedError( `[Google Ads Offline Conversions]:: ${response?.error?.message} during google_ads_offline_store_conversions Job Creation`, @@ -51,8 +69,16 @@ const createJob = async (endpoint, headers, payload) => { return response.resourceName.split('/')[3]; }; -const addConversionToJob = async (endpoint, headers, jobId, payload) => { +const addConversionToJob = async ({ endpoint, headers, jobId, payload, metadata }) => { const endPoint = `${endpoint}/${jobId}:addOperations`; + logger.requestLog(`[${destType.toUpperCase()}] add conversion to job request`, { + metadata, + requestDetails: { + url: endpoint, + body: payload, + method: 'post', + }, + }); let addConversionToJobResponse = await httpPOST( endPoint, payload, @@ -63,23 +89,42 @@ const addConversionToJob = async (endpoint, headers, jobId, payload) => { endpointPath: `/addOperations`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }, ); addConversionToJobResponse = processAxiosResponse(addConversionToJobResponse); - if (!isHttpStatusSuccess(addConversionToJobResponse.status)) { + const { response, status, headers: responseHeaders } = addConversionToJobResponse; + logger.responseLog(`[${destType.toUpperCase()}] add conversion to job`, { + metadata, + responseDetails: { + response, + status, + headers: responseHeaders, + }, + }); + if (!isHttpStatusSuccess(status)) { throw new AbortedError( - `[Google Ads Offline Conversions]:: ${addConversionToJobResponse.response?.error?.message} during google_ads_offline_store_conversions Add Conversion`, - addConversionToJobResponse.status, - addConversionToJobResponse.response, + `[Google Ads Offline Conversions]:: ${response?.error?.message} during google_ads_offline_store_conversions Add Conversion`, + status, + response, getAuthErrCategoryFromStCode(get(addConversionToJobResponse, 'status')), ); } return true; }; -const runTheJob = async (endpoint, headers, payload, jobId) => { +const runTheJob = async ({ endpoint, headers, payload, jobId, metadata }) => { const endPoint = `${endpoint}/${jobId}:run`; - const executeJobResponse = await httpPOST( + logger.requestLog(`[${destType.toUpperCase()}] run job request`, { + metadata, + requestDetails: { + body: payload, + method: 'POST', + url: endPoint, + }, + }); + const { httpResponse: executeJobResponse, processedResponse } = await handleHttpRequest( + 'post', endPoint, payload, { headers }, @@ -89,8 +134,18 @@ const runTheJob = async (endpoint, headers, payload, jobId) => { endpointPath: `/run`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }, ); + const { headers: responseHeaders, response, status } = processedResponse; + logger.responseLog(`[${destType.toUpperCase()}] run job`, { + metadata, + responseDetails: { + response, + status, + responseHeaders, + }, + }); return executeJobResponse; }; @@ -102,7 +157,7 @@ const runTheJob = async (endpoint, headers, payload, jobId) => { * @param {*} headers * @returns */ -const getConversionCustomVariable = async (headers, params) => { +const getConversionCustomVariable = async ({ headers, params, metadata }) => { const conversionCustomVariableKey = sha256(params.customerId).toString(); return conversionCustomVariableCache.get(conversionCustomVariableKey, async () => { const data = { @@ -112,23 +167,41 @@ const getConversionCustomVariable = async (headers, params) => { const requestOptions = { headers, }; + logger.requestLog(`[${destType.toUpperCase()}] get conversion custom variable request`, { + metadata, + requestDetails: { + url: endpoint, + body: data, + method: 'post', + }, + }); let searchStreamResponse = await httpPOST(endpoint, data, requestOptions, { destType: 'google_adwords_offline_conversions', feature: 'proxy', endpointPath: `/searchStream`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }); searchStreamResponse = processAxiosResponse(searchStreamResponse); - if (!isHttpStatusSuccess(searchStreamResponse.status)) { + const { response, status, headers: responseHeaders } = searchStreamResponse; + logger.responseLog(`[${destType.toUpperCase()}] get conversion custom variable`, { + metadata, + responseDetails: { + response, + status, + headers: responseHeaders, + }, + }); + if (!isHttpStatusSuccess(status)) { throw new NetworkError( - `[Google Ads Offline Conversions]:: ${searchStreamResponse?.response?.[0]?.error?.message} during google_ads_offline_conversions response transformation`, - searchStreamResponse.status, + `[Google Ads Offline Conversions]:: ${response?.[0]?.error?.message} during google_ads_offline_conversions response transformation`, + status, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(searchStreamResponse.status), + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, - searchStreamResponse?.response || searchStreamResponse, - getAuthErrCategoryFromStCode(searchStreamResponse.status), + response || searchStreamResponse, + getAuthErrCategoryFromStCode(status), ); } const conversionCustomVariable = get(searchStreamResponse, 'response.0.results'); @@ -195,37 +268,53 @@ const isValidCustomVariables = (customVariables) => { * @returns */ const ProxyRequest = async (request) => { - const { method, endpoint, headers, params, body } = request; + const { method, endpoint, headers, params, body, metadata } = request; if (body.JSON?.isStoreConversion) { - const firstResponse = await createJob(endpoint, headers, body.JSON.createJobPayload); + const firstResponse = await createJob({ + endpoint, + headers, + payload: body.JSON.createJobPayload, + metadata, + }); const addPayload = body.JSON.addConversionPayload; // Mapping Conversion Action - const conversionId = await getConversionActionId(headers, params); + const conversionId = await getConversionActionId({ headers, params, metadata }); if (Array.isArray(addPayload.operations)) { addPayload.operations.forEach((operation) => { set(operation, 'create.transaction_attribute.conversion_action', conversionId); }); } - await addConversionToJob(endpoint, headers, firstResponse, addPayload); - const thirdResponse = await runTheJob( + await addConversionToJob({ endpoint, headers, - body.JSON.executeJobPayload, - firstResponse, - ); + jobId: firstResponse, + payload: addPayload, + metadata, + }); + const thirdResponse = await runTheJob({ + endpoint, + headers, + payload: body.JSON.executeJobPayload, + jobId: firstResponse, + metadata, + }); return thirdResponse; } // fetch conversionAction // httpPOST -> myAxios.post() if (params?.event) { - const conversionActionId = await getConversionActionId(headers, params); + const conversionActionId = await getConversionActionId({ headers, params, metadata }); set(body.JSON, 'conversions.0.conversionAction', conversionActionId); } // customVariables would be undefined in case of Store Conversions if (isValidCustomVariables(params.customVariables)) { // fetch all conversion custom variable in google ads - let conversionCustomVariable = await getConversionCustomVariable(headers, params); + let conversionCustomVariable = await getConversionCustomVariable({ + headers, + params, + metadata, + }); // convert it into hashMap conversionCustomVariable = getConversionCustomVariableHashMap(conversionCustomVariable); @@ -249,16 +338,29 @@ const ProxyRequest = async (request) => { set(body.JSON, 'conversions.0.customVariables', resultantCustomVariables); } } - const requestBody = { url: endpoint, data: body.JSON, headers, method }; - const response = await httpSend(requestBody, { + logger.requestLog(`[${destType.toUpperCase()}] offline conversion creation request`, { + metadata, + requestDetails: { url: requestBody.url, body: requestBody.data, method }, + }); + const { httpResponse, processedResponse } = await handleHttpRequest('constructor', requestBody, { feature: 'proxy', destType: 'gogole_adwords_offline_conversions', endpointPath: `/proxy`, requestMethod: 'POST', module: 'dataDelivery', + metadata, + }); + const { headers: responseHeaders, status, response } = processedResponse; + logger.responseLog(`[${destType.toUpperCase()}] deliver event to destination`, { + metadata, + responseDetails: { + response, + headers: responseHeaders, + status, + }, }); - return response; + return httpResponse; }; const responseHandler = (responseParams) => { diff --git a/src/v0/destinations/google_adwords_offline_conversions/transform.js b/src/v0/destinations/google_adwords_offline_conversions/transform.js index 68d4d01fa7..76b12587cd 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/transform.js +++ b/src/v0/destinations/google_adwords_offline_conversions/transform.js @@ -3,7 +3,6 @@ const { InstrumentationError, ConfigurationError } = require('@rudderstack/integ const { EventType } = require('../../../constants'); const { getHashFromArrayWithDuplicate, - constructPayload, removeHyphens, getHashFromArray, handleRtTfSingleEventError, @@ -11,16 +10,14 @@ const { getSuccessRespEvents, combineBatchRequestsWithSameJobIds, } = require('../../util'); -const { - CALL_CONVERSION, - trackCallConversionsMapping, - STORE_CONVERSION_CONFIG, -} = require('./config'); +const { CALL_CONVERSION, STORE_CONVERSION_CONFIG } = require('./config'); const { validateDestinationConfig, getStoreConversionPayload, requestBuilder, getClickConversionPayloadAndEndpoint, + getConsentsDataFromIntegrationObj, + getCallConversionPayload, } = require('./utils'); const helper = require('./helper'); @@ -41,12 +38,15 @@ const getConversions = (message, metadata, { Config }, event, conversionType) => const { properties, timestamp, originalTimestamp } = message; const filteredCustomerId = removeHyphens(customerId); + const eventLevelConsentsData = getConsentsDataFromIntegrationObj(message); + if (conversionType === 'click') { // click conversion const convertedPayload = getClickConversionPayloadAndEndpoint( message, Config, filteredCustomerId, + eventLevelConsentsData, ); payload = convertedPayload.payload; endpoint = convertedPayload.endpoint; @@ -55,7 +55,7 @@ const getConversions = (message, metadata, { Config }, event, conversionType) => endpoint = STORE_CONVERSION_CONFIG.replace(':customerId', filteredCustomerId); } else { // call conversions - payload = constructPayload(message, trackCallConversionsMapping); + payload = getCallConversionPayload(message, Config, eventLevelConsentsData); endpoint = CALL_CONVERSION.replace(':customerId', filteredCustomerId); } @@ -119,7 +119,6 @@ const trackResponseBuilder = (message, metadata, destination) => { const process = async (event) => { const { message, metadata, destination } = event; - if (!message.type) { throw new InstrumentationError('Message type is not present. Aborting message.'); } @@ -171,9 +170,8 @@ const batchEvents = (storeSalesEvents) => { storeSalesEvent.message?.body?.JSON?.addConversionPayload?.operations, ); batchEventResponse.metadatas.push(storeSalesEvent.metadata); - batchEventResponse.destination = storeSalesEvent.destination; }); - + batchEventResponse.destination = storeSalesEvents[0].destination; return [ getSuccessRespEvents( batchEventResponse.batchedRequest, diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index ee677373a3..bf1773d450 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -1,4 +1,5 @@ const sha256 = require('sha256'); +const SqlString = require('sqlstring'); const { get, set, cloneDeep } = require('lodash'); const { AbortedError, @@ -17,6 +18,7 @@ const { isDefinedAndNotNull, getAuthErrCategoryFromStCode, getAccessToken, + getIntegrationsObj, } = require('../../util'); const { SEARCH_STREAM, @@ -26,10 +28,15 @@ const { trackAddStoreAddressConversionsMapping, trackClickConversionsMapping, CLICK_CONVERSION, + trackCallConversionsMapping, + consentConfigMap, + destType, } = require('./config'); const { processAxiosResponse } = require('../../../adapters/utils/networkUtils'); const Cache = require('../../util/cache'); const helper = require('./helper'); +const { finaliseConsent } = require('../../util/googleUtils'); +const logger = require('../../../logger'); const conversionActionIdCache = new Cache(CONVERSION_ACTION_ID_CACHE_TTL); @@ -50,31 +57,53 @@ const validateDestinationConfig = ({ Config }) => { * @param {*} headers * @returns */ -const getConversionActionId = async (headers, params) => { +const getConversionActionId = async ({ headers, params, metadata }) => { const conversionActionIdKey = sha256(params.event + params.customerId).toString(); return conversionActionIdCache.get(conversionActionIdKey, async () => { + const queryString = SqlString.format( + 'SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = ?', + [params.event], + ); const data = { - query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = '${params.event}'`, + query: queryString, }; const endpoint = SEARCH_STREAM.replace(':customerId', params.customerId); const requestOptions = { headers, }; + logger.requestLog(`[${destType.toUpperCase()}] get conversion action id request`, { + metadata, + requestDetails: { + url: endpoint, + body: data, + method: 'post', + }, + }); let searchStreamResponse = await httpPOST(endpoint, data, requestOptions, { destType: 'google_adwords_offline_conversions', feature: 'transformation', endpointPath: `/googleAds:searchStream`, requestMethod: 'POST', module: 'dataDelivery', + metadata, }); searchStreamResponse = processAxiosResponse(searchStreamResponse); - if (!isHttpStatusSuccess(searchStreamResponse.status)) { + const { response, status, headers: responseHeaders } = searchStreamResponse; + logger.responseLog(`[${destType.toUpperCase()}] get conversion action id response`, { + metadata, + responseDetails: { + response, + status, + headers: responseHeaders, + }, + }); + if (!isHttpStatusSuccess(status)) { throw new AbortedError( `[Google Ads Offline Conversions]:: ${JSON.stringify( - searchStreamResponse.response, + response, )} during google_ads_offline_conversions response transformation`, - searchStreamResponse.status, - searchStreamResponse.response, + status, + response, getAuthErrCategoryFromStCode(get(searchStreamResponse, 'status')), ); } @@ -131,17 +160,17 @@ const buildAndGetAddress = (message, hashUserIdentifier) => { const address = constructPayload(message, trackAddStoreAddressConversionsMapping); if (address.hashed_last_name) { address.hashed_last_name = hashUserIdentifier - ? sha256(address.hashed_last_name).toString() + ? sha256(address.hashed_last_name.trim()).toString() : address.hashed_last_name; } if (address.hashed_first_name) { address.hashed_first_name = hashUserIdentifier - ? sha256(address.hashed_first_name).toString() + ? sha256(address.hashed_first_name.trim()).toString() : address.hashed_first_name; } if (address.hashed_street_address) { address.hashed_street_address = hashUserIdentifier - ? sha256(address.hashed_street_address).toString() + ? sha256(address.hashed_street_address.trim()).toString() : address.hashed_street_address; } return Object.keys(address).length > 0 ? address : null; @@ -216,6 +245,17 @@ function getExisitingUserIdentifier(userIdentifierInfo, defaultUserIdentifier) { return result; } +const getCallConversionPayload = (message, Config, eventLevelConsentsData) => { + const payload = constructPayload(message, trackCallConversionsMapping); + // here conversions[0] should be present because there are some mandatory properties mapped in the mapping json. + payload.conversions[0].consent = finaliseConsent( + consentConfigMap, + eventLevelConsentsData, + Config, + ); + return payload; +}; + /** * This Function create the add conversion payload * and returns the payload @@ -249,8 +289,10 @@ const getAddConversionPayload = (message, Config) => { const phone = getFieldValueFromMessage(message, 'phone'); const userIdentifierInfo = { - email: hashUserIdentifier && isDefinedAndNotNull(email) ? sha256(email).toString() : email, - phone: hashUserIdentifier && isDefinedAndNotNull(phone) ? sha256(phone).toString() : phone, + email: + hashUserIdentifier && isDefinedAndNotNull(email) ? sha256(email.trim()).toString() : email, + phone: + hashUserIdentifier && isDefinedAndNotNull(phone) ? sha256(phone.trim()).toString() : phone, address: buildAndGetAddress(message, hashUserIdentifier), }; @@ -272,6 +314,10 @@ const getAddConversionPayload = (message, Config) => { set(payload, 'operations.create.userIdentifiers[0]', {}); } } + // add consent support for store conversions. Note: No event level consent supported. + const consentObject = finaliseConsent(consentConfigMap, {}, Config); + // create property should be present because there are some mandatory properties mapped in the mapping json. + set(payload, 'operations.create.consent', consentObject); return payload; }; @@ -287,7 +333,12 @@ const getStoreConversionPayload = (message, Config, event) => { return payload; }; -const getClickConversionPayloadAndEndpoint = (message, Config, filteredCustomerId) => { +const getClickConversionPayloadAndEndpoint = ( + message, + Config, + filteredCustomerId, + eventLevelConsent, +) => { const email = getFieldValueFromMessage(message, 'emailOnly'); const phone = getFieldValueFromMessage(message, 'phone'); const { hashUserIdentifier, defaultUserIdentifier, UserIdentifierSource, conversionEnvironment } = @@ -334,8 +385,10 @@ const getClickConversionPayloadAndEndpoint = (message, Config, filteredCustomerI // Ref - https://developers.google.com/google-ads/api/rest/reference/rest/v11/customers/uploadClickConversions#ClickConversion const userIdentifierInfo = { - email: hashUserIdentifier && isDefinedAndNotNull(email) ? sha256(email).toString() : email, - phone: hashUserIdentifier && isDefinedAndNotNull(phone) ? sha256(phone).toString() : phone, + email: + hashUserIdentifier && isDefinedAndNotNull(email) ? sha256(email.trim()).toString() : email, + phone: + hashUserIdentifier && isDefinedAndNotNull(phone) ? sha256(phone.trim()).toString() : phone, }; const keyName = getExisitingUserIdentifier(userIdentifierInfo, defaultUserIdentifier); @@ -359,9 +412,19 @@ const getClickConversionPayloadAndEndpoint = (message, Config, filteredCustomerI if (!properties.conversionEnvironment && conversionEnvironment !== 'none') { set(payload, 'conversions[0].conversionEnvironment', conversionEnvironment); } + + // add consent support for click conversions + const consentObject = finaliseConsent(consentConfigMap, eventLevelConsent, Config); + // here conversions[0] is expected to be present there are some mandatory properties mapped in the mapping json. + set(payload, 'conversions[0].consent', consentObject); return { payload, endpoint }; }; +const getConsentsDataFromIntegrationObj = (message) => { + const integrationObj = getIntegrationsObj(message, 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS') || {}; + return integrationObj?.consents || {}; +}; + module.exports = { validateDestinationConfig, generateItemListFromProducts, @@ -372,4 +435,6 @@ module.exports = { buildAndGetAddress, getClickConversionPayloadAndEndpoint, getExisitingUserIdentifier, + getConsentsDataFromIntegrationObj, + getCallConversionPayload, }; diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.test.js b/src/v0/destinations/google_adwords_offline_conversions/utils.test.js index 8deaa3ab0a..2d1863413c 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.test.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.test.js @@ -2,6 +2,8 @@ const { getClickConversionPayloadAndEndpoint, buildAndGetAddress, getExisitingUserIdentifier, + getConsentsDataFromIntegrationObj, + getCallConversionPayload, } = require('./utils'); const getTestMessage = () => { @@ -161,12 +163,16 @@ describe('getExisitingUserIdentifier util tests', () => { describe('getClickConversionPayloadAndEndpoint util tests', () => { it('getClickConversionPayloadAndEndpoint flow check when default field identifier is present', () => { let expectedOutput = { - endpoint: 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + endpoint: 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', payload: { conversions: [ { conversionDateTime: '2022-01-01 12:32:45-08:00', conversionEnvironment: 'WEB', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, userIdentifiers: [ { hashedEmail: 'fa922cb41ff930664d4c9ced3c472ce7ecf29a0f8248b7018456e990177fff75', @@ -187,11 +193,15 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { delete fittingPayload.traits.email; delete fittingPayload.properties.email; let expectedOutput = { - endpoint: 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + endpoint: 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', payload: { conversions: [ { conversionDateTime: '2022-01-01 12:32:45-08:00', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, conversionEnvironment: 'WEB', userIdentifiers: [ { @@ -215,7 +225,7 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { delete fittingPayload.traits.phone; delete fittingPayload.properties.email; let expectedOutput = { - endpoint: 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + endpoint: 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', payload: { conversions: [ { @@ -237,7 +247,7 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { ).toThrow('Either of email or phone is required for user identifier'); }); - it('getClickConversionPayloadAndEndpoint flow check when default field identifier is present and product list present', () => { + it('finaliseConsent', () => { let fittingPayload = { ...getTestMessage() }; fittingPayload.properties.products = [ { @@ -251,13 +261,17 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { }, ]; let expectedOutput = { - endpoint: 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + endpoint: 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', payload: { conversions: [ { cartData: { items: [{ productId: 1234, quantity: 2, unitPrice: 10 }] }, conversionDateTime: '2022-01-01 12:32:45-08:00', conversionEnvironment: 'WEB', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, userIdentifiers: [ { hashedEmail: 'fa922cb41ff930664d4c9ced3c472ce7ecf29a0f8248b7018456e990177fff75', @@ -273,3 +287,118 @@ describe('getClickConversionPayloadAndEndpoint util tests', () => { ); }); }); + +describe('getConsentsDataFromIntegrationObj', () => { + it('should return an empty object when conversionType is "store"', () => { + const message = {}; + const result = getConsentsDataFromIntegrationObj(message); + expect(result).toEqual({}); + }); + it('should return the consent object when conversion type is call', () => { + const message = { + integrations: { + GOOGLE_ADWORDS_OFFLINE_CONVERSIONS: { + consents: { + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }, + }, + }, + }; + const conversionType = 'call'; + const result = getConsentsDataFromIntegrationObj(message, conversionType); + expect(result).toEqual({ + adPersonalization: 'DENIED', + adUserData: 'GRANTED', + }); + }); +}); + +describe('getCallConversionPayload', () => { + it('should call conversion payload with consent object', () => { + const message = { + properties: { + callerId: '1234', + callStartDateTime: '2022-01-01 12:32:45-08:00', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + }; + const result = getCallConversionPayload( + message, + { + userDataConsent: 'GRANTED', + personalizationConsent: 'DENIED', + }, + { + adUserData: 'GRANTED', + adPersonalization: 'GRANTED', + }, + ); + expect(result).toEqual({ + conversions: [ + { + callStartDateTime: '2022-01-01 12:32:45-08:00', + callerId: '1234', + consent: { + adPersonalization: 'GRANTED', + adUserData: 'GRANTED', + }, + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + ], + }); + }); + it('should call conversion payload with consent object', () => { + const message = { + properties: { + callerId: '1234', + callStartDateTime: '2022-01-01 12:32:45-08:00', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + }; + const result = getCallConversionPayload( + message, + { + userDataConsent: 'GRANTED', + personalizationConsent: 'DENIED', + }, + {}, + ); + expect(result).toEqual({ + conversions: [ + { + callStartDateTime: '2022-01-01 12:32:45-08:00', + callerId: '1234', + consent: { + adPersonalization: 'DENIED', + adUserData: 'GRANTED', + }, + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + ], + }); + }); + it('should call conversion payload with consent object even if no consent input from UI as well as event level', () => { + const message = { + properties: { + callerId: '1234', + callStartDateTime: '2022-01-01 12:32:45-08:00', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + }; + const result = getCallConversionPayload(message, {}, {}); + expect(result).toEqual({ + conversions: [ + { + callStartDateTime: '2022-01-01 12:32:45-08:00', + callerId: '1234', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + ], + }); + }); +}); diff --git a/src/v0/destinations/google_adwords_remarketing_lists/config.js b/src/v0/destinations/google_adwords_remarketing_lists/config.js index 5bf0d8a299..c7b97e0e6c 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/config.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/config.js @@ -1,6 +1,6 @@ const { getMappingConfig } = require('../../util'); -const BASE_ENDPOINT = 'https://googleads.googleapis.com/v15/customers'; +const BASE_ENDPOINT = 'https://googleads.googleapis.com/v16/customers'; const CONFIG_CATEGORIES = { AUDIENCE_LIST: { type: 'audienceList', name: 'offlineDataJobs' }, ADDRESSINFO: { type: 'addressInfo', name: 'addressInfo' }, @@ -16,6 +16,11 @@ const TYPEOFLIST = Object.freeze({ mobileDeviceID: 'mobileId', }); +const consentConfigMap = { + personalizationConsent: 'adPersonalization', + userDataConsent: 'adUserData', +}; + module.exports = { BASE_ENDPOINT, TYPEOFLIST, @@ -23,4 +28,6 @@ module.exports = { hashAttributes, offlineDataJobsMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.AUDIENCE_LIST.name], addressInfoMapping: MAPPING_CONFIG[CONFIG_CATEGORIES.ADDRESSINFO.name], + consentConfigMap, + destType: 'google_adwords_remarketing_lists', }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js b/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js index 3045c1713f..82fb62b74e 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js @@ -1,12 +1,14 @@ const { NetworkError } = require('@rudderstack/integrations-lib'); -const { httpSend, prepareProxyRequest } = require('../../../adapters/network'); +const { prepareProxyRequest, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index'); +const logger = require('../../../logger'); const { processAxiosResponse, getDynamicErrorType, } = require('../../../adapters/utils/networkUtils'); const tags = require('../../util/tags'); +const { destType } = require('./config'); /** * This function helps to create a offlineUserDataJobs * @param endpoint @@ -18,7 +20,7 @@ const tags = require('../../util/tags'); * ref: https://developers.google.com/google-ads/api/rest/reference/rest/v15/CustomerMatchUserListMetadata */ -const createJob = async (endpoint, headers, method, params) => { +const createJob = async ({ endpoint, headers, method, params, metadata }) => { const jobCreatingUrl = `${endpoint}:create`; const customerMatchUserListMetadata = { userList: `customers/${params.customerId}/userLists/${params.listId}`, @@ -37,14 +39,31 @@ const createJob = async (endpoint, headers, method, params) => { headers, method, }; - const response = await httpSend(jobCreatingRequest, { - destType: 'google_adwords_remarketing_lists', - feature: 'proxy', - endpointPath: '/customers/create', - requestMethod: 'POST', - module: 'dataDelivery', + logger.requestLog(`[${destType.toUpperCase()}] job creation request`, { + metadata, + requestDetails: { + url: jobCreatingRequest.url, + body: jobCreatingRequest.data, + method: jobCreatingRequest.method, + }, }); - return response; + const { httpResponse, processedResponse } = await handleHttpRequest( + 'constructor', + jobCreatingRequest, + { + destType: 'google_adwords_remarketing_lists', + feature: 'proxy', + endpointPath: '/customers/create', + requestMethod: 'POST', + module: 'dataDelivery', + metadata, + }, + ); + logger.responseLog(`[${destType.toUpperCase()}] job creation response`, { + metadata, + responseDetails: processedResponse, + }); + return httpResponse; }; /** * This function helps to put user details in a offlineUserDataJobs @@ -55,7 +74,7 @@ const createJob = async (endpoint, headers, method, params) => { * @param body */ -const addUserToJob = async (endpoint, headers, method, jobId, body) => { +const addUserToJob = async ({ endpoint, headers, method, jobId, body, metadata }) => { const jobAddingUrl = `${endpoint}/${jobId}:addOperations`; const secondRequest = { url: jobAddingUrl, @@ -63,12 +82,29 @@ const addUserToJob = async (endpoint, headers, method, jobId, body) => { headers, method, }; - const response = await httpSend(secondRequest, { - destType: 'google_adwords_remarketing_lists', - feature: 'proxy', - endpointPath: '/addOperations', - requestMethod: 'POST', - module: 'dataDelivery', + logger.requestLog(`[${destType.toUpperCase()}] add user to job request`, { + metadata, + requestDetails: { + url: secondRequest.url, + body: secondRequest.data, + method: secondRequest.method, + }, + }); + const { httpResponse: response, processedResponse } = await handleHttpRequest( + 'constructor', + secondRequest, + { + destType: 'google_adwords_remarketing_lists', + feature: 'proxy', + endpointPath: '/addOperations', + requestMethod: 'POST', + module: 'dataDelivery', + metadata, + }, + ); + logger.responseLog(`[${destType.toUpperCase()}] add user to job response`, { + metadata, + responseDetails: processedResponse, }); return response; }; @@ -80,19 +116,35 @@ const addUserToJob = async (endpoint, headers, method, jobId, body) => { * @param method * @param jobId */ -const runTheJob = async (endpoint, headers, method, jobId) => { +const runTheJob = async ({ endpoint, headers, method, jobId, metadata }) => { const jobRunningUrl = `${endpoint}/${jobId}:run`; const thirdRequest = { url: jobRunningUrl, headers, method, }; - const response = await httpSend(thirdRequest, { - destType: 'google_adwords_remarketing_lists', - feature: 'proxy', - endpointPath: '/run', - requestMethod: 'POST', - module: 'dataDelivery', + logger.requestLog(`[${destType.toUpperCase()}] run job request`, { + metadata, + requestDetails: { + url: thirdRequest.url, + body: thirdRequest.data, + method: thirdRequest.method, + }, + }); + const { httpResponse: response, processedResponse } = await handleHttpRequest( + 'constructor', + thirdRequest, + { + destType: 'google_adwords_remarketing_lists', + feature: 'proxy', + endpointPath: '/run', + requestMethod: 'POST', + module: 'dataDelivery', + }, + ); + logger.responseLog(`[${destType.toUpperCase()}] run job response`, { + metadata, + responseDetails: processedResponse, }); return response; }; @@ -104,12 +156,12 @@ const runTheJob = async (endpoint, headers, method, jobId) => { * @returns */ const gaAudienceProxyRequest = async (request) => { - const { body, method, params, endpoint } = request; + const { body, method, params, endpoint, metadata } = request; const { headers } = request; // step1: offlineUserDataJobs creation - const firstResponse = await createJob(endpoint, headers, method, params); + const firstResponse = await createJob({ endpoint, headers, method, params, metadata }); if (!firstResponse.success && !isHttpStatusSuccess(firstResponse?.response?.status)) { return firstResponse; } @@ -126,7 +178,7 @@ const gaAudienceProxyRequest = async (request) => { if (firstResponse?.response?.data?.resourceName) // eslint-disable-next-line prefer-destructuring jobId = firstResponse.response.data.resourceName.split('/')[3]; - const secondResponse = await addUserToJob(endpoint, headers, method, jobId, body); + const secondResponse = await addUserToJob({ endpoint, headers, method, jobId, body, metadata }); if (!secondResponse.success && !isHttpStatusSuccess(secondResponse?.response?.status)) { return secondResponse; } @@ -139,7 +191,7 @@ const gaAudienceProxyRequest = async (request) => { } // step3: running the job - const thirdResponse = await runTheJob(endpoint, headers, method, jobId); + const thirdResponse = await runTheJob({ endpoint, headers, method, jobId, metadata }); return thirdResponse; }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js b/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js new file mode 100644 index 0000000000..18e7f96821 --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/recordTransform.js @@ -0,0 +1,144 @@ +/* eslint-disable no-const-assign */ +const lodash = require('lodash'); +const { InstrumentationError } = require('@rudderstack/integrations-lib'); +const { + getValueFromMessage, + getAccessToken, + constructPayload, + returnArrayOfSubarrays, + getSuccessRespEvents, +} = require('../../util'); +const { populateConsentFromConfig } = require('../../util/googleUtils'); +const { populateIdentifiers, responseBuilder } = require('./util'); +const { getErrorResponse, createFinalResponse } = require('../../util/recordUtils'); +const { offlineDataJobsMapping, consentConfigMap } = require('./config'); + +const processRecordEventArray = ( + records, + message, + destination, + accessToken, + developerToken, + operationType, +) => { + let outputPayloads = {}; + // ** only send it if identifier > 0 + + const fieldsArray = []; + const metadata = []; + records.forEach((record) => { + fieldsArray.push(record.message.fields); + metadata.push(record.metadata); + }); + + const userIdentifiersList = populateIdentifiers(fieldsArray, destination); + + const outputPayload = constructPayload(message, offlineDataJobsMapping); + outputPayload.operations = []; + // breaking the userIdentiFier array in chunks of 20 + const userIdentifierChunks = returnArrayOfSubarrays(userIdentifiersList, 20); + // putting each chunk in different create/remove operations + switch (operationType) { + case 'add': + // for add operation + userIdentifierChunks.forEach((element) => { + const operations = { + create: {}, + }; + operations.create.userIdentifiers = element; + outputPayload.operations.push(operations); + }); + outputPayloads = { ...outputPayloads, create: outputPayload }; + break; + case 'remove': + // for remove operation + userIdentifierChunks.forEach((element) => { + const operations = { + remove: {}, + }; + operations.remove.userIdentifiers = element; + outputPayload.operations.push(operations); + }); + outputPayloads = { ...outputPayloads, remove: outputPayload }; + break; + default: + } + + const toSendEvents = []; + Object.values(outputPayloads).forEach((data) => { + const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); + toSendEvents.push( + responseBuilder(accessToken, developerToken, data, destination, message, consentObj), + ); + }); + + const successResponse = getSuccessRespEvents(toSendEvents, metadata, destination, true); + + return successResponse; +}; + +async function processRecordInputs(groupedRecordInputs) { + const { destination, message, metadata } = groupedRecordInputs[0]; + const accessToken = getAccessToken(metadata, 'accessToken'); + const developerToken = getValueFromMessage(metadata, 'secret.developer_token'); + + const groupedRecordsByAction = lodash.groupBy(groupedRecordInputs, (record) => + record.message.action?.toLowerCase(), + ); + + let insertResponse; + let deleteResponse; + let updateResponse; + + if (groupedRecordsByAction.delete) { + deleteResponse = processRecordEventArray( + groupedRecordsByAction.delete, + message, + destination, + accessToken, + developerToken, + 'remove', + ); + } + + if (groupedRecordsByAction.insert) { + insertResponse = processRecordEventArray( + groupedRecordsByAction.insert, + message, + destination, + accessToken, + developerToken, + 'add', + ); + } + + if (groupedRecordsByAction.update) { + updateResponse = processRecordEventArray( + groupedRecordsByAction.update, + message, + destination, + accessToken, + developerToken, + 'add', + ); + } + + const errorResponse = getErrorResponse(groupedRecordsByAction); + const finalResponse = createFinalResponse( + deleteResponse, + insertResponse, + updateResponse, + errorResponse, + ); + if (finalResponse.length === 0) { + throw new InstrumentationError( + 'Missing valid parameters, unable to generate transformed payload', + ); + } + + return finalResponse; +} + +module.exports = { + processRecordInputs, +}; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/transform.js b/src/v0/destinations/google_adwords_remarketing_lists/transform.js index 9ab415346a..d879a39c63 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/transform.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/transform.js @@ -1,137 +1,29 @@ -const sha256 = require('sha256'); -const get = require('get-value'); +const lodash = require('lodash'); const { InstrumentationError, ConfigurationError } = require('@rudderstack/integrations-lib'); const logger = require('../../../logger'); const { - isDefinedAndNotNullAndNotEmpty, returnArrayOfSubarrays, constructPayload, - defaultRequestConfig, getValueFromMessage, - removeUndefinedAndNullValues, - removeHyphens, simpleProcessRouterDest, - getDestinationExternalIDInfoForRetl, getAccessToken, } = require('../../util'); -const { populateConsentForGoogleDestinations } = require('../../util/googleUtils'); +const { populateConsentFromConfig } = require('../../util/googleUtils'); +const { offlineDataJobsMapping, consentConfigMap } = require('./config'); +const { processRecordInputs } = require('./recordTransform'); +const { populateIdentifiers, responseBuilder } = require('./util'); -const { - offlineDataJobsMapping, - addressInfoMapping, - BASE_ENDPOINT, - attributeMapping, - hashAttributes, - TYPEOFLIST, -} = require('./config'); -const { JSON_MIME_TYPE } = require('../../util/constant'); -const { MappedToDestinationKey } = require('../../../constants'); - -const hashEncrypt = (object) => { - Object.keys(object).forEach((key) => { - if (hashAttributes.includes(key) && object[key]) { - // eslint-disable-next-line no-param-reassign - object[key] = sha256(object[key]); +function extraKeysPresent(dictionary, keyList) { + // eslint-disable-next-line no-restricted-syntax + for (const key in dictionary) { + if (!keyList.includes(key)) { + return true; } - }); -}; - -/** - * This function is used for building the response. It create a default rudder response - * and populate headers, params and body.JSON - * @param {*} metadata - * @param {*} body - * @param {*} param2 - * @returns - */ -const responseBuilder = (metadata, body, { Config }, message, consentBlock) => { - const payload = body; - const response = defaultRequestConfig(); - const filteredCustomerId = removeHyphens(Config.customerId); - response.endpoint = `${BASE_ENDPOINT}/${filteredCustomerId}/offlineUserDataJobs`; - response.body.JSON = removeUndefinedAndNullValues(payload); - const accessToken = getAccessToken(metadata, 'access_token'); - let operationAudienceId = Config.audienceId || Config.listId; - const mappedToDestination = get(message, MappedToDestinationKey); - if (!operationAudienceId && mappedToDestination) { - const { objectType } = getDestinationExternalIDInfoForRetl( - message, - 'GOOGLE_ADWORDS_REMARKETING_LISTS', - ); - operationAudienceId = objectType; } - if (!isDefinedAndNotNullAndNotEmpty(operationAudienceId)) { - throw new ConfigurationError('List ID is a mandatory field'); - } - response.params = { - listId: operationAudienceId, - customerId: filteredCustomerId, - consent: consentBlock, - }; - response.headers = { - Authorization: `Bearer ${accessToken}`, - 'Content-Type': JSON_MIME_TYPE, - 'developer-token': getValueFromMessage(metadata, 'secret.developer_token'), - }; - if (Config.subAccount) - if (Config.loginCustomerId) { - const filteredLoginCustomerId = removeHyphens(Config.loginCustomerId); - response.headers['login-customer-id'] = filteredLoginCustomerId; - } else throw new ConfigurationError(`loginCustomerId is required as subAccount is true.`); - return response; -}; -/** - * This function helps creates an array with proper mapping for userIdentiFier. - * Logics: Here we are creating an array with all the attributes provided in the add/remove array - * inside listData. - * @param {rudder event message properties listData add} attributeArray - * @param {rudder event destination} Config - * @returns - */ + return false; +} -const populateIdentifiers = (attributeArray, { Config }) => { - const userIdentifier = []; - const { typeOfList } = Config; - const { isHashRequired, userSchema } = Config; - let attribute; - if (TYPEOFLIST[typeOfList]) { - attribute = TYPEOFLIST[typeOfList]; - } else { - attribute = userSchema; - } - if (isDefinedAndNotNullAndNotEmpty(attributeArray)) { - // traversing through every element in the add array - attributeArray.forEach((element, index) => { - if (isHashRequired) { - hashEncrypt(element); - } - // checking if the attribute is an array or not for generic type list - if (!Array.isArray(attribute)) { - if (element[attribute]) { - userIdentifier.push({ [attribute]: element[attribute] }); - } else { - logger.info(` ${attribute} is not present in index:`, index); - } - } else { - attribute.forEach((attributeElement, index2) => { - if (attributeElement === 'addressInfo') { - const addressInfo = constructPayload(element, addressInfoMapping); - // checking if addressInfo object is empty or not. - if (isDefinedAndNotNullAndNotEmpty(addressInfo)) userIdentifier.push({ addressInfo }); - } else if (element[`${attributeElement}`]) { - userIdentifier.push({ - [`${attributeMapping[attributeElement]}`]: element[`${attributeElement}`], - }); - } else { - logger.info(` ${attribute[index2]} is not present in index:`, index); - } - }); - } - }); - } - return userIdentifier; -}; /** * This function helps to create different operations by breaking the * userIdentiFier Array in chunks of 20. @@ -142,7 +34,6 @@ const populateIdentifiers = (attributeArray, { Config }) => { * @param {rudder event destination} destination * @returns */ - const createPayload = (message, destination) => { const { listData } = message.properties; const properties = ['add', 'remove']; @@ -217,9 +108,14 @@ const processEvent = async (metadata, message, destination) => { ); } + const accessToken = getAccessToken(metadata, 'accessToken'); + const developerToken = getValueFromMessage(metadata, 'secret.developer_token'); + Object.values(createdPayload).forEach((data) => { - const consentObj = populateConsentForGoogleDestinations(destination.Config); - response.push(responseBuilder(metadata, data, destination, message, consentObj)); + const consentObj = populateConsentFromConfig(destination.Config, consentConfigMap); + response.push( + responseBuilder(accessToken, developerToken, data, destination, message, consentObj), + ); }); return response; } @@ -230,7 +126,29 @@ const processEvent = async (metadata, message, destination) => { const process = async (event) => processEvent(event.metadata, event.message, event.destination); const processRouterDest = async (inputs, reqMetadata) => { - const respList = await simpleProcessRouterDest(inputs, process, reqMetadata); + const respList = []; + const groupedInputs = lodash.groupBy(inputs, (input) => input.message.type?.toLowerCase()); + let transformedRecordEvent = []; + let transformedAudienceEvent = []; + + const eventTypes = ['record', 'audiencelist']; + if (extraKeysPresent(groupedInputs, eventTypes)) { + throw new ConfigurationError('unsupported events present in the event'); + } + + if (groupedInputs.record) { + transformedRecordEvent = await processRecordInputs(groupedInputs.record, reqMetadata); + } + + if (groupedInputs.audiencelist) { + transformedAudienceEvent = await simpleProcessRouterDest( + groupedInputs.audiencelist, + process, + reqMetadata, + ); + } + + respList.push(...transformedRecordEvent, ...transformedAudienceEvent); return respList; }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/util.js b/src/v0/destinations/google_adwords_remarketing_lists/util.js new file mode 100644 index 0000000000..3e04dd8f6f --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/util.js @@ -0,0 +1,122 @@ +const get = require('get-value'); +const sha256 = require('sha256'); +const { ConfigurationError } = require('@rudderstack/integrations-lib'); +const { + isDefinedAndNotNullAndNotEmpty, + constructPayload, + defaultRequestConfig, + removeHyphens, + removeUndefinedAndNullValues, + getDestinationExternalIDInfoForRetl, +} = require('../../util'); +const logger = require('../../../logger'); +const { MappedToDestinationKey } = require('../../../constants'); +const { JSON_MIME_TYPE } = require('../../util/constant'); +const { + addressInfoMapping, + attributeMapping, + TYPEOFLIST, + BASE_ENDPOINT, + hashAttributes, +} = require('./config'); + +const hashEncrypt = (object) => { + Object.keys(object).forEach((key) => { + if (hashAttributes.includes(key) && object[key]) { + // eslint-disable-next-line no-param-reassign + object[key] = sha256(object[key]); + } + }); +}; + +const responseBuilder = (accessToken, developerToken, body, { Config }, message, consentBlock) => { + const payload = body; + const response = defaultRequestConfig(); + const filteredCustomerId = removeHyphens(Config.customerId); + response.endpoint = `${BASE_ENDPOINT}/${filteredCustomerId}/offlineUserDataJobs`; + response.body.JSON = removeUndefinedAndNullValues(payload); + let operationAudienceId = Config.audienceId || Config.listId; + const mappedToDestination = get(message, MappedToDestinationKey); + if (!operationAudienceId && mappedToDestination) { + const { objectType } = getDestinationExternalIDInfoForRetl( + message, + 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ); + operationAudienceId = objectType; + } + if (!isDefinedAndNotNullAndNotEmpty(operationAudienceId)) { + throw new ConfigurationError('List ID is a mandatory field'); + } + response.params = { + listId: operationAudienceId, + customerId: filteredCustomerId, + consent: consentBlock, + }; + response.headers = { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': JSON_MIME_TYPE, + 'developer-token': developerToken, + }; + if (Config.subAccount) + if (Config.loginCustomerId) { + const filteredLoginCustomerId = removeHyphens(Config.loginCustomerId); + response.headers['login-customer-id'] = filteredLoginCustomerId; + } else throw new ConfigurationError(`loginCustomerId is required as subAccount is true.`); + return response; +}; + +/** + * This function helps creates an array with proper mapping for userIdentiFier. + * Logics: Here we are creating an array with all the attributes provided in the add/remove array + * inside listData. + * @param {rudder event message properties listData add} attributeArray + * @param {rudder event destination} Config + * @returns + */ +const populateIdentifiers = (attributeArray, { Config }) => { + const userIdentifier = []; + const { typeOfList } = Config; + const { isHashRequired, userSchema } = Config; + let attribute; + if (TYPEOFLIST[typeOfList]) { + attribute = TYPEOFLIST[typeOfList]; + } else { + attribute = userSchema; + } + if (isDefinedAndNotNullAndNotEmpty(attributeArray)) { + // traversing through every element in the add array + attributeArray.forEach((element, index) => { + if (isHashRequired) { + hashEncrypt(element); + } + // checking if the attribute is an array or not for generic type list + if (!Array.isArray(attribute)) { + if (element[attribute]) { + userIdentifier.push({ [attribute]: element[attribute] }); + } else { + logger.info(` ${attribute} is not present in index:`, index); + } + } else { + attribute.forEach((attributeElement, index2) => { + if (attributeElement === 'addressInfo') { + const addressInfo = constructPayload(element, addressInfoMapping); + // checking if addressInfo object is empty or not. + if (isDefinedAndNotNullAndNotEmpty(addressInfo)) userIdentifier.push({ addressInfo }); + } else if (element[`${attributeElement}`]) { + userIdentifier.push({ + [`${attributeMapping[attributeElement]}`]: element[`${attributeElement}`], + }); + } else { + logger.info(` ${attribute[index2]} is not present in index:`, index); + } + }); + } + }); + } + return userIdentifier; +}; + +module.exports = { + populateIdentifiers, + responseBuilder, +}; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/util.test.js b/src/v0/destinations/google_adwords_remarketing_lists/util.test.js new file mode 100644 index 0000000000..e9fe90c317 --- /dev/null +++ b/src/v0/destinations/google_adwords_remarketing_lists/util.test.js @@ -0,0 +1,203 @@ +const { populateIdentifiers, responseBuilder } = require('./util'); + +const accessToken = 'abcd1234'; +const developerToken = 'ijkl9101'; +const body = { + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], +}; +const baseDestination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, +}; +const consentBlock = { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', +}; +const message = { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + phone: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + firstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + lastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + country: 'US', + postalCode: '1245', + }, + type: 'record', +}; +const expectedResponse = { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl9101', + }, + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + }, + body: { + JSON: { + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, +}; +const attributeArray = [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, +]; + +const hashedArray = [ + { + hashedEmail: 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, +]; + +describe('GARL utils test', () => { + describe('responseBuilder function tests', () => { + it('Should return correct response for given payload', () => { + const response = responseBuilder( + accessToken, + developerToken, + body, + baseDestination, + message, + consentBlock, + ); + expect(response).toEqual(expectedResponse); + }); + + it('Should throw error if subaccount is true and loginCustomerId is not defined', () => { + try { + const destination2 = Object.create(baseDestination); + destination2.Config.subAccount = true; + destination2.Config.loginCustomerId = ''; + const response = responseBuilder( + accessToken, + developerToken, + body, + destination2, + message, + consentBlock, + ); + expect(response).toEqual(); + } catch (error) { + expect(error.message).toEqual(`loginCustomerId is required as subAccount is true.`); + } + }); + + it('Should throw error if operationAudienceId is not defined', () => { + try { + const destination1 = Object.create(baseDestination); + destination1.Config.listId = ''; + const response = responseBuilder( + accessToken, + developerToken, + body, + destination1, + message, + consentBlock, + ); + expect(response).toEqual(); + } catch (error) { + expect(error.message).toEqual(`List ID is a mandatory field`); + } + }); + }); + + describe('populateIdentifiers function tests', () => { + it('Should hash and return identifiers for a given list of attributes', () => { + const identifier = populateIdentifiers(attributeArray, baseDestination); + expect(identifier).toEqual(hashedArray); + }); + }); +}); diff --git a/src/v0/destinations/hs/HSTransform-v2.js b/src/v0/destinations/hs/HSTransform-v2.js index 2acdd82152..d2b26f1ab8 100644 --- a/src/v0/destinations/hs/HSTransform-v2.js +++ b/src/v0/destinations/hs/HSTransform-v2.js @@ -12,7 +12,6 @@ const { defaultPatchRequestConfig, getFieldValueFromMessage, getSuccessRespEvents, - addExternalIdToTraits, defaultBatchRequestConfig, removeUndefinedAndNullValues, getDestinationExternalID, @@ -42,6 +41,7 @@ const { getEventAndPropertiesFromConfig, getHsSearchId, populateTraits, + addExternalIdToHSTraits, } = require('./util'); const { JSON_MIME_TYPE } = require('../../util/constant'); @@ -110,11 +110,11 @@ const processIdentify = async (message, destination, propertyMap) => { GENERIC_TRUE_VALUES.includes(mappedToDestination.toString()) && operation ) { - addExternalIdToTraits(message); if (!objectType) { throw new InstrumentationError('objectType not found'); } if (operation === 'createObject') { + addExternalIdToHSTraits(message); endpoint = CRM_CREATE_UPDATE_ALL_OBJECTS.replace(':objectType', objectType); } else if (operation === 'updateObject' && getHsSearchId(message)) { const { hsSearchId } = getHsSearchId(message); diff --git a/src/v0/destinations/hs/config.js b/src/v0/destinations/hs/config.js index fb9790f0e5..67ad3b5bed 100644 --- a/src/v0/destinations/hs/config.js +++ b/src/v0/destinations/hs/config.js @@ -84,6 +84,9 @@ const RETL_SOURCE = 'rETL'; const mappingConfig = getMappingConfig(ConfigCategory, __dirname); const hsCommonConfigJson = mappingConfig[ConfigCategory.COMMON.name]; +const primaryToSecondaryFields = { + email: 'hs_additional_emails', +}; module.exports = { BASE_ENDPOINT, CONTACT_PROPERTY_MAP_ENDPOINT, @@ -112,5 +115,6 @@ module.exports = { RETL_SOURCE, RETL_CREATE_ASSOCIATION_OPERATION, MAX_CONTACTS_PER_REQUEST, + primaryToSecondaryFields, DESTINATION: 'HS', }; diff --git a/src/v0/destinations/hs/util.js b/src/v0/destinations/hs/util.js index 359c93dc1a..b30207fe15 100644 --- a/src/v0/destinations/hs/util.js +++ b/src/v0/destinations/hs/util.js @@ -1,5 +1,6 @@ /* eslint-disable no-await-in-loop */ const lodash = require('lodash'); +const set = require('set-value'); const get = require('get-value'); const { NetworkInstrumentationError, @@ -20,6 +21,7 @@ const { getDestinationExternalIDInfoForRetl, getValueFromMessage, isNull, + validateEventName, } = require('../../util'); const { CONTACT_PROPERTY_MAP_ENDPOINT, @@ -27,6 +29,7 @@ const { IDENTIFY_CRM_SEARCH_ALL_OBJECTS, SEARCH_LIMIT_VALUE, hsCommonConfigJson, + primaryToSecondaryFields, DESTINATION, MAX_CONTACTS_PER_REQUEST, } = require('./config'); @@ -435,6 +438,7 @@ const getEventAndPropertiesFromConfig = (message, destination, payload) => { if (!hubspotEvents) { throw new InstrumentationError('Event and property mappings are required for track call'); } + validateEventName(event); event = event.trim().toLowerCase(); let eventName; let eventProperties; @@ -574,16 +578,30 @@ const performHubSpotSearch = async ( checkAfter = after; // assigning to the new value if no after we assign it to 0 and no more calls will take place const results = processedResponse.response?.results; + const extraProp = primaryToSecondaryFields[identifierType]; if (results) { searchResults.push( - ...results.map((result) => ({ - id: result.id, - property: result.properties[identifierType], - })), + ...results.map((result) => { + const contact = { + id: result.id, + property: result.properties[identifierType], + }; + // Following maps the extra property to the contact object which + // help us to know if the contact was found using secondary property + if (extraProp) { + contact[extraProp] = result.properties?.[extraProp]; + } + return contact; + }), ); } } - + /* + searchResults = { + id: 'existing_contact_id', + property: 'existing_contact_email', // when email is identifier + hs_additional_emails: ['secondary_email'] // when email is identifier + } */ return searchResults; }; @@ -610,7 +628,25 @@ const getRequestData = (identifierType, chunk) => { limit: SEARCH_LIMIT_VALUE, after: 0, }; - + /* In case of email as identifier we add a filter for hs_additional_emails field + * and append hs_additional_emails to properties list + * We are doing this because there might be emails exisitng as hs_additional_emails for some conatct but + * will not come up in search API until we search with hs_additional_emails as well. + * Not doing this resulted in erro 409 Duplicate records found + */ + const secondaryProp = primaryToSecondaryFields[identifierType]; + if (secondaryProp) { + requestData.filterGroups.push({ + filters: [ + { + propertyName: secondaryProp, + values: chunk, + operator: 'IN', + }, + ], + }); + requestData.properties.push(secondaryProp); + } return requestData; }; @@ -621,7 +657,7 @@ const getRequestData = (identifierType, chunk) => { */ const getExistingContactsData = async (inputs, destination) => { const { Config } = destination; - const updateHubspotIds = []; + const hsIdsToBeUpdated = []; const firstMessage = inputs[0].message; if (!firstMessage) { @@ -649,13 +685,19 @@ const getExistingContactsData = async (inputs, destination) => { destination, ); if (searchResults.length > 0) { - updateHubspotIds.push(...searchResults); + hsIdsToBeUpdated.push(...searchResults); } } - return updateHubspotIds; + return hsIdsToBeUpdated; }; - -const setHsSearchId = (input, id) => { +/** + * This functions sets HsSearchId in the externalId array + * @param {*} input -> Input message + * @param {*} id -> Id to be added + * @param {*} useSecondaryProp -> Let us know if that id was found using secondary property and not primnary + * @returns + */ +const setHsSearchId = (input, id, useSecondaryProp = false) => { const { message } = input; const resultExternalId = []; const externalIdArray = message.context?.externalId; @@ -666,6 +708,11 @@ const setHsSearchId = (input, id) => { if (type.includes(DESTINATION)) { extIdObjParam.hsSearchId = id; } + if (useSecondaryProp) { + // we are using it so that when final payload is made + // then primary key shouldn't be overidden + extIdObjParam.useSecondaryObject = useSecondaryProp; + } resultExternalId.push(extIdObjParam); }); } @@ -678,20 +725,24 @@ const setHsSearchId = (input, id) => { * We do search for all the objects before router transform and assign the type (create/update) * accordingly to context.hubspotOperation * + * For email as primary key we use `hs_additional_emails` as well property to search existing contacts * */ const splitEventsForCreateUpdate = async (inputs, destination) => { // get all the id and properties of already existing objects needed for update. - const updateHubspotIds = await getExistingContactsData(inputs, destination); + const hsIdsToBeUpdated = await getExistingContactsData(inputs, destination); const resultInput = inputs.map((input) => { const { message } = input; const inputParam = input; - const { destinationExternalId } = getDestinationExternalIDInfoForRetl(message, DESTINATION); + const { destinationExternalId, identifierType } = getDestinationExternalIDInfoForRetl( + message, + DESTINATION, + ); - const filteredInfo = updateHubspotIds.filter( + const filteredInfo = hsIdsToBeUpdated.filter( (update) => - update.property.toString().toLowerCase() === destinationExternalId.toString().toLowerCase(), + update.property.toString().toLowerCase() === destinationExternalId.toString().toLowerCase(), // second condition is for secondary property for identifier type ); if (filteredInfo.length > 0) { @@ -699,6 +750,33 @@ const splitEventsForCreateUpdate = async (inputs, destination) => { inputParam.message.context.hubspotOperation = 'updateObject'; return inputParam; } + const secondaryProp = primaryToSecondaryFields[identifierType]; + if (secondaryProp) { + /* second condition is for secondary property for identifier type + For example: + update[secondaryProp] = "abc@e.com;cd@e.com;k@w.com" + destinationExternalId = "cd@e.com" + So we are splitting all the emails in update[secondaryProp] into an array using ';' + and then checking if array includes destinationExternalId + */ + const filteredInfoForSecondaryProp = hsIdsToBeUpdated.filter((update) => + update[secondaryProp] + ?.toString() + .toLowerCase() + .split(';') + .includes(destinationExternalId.toString().toLowerCase()), + ); + if (filteredInfoForSecondaryProp.length > 0) { + inputParam.message.context.externalId = setHsSearchId( + input, + filteredInfoForSecondaryProp[0].id, + true, + ); + inputParam.message.context.hubspotOperation = 'updateObject'; + return inputParam; + } + } + // if not found in the existing contacts, then it's a new contact inputParam.message.context.hubspotOperation = 'createObject'; return inputParam; }); @@ -746,8 +824,22 @@ const populateTraits = async (propertyMap, traits, destination) => { return populatedTraits; }; +const addExternalIdToHSTraits = (message) => { + const externalIdObj = message.context?.externalId?.[0]; + if (externalIdObj.useSecondaryObject) { + /* this condition help us to NOT override the primary key value with the secondary key value + example: + for `email` as primary key and `hs_additonal_emails` as secondary key we don't want to override `email` with `hs_additional_emails`. + neither we want to map anything for `hs_additional_emails` as this property can not be set + */ + return; + } + set(getFieldValueFromMessage(message, 'traits'), externalIdObj.identifierType, externalIdObj.id); +}; + module.exports = { validateDestinationConfig, + addExternalIdToHSTraits, formatKey, fetchFinalSetOfTraits, getProperties, diff --git a/src/v0/destinations/hs/util.test.js b/src/v0/destinations/hs/util.test.js index 30e89d3aee..ea2e10dc3d 100644 --- a/src/v0/destinations/hs/util.test.js +++ b/src/v0/destinations/hs/util.test.js @@ -4,6 +4,7 @@ const { validatePayloadDataTypes, getObjectAndIdentifierType, } = require('./util'); +const { primaryToSecondaryFields } = require('./config'); const propertyMap = { firstName: 'string', @@ -205,7 +206,7 @@ describe('extractUniqueValues utility test cases', () => { describe('getRequestDataAndRequestOptions utility test cases', () => { it('Should return an object with requestData and requestOptions', () => { const identifierType = 'email'; - const chunk = 'test1@gmail.com'; + const chunk = ['test1@gmail.com']; const accessToken = 'dummyAccessToken'; const expectedRequestData = { @@ -219,8 +220,17 @@ describe('getRequestDataAndRequestOptions utility test cases', () => { }, ], }, + { + filters: [ + { + propertyName: primaryToSecondaryFields[identifierType], + values: chunk, + operator: 'IN', + }, + ], + }, ], - properties: [identifierType], + properties: [identifierType, primaryToSecondaryFields[identifierType]], limit: 100, after: 0, }; diff --git a/src/v0/destinations/impact/data/ImpactConversionConfig.json b/src/v0/destinations/impact/data/ImpactConversionConfig.json index 7ae0cc72eb..a898e6a98c 100644 --- a/src/v0/destinations/impact/data/ImpactConversionConfig.json +++ b/src/v0/destinations/impact/data/ImpactConversionConfig.json @@ -120,7 +120,7 @@ }, { "destKey": "CustomerEmail", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/impact/data/ImpactPageLoadConfig.json b/src/v0/destinations/impact/data/ImpactPageLoadConfig.json index 0ca0a26e69..700557d3e0 100644 --- a/src/v0/destinations/impact/data/ImpactPageLoadConfig.json +++ b/src/v0/destinations/impact/data/ImpactPageLoadConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "CustomerEmail", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/impact/transform.js b/src/v0/destinations/impact/transform.js index 2eefdf7992..729f988938 100644 --- a/src/v0/destinations/impact/transform.js +++ b/src/v0/destinations/impact/transform.js @@ -59,7 +59,7 @@ const buildPageLoadPayload = (message, campaignId, impactAppId, enableEmailHashi let payload = constructPayload(message, MAPPING_CONFIG[CONFIG_CATEGORIES.PAGELOAD.name]); if (isDefinedAndNotNull(payload.CustomerEmail)) { payload.CustomerEmail = enableEmailHashing - ? sha1(payload?.CustomerEmail) + ? sha1(payload?.CustomerEmail.trim()) : payload?.CustomerEmail; } payload.CampaignId = campaignId; @@ -155,7 +155,7 @@ const processTrackEvent = (message, Config) => { payload.ImpactAppId = impactAppId; if (isDefinedAndNotNull(payload.CustomerEmail)) { payload.CustomerEmail = enableEmailHashing - ? sha1(payload?.CustomerEmail) + ? sha1(payload?.CustomerEmail.trim()) : payload?.CustomerEmail; } diff --git a/src/v0/destinations/impact/util.js b/src/v0/destinations/impact/util.js index b8c6e92e76..8fb43a8df6 100644 --- a/src/v0/destinations/impact/util.js +++ b/src/v0/destinations/impact/util.js @@ -78,6 +78,7 @@ const populateProductProperties = (productsMapping, properties) => { const productProperties = {}; if (products && Array.isArray(products)) { products.forEach((item, index) => { + // Following product properties have a default mapping as well in config.js as itemMapping productProperties[getPropertyName('ItemBrand', index + 1)] = item[getProductsMapping(productsMapping, 'ItemBrand')]; productProperties[getPropertyName('ItemCategory', index + 1)] = @@ -92,8 +93,16 @@ const populateProductProperties = (productsMapping, properties) => { item[getProductsMapping(productsMapping, 'ItemQuantity')]; productProperties[getPropertyName('ItemSku', index + 1)] = item[getProductsMapping(productsMapping, 'ItemSku')]; + + // Following product properties are build from configuration in RudderStack dashboard + if (productsMapping && Array.isArray(productsMapping)) { + productsMapping.forEach((mapping) => { + productProperties[getPropertyName(mapping.to, index + 1)] = item[mapping.from]; + }); + } }); } else { + // Not providing product level mapping here as following are fetched from properties level const index = 1; productProperties[getPropertyName('ItemBrand', index)] = brand; productProperties[getPropertyName('ItemCategory', index)] = category; diff --git a/src/v0/destinations/impact/utils.test.js b/src/v0/destinations/impact/utils.test.js new file mode 100644 index 0000000000..17f90bbc5d --- /dev/null +++ b/src/v0/destinations/impact/utils.test.js @@ -0,0 +1,134 @@ +const { populateProductProperties } = require('./util'); +// Generated by CodiumAI + +describe('populateProductProperties', () => { + // Given a valid productsMapping and properties, it should return product properties with default mappings and configured mappings from RudderStack dashboard + it('should return product properties with default and configured mappings', () => { + // Arrange + const productsMapping = [ + { to: 'ItemBrand', from: 'Brand' }, + { to: 'ItemCategory', from: 'Category' }, + { to: 'ItemName', from: 'Name' }, + { to: 'ItemPrice', from: 'Price' }, + { to: 'ItemPromoCode', from: 'PromoCode' }, + { to: 'ItemQuantity', from: 'Quantity' }, + { to: 'ItemSku', from: 'Sku' }, + { to: 'dummyRHS', from: 'dummyLHS' }, + ]; + const properties = { + products: [ + { + Brand: 'Brand1', + Category: 'Category1', + Name: 'Name1', + Price: 10, + PromoCode: 'PromoCode1', + Quantity: 1, + Sku: 'Sku1', + dummyLHS: 'DummyValue', + }, + { + Brand: 'Brand2', + Category: 'Category2', + Name: 'Name2', + Price: 20, + PromoCode: 'PromoCode2', + Quantity: 2, + }, + ], + brand: 'Brand3', + category: 'Category3', + name: 'Name3', + price: 30, + coupon: 'PromoCode3', + quantity: 3, + sku: 'Sku3', + }; + + // Act + const result = populateProductProperties(productsMapping, properties); + + // Assert + expect(result).toEqual({ + ItemBrand1: 'Brand1', + ItemCategory1: 'Category1', + ItemName1: 'Name1', + ItemPrice1: 10, + ItemPromoCode1: 'PromoCode1', + ItemQuantity1: 1, + ItemSku1: 'Sku1', + ItemBrand2: 'Brand2', + ItemCategory2: 'Category2', + ItemName2: 'Name2', + ItemPrice2: 20, + ItemPromoCode2: 'PromoCode2', + ItemQuantity2: 2, + dummyRHS1: 'DummyValue', + }); + }); + + // Given an empty productsMapping and valid properties, it should return product properties with default mappings from properties + it('should return product properties with default mappings from properties when products array is not available', () => { + // Arrange + const productsMapping = []; + const properties = { + brand: 'Brand3', + category: 'Category3', + name: 'Name3', + price: 30, + coupon: 'PromoCode3', + quantity: 3, + sku: 'Sku3', + }; + + // Act + const result = populateProductProperties(productsMapping, properties); + + // Assert + expect(result).toEqual({ + ItemBrand1: 'Brand3', + ItemCategory1: 'Category3', + ItemName1: 'Name3', + ItemPrice1: 30, + ItemPromoCode1: 'PromoCode3', + ItemQuantity1: 3, + ItemSku1: 'Sku3', + }); + }); + it('should return product properties with custom mappings', () => { + const productsMapping = [ + { + from: 'dummy_LHS', + to: 'dummy_RHS', + }, + ]; + const properties = { + products: [ + { + brand: 'Brand3', + category: 'Category3', + name: 'Name3', + price: 30, + coupon: 'PromoCode3', + quantity: 3, + sku: 'Sku3', + dummy_LHS: 'DummyValue', + }, + ], + }; + + const result = populateProductProperties(productsMapping, properties); + + // Assert + expect(result).toEqual({ + ItemBrand1: 'Brand3', + ItemCategory1: 'Category3', + ItemName1: 'Name3', + ItemPrice1: 30, + ItemPromoCode1: 'PromoCode3', + ItemQuantity1: 3, + ItemSku1: 'Sku3', + dummy_RHS1: 'DummyValue', + }); + }); +}); diff --git a/src/v0/destinations/klaviyo/config.js b/src/v0/destinations/klaviyo/config.js index 5c15804e14..d8583ab9cb 100644 --- a/src/v0/destinations/klaviyo/config.js +++ b/src/v0/destinations/klaviyo/config.js @@ -57,6 +57,7 @@ const LIST_CONF = { }; const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); +const destType = 'klaviyo'; module.exports = { BASE_ENDPOINT, @@ -68,4 +69,5 @@ module.exports = { ecomEvents, eventNameMapping, jsonNameMapping, + destType, }; diff --git a/src/v0/destinations/klaviyo/data/KlaviyoGroup.json b/src/v0/destinations/klaviyo/data/KlaviyoGroup.json index baf6bcee86..b03cc9ee0a 100644 --- a/src/v0/destinations/klaviyo/data/KlaviyoGroup.json +++ b/src/v0/destinations/klaviyo/data/KlaviyoGroup.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/klaviyo/data/KlaviyoIdentify.json b/src/v0/destinations/klaviyo/data/KlaviyoIdentify.json index b358919bc1..fd46b6cda9 100644 --- a/src/v0/destinations/klaviyo/data/KlaviyoIdentify.json +++ b/src/v0/destinations/klaviyo/data/KlaviyoIdentify.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/klaviyo/data/KlaviyoProfile.json b/src/v0/destinations/klaviyo/data/KlaviyoProfile.json index 329ecd978f..03f155e787 100644 --- a/src/v0/destinations/klaviyo/data/KlaviyoProfile.json +++ b/src/v0/destinations/klaviyo/data/KlaviyoProfile.json @@ -1,7 +1,7 @@ [ { "destKey": "$email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/klaviyo/transform.js b/src/v0/destinations/klaviyo/transform.js index a0fe3e81a7..09e75919f9 100644 --- a/src/v0/destinations/klaviyo/transform.js +++ b/src/v0/destinations/klaviyo/transform.js @@ -51,7 +51,10 @@ const { JSON_MIME_TYPE, HTTP_STATUS_CODES } = require('../../util/constant'); * @param {*} reqMetadata * @returns */ -const identifyRequestHandler = async (message, category, destination, reqMetadata) => { +const identifyRequestHandler = async ( + { message, category, destination, metadata }, + reqMetadata, +) => { // If listId property is present try to subscribe/member user in list const { privateApiKey, enforceEmailAsPrimary, listId, flattenProperties } = destination.Config; const mappedToDestination = get(message, MappedToDestinationKey); @@ -109,11 +112,12 @@ const identifyRequestHandler = async (message, category, destination, reqMetadat }, }; - const { profileId, response, statusCode } = await getIdFromNewOrExistingProfile( + const { profileId, response, statusCode } = await getIdFromNewOrExistingProfile({ endpoint, payload, requestOptions, - ); + metadata, + }); const responseMap = { profileUpdateResponse: profileUpdateResponseBuilder( @@ -271,7 +275,8 @@ const groupRequestHandler = (message, category, destination) => { }; // Main event processor using specific handler funcs -const processEvent = async (message, destination, reqMetadata) => { +const processEvent = async (event, reqMetadata) => { + const { message, destination, metadata } = event; if (!message.type) { throw new InstrumentationError('Event type is required'); } @@ -285,7 +290,10 @@ const processEvent = async (message, destination, reqMetadata) => { switch (messageType) { case EventType.IDENTIFY: category = CONFIG_CATEGORIES.IDENTIFY; - response = await identifyRequestHandler(message, category, destination, reqMetadata); + response = await identifyRequestHandler( + { message, category, destination, metadata }, + reqMetadata, + ); break; case EventType.SCREEN: case EventType.TRACK: @@ -303,7 +311,7 @@ const processEvent = async (message, destination, reqMetadata) => { }; const process = async (event, reqMetadata) => { - const result = await processEvent(event.message, event.destination, reqMetadata); + const result = await processEvent(event, reqMetadata); return result; }; diff --git a/src/v0/destinations/klaviyo/util.js b/src/v0/destinations/klaviyo/util.js index df2dbb4712..4db59cfb05 100644 --- a/src/v0/destinations/klaviyo/util.js +++ b/src/v0/destinations/klaviyo/util.js @@ -2,6 +2,7 @@ const { defaultRequestConfig } = require('rudder-transformer-cdk/build/utils'); const lodash = require('lodash'); const { NetworkError, InstrumentationError } = require('@rudderstack/integrations-lib'); const { WhiteListedTraits } = require('../../../constants'); +const logger = require('../../../logger'); const { constructPayload, @@ -17,7 +18,13 @@ const tags = require('../../util/tags'); const { handleHttpRequest } = require('../../../adapters/network'); const { JSON_MIME_TYPE, HTTP_STATUS_CODES } = require('../../util/constant'); const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); -const { BASE_ENDPOINT, MAPPING_CONFIG, CONFIG_CATEGORIES, MAX_BATCH_SIZE } = require('./config'); +const { + BASE_ENDPOINT, + MAPPING_CONFIG, + CONFIG_CATEGORIES, + MAX_BATCH_SIZE, + destType, +} = require('./config'); const REVISION_CONSTANT = '2023-02-22'; @@ -32,10 +39,18 @@ const REVISION_CONSTANT = '2023-02-22'; * @param {*} requestOptions * @returns */ -const getIdFromNewOrExistingProfile = async (endpoint, payload, requestOptions) => { +const getIdFromNewOrExistingProfile = async ({ endpoint, payload, requestOptions, metadata }) => { let response; let profileId; const endpointPath = '/api/profiles'; + logger.requestLog(`[${destType.toUpperCase()}] get id from profile request`, { + metadata, + requestDetails: { + url: endpoint, + body: payload, + method: 'post', + }, + }); const { processedResponse: resp } = await handleHttpRequest( 'post', endpoint, @@ -49,6 +64,10 @@ const getIdFromNewOrExistingProfile = async (endpoint, payload, requestOptions) module: 'router', }, ); + logger.responseLog(`[${destType.toUpperCase()}] get id from profile response`, { + metadata, + responseDetails: resp, + }); /** * 201 - profile is created with updated payload no need to update it again (suppress event with 299 status code) diff --git a/src/v0/destinations/mailchimp/utils.js b/src/v0/destinations/mailchimp/utils.js index 1f4fc03ee5..f678742f2d 100644 --- a/src/v0/destinations/mailchimp/utils.js +++ b/src/v0/destinations/mailchimp/utils.js @@ -3,7 +3,6 @@ const md5 = require('md5'); const { InstrumentationError, NetworkError } = require('@rudderstack/integrations-lib'); const myAxios = require('../../../util/myAxios'); const { MappedToDestinationKey } = require('../../../constants'); -const logger = require('../../../logger'); const { isDefinedAndNotNull, isDefined, @@ -16,6 +15,7 @@ const { defaultBatchRequestConfig, constructPayload, } = require('../../util'); +const logger = require('../../../logger'); const { MERGE_CONFIG, MERGE_ADDRESS, SUBSCRIPTION_STATUS, VALID_STATUSES } = require('./config'); const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); const tags = require('../../util/tags'); diff --git a/src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json b/src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json index e32463e034..106560a447 100644 --- a/src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json +++ b/src/v0/destinations/mailjet/data/MailJetIdentifyConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": true, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/marketo_bulk_upload/marketo_bulk_upload.util.test.js b/src/v0/destinations/marketo_bulk_upload/marketo_bulk_upload.util.test.js index aa4b3aacc4..13e1b3a09a 100644 --- a/src/v0/destinations/marketo_bulk_upload/marketo_bulk_upload.util.test.js +++ b/src/v0/destinations/marketo_bulk_upload/marketo_bulk_upload.util.test.js @@ -514,7 +514,7 @@ describe('checkEventStatusViaSchemaMatching', () => { }); // The function correctly handles events with null values. - it('should correctly handle events with null values', () => { + it('should ignore event properties with null values', () => { const event = { input: [ { @@ -537,8 +537,6 @@ describe('checkEventStatusViaSchemaMatching', () => { const result = checkEventStatusViaSchemaMatching(event, fieldSchemaMapping); - expect(result).toEqual({ - job1: 'invalid id', - }); + expect(result).toEqual({}); }); }); diff --git a/src/v0/destinations/marketo_bulk_upload/util.js b/src/v0/destinations/marketo_bulk_upload/util.js index 4c99ba7483..033239b5e4 100644 --- a/src/v0/destinations/marketo_bulk_upload/util.js +++ b/src/v0/destinations/marketo_bulk_upload/util.js @@ -3,6 +3,7 @@ const { RetryableError, NetworkError, TransformationError, + isDefinedAndNotNull, } = require('@rudderstack/integrations-lib'); const { handleHttpRequest } = require('../../../adapters/network'); const tags = require('../../util/tags'); @@ -360,7 +361,6 @@ const getFieldSchemaMap = async (accessToken, munchkinId) => { module: 'router', }, ); - if (fieldSchemaMapping.response.errors) { handleCommonErrorResponse( fieldSchemaMapping, @@ -411,7 +411,11 @@ const checkEventStatusViaSchemaMatching = (event, fieldMap) => { const expectedDataType = SCHEMA_DATA_TYPE_MAP[fieldMap[paramName]]; const actualDataType = typeof paramValue; - if (!mismatchedFields[job_id] && actualDataType !== expectedDataType) { + if ( + isDefinedAndNotNull(paramValue) && + !mismatchedFields[job_id] && + actualDataType !== expectedDataType + ) { mismatchedFields[job_id] = `invalid ${paramName}`; } }); diff --git a/src/v0/destinations/monday/util.js b/src/v0/destinations/monday/util.js index 872fad42a7..0694028eb2 100644 --- a/src/v0/destinations/monday/util.js +++ b/src/v0/destinations/monday/util.js @@ -27,7 +27,7 @@ const getGroupId = (groupTitle, board) => { } }); if (groupId) { - return groupId; + return JSON.stringify(groupId); } throw new ConfigurationError(`Group ${groupTitle} doesn't exist in the board`); }; @@ -239,19 +239,20 @@ const populatePayload = (message, Config, boardDeatailsResponse) => { columnToPropertyMapping, boardDeatailsResponse.response?.data, ); + const items = [ + `board_id: ${boardId}`, + `item_name: ${JSON.stringify(message.properties?.name)}`, + `column_values: ${JSON.stringify(columnValues)}`, + ]; if (groupTitle) { if (!message.properties?.name) { throw new InstrumentationError('Item name is required to create an item'); } const groupId = getGroupId(groupTitle, boardDeatailsResponse.response?.data); - payload.query = `mutation { create_item (board_id: ${boardId}, group_id: ${groupId} item_name: ${JSON.stringify( - message.properties?.name, - )}, column_values: ${JSON.stringify(columnValues)}) {id}}`; - } else { - payload.query = `mutation { create_item (board_id: ${boardId}, item_name: ${JSON.stringify( - message.properties?.name, - )}, column_values: ${JSON.stringify(columnValues)}) {id}}`; + items.push(`group_id: ${groupId}`); } + const itemsQuery = items.join(', '); + payload.query = `mutation { create_item (${itemsQuery}) {id}}`; return payload; }; diff --git a/src/v0/destinations/mp/config.js b/src/v0/destinations/mp/config.js index 35b40294f5..3abdf2eebb 100644 --- a/src/v0/destinations/mp/config.js +++ b/src/v0/destinations/mp/config.js @@ -49,7 +49,6 @@ const MP_IDENTIFY_EXCLUSION_LIST = [ ]; const GEO_SOURCE_ALLOWED_VALUES = [null, 'reverse_geocoding']; -const TRACK_MAX_BATCH_SIZE = 50; const IMPORT_MAX_BATCH_SIZE = 2000; const ENGAGE_MAX_BATCH_SIZE = 2000; const GROUPS_MAX_BATCH_SIZE = 200; @@ -68,7 +67,6 @@ module.exports = { MP_IDENTIFY_EXCLUSION_LIST, getCreateDeletionTaskEndpoint, DISTINCT_ID_MAX_BATCH_SIZE, - TRACK_MAX_BATCH_SIZE, IMPORT_MAX_BATCH_SIZE, ENGAGE_MAX_BATCH_SIZE, GROUPS_MAX_BATCH_SIZE, diff --git a/src/v0/destinations/mp/data/MPIdentifyConfig.json b/src/v0/destinations/mp/data/MPIdentifyConfig.json index 679ce66d7f..b5e4f51970 100644 --- a/src/v0/destinations/mp/data/MPIdentifyConfig.json +++ b/src/v0/destinations/mp/data/MPIdentifyConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "$email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/mp/transform.js b/src/v0/destinations/mp/transform.js index 10271bebef..02eca8ed22 100644 --- a/src/v0/destinations/mp/transform.js +++ b/src/v0/destinations/mp/transform.js @@ -25,7 +25,6 @@ const { BASE_ENDPOINT, BASE_ENDPOINT_EU, IMPORT_MAX_BATCH_SIZE, - TRACK_MAX_BATCH_SIZE, ENGAGE_MAX_BATCH_SIZE, GROUPS_MAX_BATCH_SIZE, } = require('./config'); @@ -37,6 +36,7 @@ const { batchEvents, trimTraits, generatePageOrScreenCustomEventName, + recordBatchSizeMetrics, } = require('./util'); const { CommonUtils } = require('../../../util/common'); @@ -46,21 +46,19 @@ const mPEventPropertiesConfigJson = mappingConfig[ConfigCategory.EVENT_PROPERTIE const setImportCredentials = (destConfig) => { const endpoint = destConfig.dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/import/` : `${BASE_ENDPOINT}/import/`; - const headers = { 'Content-Type': 'application/json' }; const params = { strict: destConfig.strictMode ? 1 : 0 }; - const { apiSecret, serviceAccountUserName, serviceAccountSecret, projectId } = destConfig; - if (apiSecret) { - headers.Authorization = `Basic ${base64Convertor(`${apiSecret}:`)}`; + const { serviceAccountUserName, serviceAccountSecret, projectId, token } = destConfig; + let credentials; + if (token) { + credentials = `${token}:`; } else if (serviceAccountUserName && serviceAccountSecret && projectId) { - headers.Authorization = `Basic ${base64Convertor( - `${serviceAccountUserName}:${serviceAccountSecret}`, - )}`; + credentials = `${serviceAccountUserName}:${serviceAccountSecret}`; params.projectId = projectId; - } else { - throw new InstrumentationError( - 'Event timestamp is older than 5 days and no API secret or service account credentials (i.e. username, secret and projectId) are provided in destination configuration', - ); } + const headers = { + 'Content-Type': 'application/json', + Authorization: `Basic ${base64Convertor(credentials)}`, + }; return { endpoint, headers, params }; }; @@ -69,45 +67,34 @@ const responseBuilderSimple = (payload, message, eventType, destConfig) => { response.method = defaultPostRequestConfig.requestMethod; response.userId = message.userId || message.anonymousId; response.body.JSON_ARRAY = { batch: JSON.stringify([removeUndefinedValues(payload)]) }; - const { apiSecret, serviceAccountUserName, serviceAccountSecret, projectId, dataResidency } = - destConfig; + const { dataResidency } = destConfig; const duration = getTimeDifference(message.timestamp); + + const setCredentials = () => { + const credentials = setImportCredentials(destConfig); + response.endpoint = credentials.endpoint; + response.headers = credentials.headers; + response.params = { + project_id: credentials.params?.projectId, + strict: credentials.params.strict, + }; + }; + switch (eventType) { case EventType.ALIAS: case EventType.TRACK: case EventType.SCREEN: - case EventType.PAGE: - if ( - !apiSecret && - !(serviceAccountUserName && serviceAccountSecret && projectId) && - duration.days <= 5 - ) { - response.endpoint = - dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/track/` : `${BASE_ENDPOINT}/track/`; - response.headers = {}; - } else if (duration.years > 5) { + case EventType.PAGE: { + if (duration.years > 5) { throw new InstrumentationError('Event timestamp should be within last 5 years'); - } else { - const credentials = setImportCredentials(destConfig); - response.endpoint = credentials.endpoint; - response.headers = credentials.headers; - response.params = { - project_id: credentials.params?.projectId, - strict: credentials.params.strict, - }; - break; } + setCredentials(); break; - case 'merge': - // eslint-disable-next-line no-case-declarations - const credentials = setImportCredentials(destConfig); - response.endpoint = credentials.endpoint; - response.headers = credentials.headers; - response.params = { - project_id: credentials.params?.projectId, - strict: credentials.params.strict, - }; + } + case 'merge': { + setCredentials(); break; + } default: response.endpoint = dataResidency === 'eu' ? `${BASE_ENDPOINT_EU}/engage/` : `${BASE_ENDPOINT}/engage/`; @@ -399,7 +386,7 @@ const processGroupEvents = (message, type, destination) => { $set: { [groupKey]: groupKeyVal, }, - $ip: get(message, 'context.ip'), + $ip: get(message, 'context.ip') || message.request_ip, }; if (destination?.Config.identityMergeApi === 'simplified') { payload.$distinct_id = message.userId || `$device:${message.anonymousId}`; @@ -479,6 +466,12 @@ const process = (event) => processSingleMessage(event.message, event.destination // Ref: https://help.mixpanel.com/hc/en-us/articles/115004613766-Default-Properties-Collected-by-Mixpanel // Ref: https://help.mixpanel.com/hc/en-us/articles/115004561786-Track-UTM-Tags const processRouterDest = async (inputs, reqMetadata) => { + const batchSize = { + engage: 0, + groups: 0, + import: 0, + }; + const groupedEvents = groupEventsByType(inputs); const response = await Promise.all( groupedEvents.map(async (listOfEvents) => { @@ -507,19 +500,17 @@ const processRouterDest = async (inputs, reqMetadata) => { ); transformedPayloads = lodash.flatMap(transformedPayloads); - const { engageEvents, groupsEvents, trackEvents, importEvents, batchErrorRespList } = + const { engageEvents, groupsEvents, importEvents, batchErrorRespList } = groupEventsByEndpoint(transformedPayloads); const engageRespList = batchEvents(engageEvents, ENGAGE_MAX_BATCH_SIZE, reqMetadata); const groupsRespList = batchEvents(groupsEvents, GROUPS_MAX_BATCH_SIZE, reqMetadata); - const trackRespList = batchEvents(trackEvents, TRACK_MAX_BATCH_SIZE, reqMetadata); const importRespList = batchEvents(importEvents, IMPORT_MAX_BATCH_SIZE, reqMetadata); - const batchSuccessRespList = [ - ...engageRespList, - ...groupsRespList, - ...trackRespList, - ...importRespList, - ]; + const batchSuccessRespList = [...engageRespList, ...groupsRespList, ...importRespList]; + + batchSize.engage += engageRespList.length; + batchSize.groups += groupsRespList.length; + batchSize.import += importRespList.length; return [...batchSuccessRespList, ...batchErrorRespList]; }), @@ -527,6 +518,9 @@ const processRouterDest = async (inputs, reqMetadata) => { // Flatten the response array containing batched events from multiple groups const allBatchedEvents = lodash.flatMap(response); + + const { destination } = allBatchedEvents[0]; + recordBatchSizeMetrics(batchSize, destination.ID); return combineBatchRequestsWithSameJobIds(allBatchedEvents); }; diff --git a/src/v0/destinations/mp/util.js b/src/v0/destinations/mp/util.js index f56242d88b..b2807d6e11 100644 --- a/src/v0/destinations/mp/util.js +++ b/src/v0/destinations/mp/util.js @@ -25,6 +25,7 @@ const { mappingConfig, } = require('./config'); const { CommonUtils } = require('../../../util/common'); +const stats = require('../../../util/stats'); const mPIdentifyConfigJson = mappingConfig[ConfigCategory.IDENTIFY.name]; const mPProfileAndroidConfigJson = mappingConfig[ConfigCategory.PROFILE_ANDROID.name]; @@ -135,7 +136,7 @@ const createIdentifyResponse = (message, type, destination, responseBuilderSimpl * @returns */ const isImportAuthCredentialsAvailable = (destination) => - destination.Config.apiSecret || + destination.Config.token || (destination.Config.serviceAccountSecret && destination.Config.serviceAccountUserName && destination.Config.projectId); @@ -178,7 +179,6 @@ const groupEventsByEndpoint = (events) => { const eventMap = { engage: [], groups: [], - track: [], import: [], }; const batchErrorRespList = []; @@ -203,7 +203,6 @@ const groupEventsByEndpoint = (events) => { return { engageEvents: eventMap.engage, groupsEvents: eventMap.groups, - trackEvents: eventMap.track, importEvents: eventMap.import, batchErrorRespList, }; @@ -342,6 +341,28 @@ const generatePageOrScreenCustomEventName = (message, userDefinedEventTemplate) return eventName; }; +/** + * Records the batch size metrics for different endpoints. + * + * @param {Object} batchSize - The object containing the batch size for different endpoints. + * @param {number} batchSize.engage - The batch size for engage endpoint. + * @param {number} batchSize.groups - The batch size for group endpoint. + * @param {number} batchSize.import - The batch size for import endpoint. + * @param {string} destinationId - The ID of the destination. + * @returns {void} + */ +const recordBatchSizeMetrics = (batchSize, destinationId) => { + stats.gauge('mixpanel_batch_engage_pack_size', batchSize.engage, { + destination_id: destinationId, + }); + stats.gauge('mixpanel_batch_group_pack_size', batchSize.groups, { + destination_id: destinationId, + }); + stats.gauge('mixpanel_batch_import_pack_size', batchSize.import, { + destination_id: destinationId, + }); +}; + module.exports = { createIdentifyResponse, isImportAuthCredentialsAvailable, @@ -351,4 +372,5 @@ module.exports = { batchEvents, trimTraits, generatePageOrScreenCustomEventName, + recordBatchSizeMetrics, }; diff --git a/src/v0/destinations/mp/util.test.js b/src/v0/destinations/mp/util.test.js index 40cdb34649..3666081f59 100644 --- a/src/v0/destinations/mp/util.test.js +++ b/src/v0/destinations/mp/util.test.js @@ -18,7 +18,6 @@ describe('Unit test cases for groupEventsByEndpoint', () => { expect(result).toEqual({ engageEvents: [], groupsEvents: [], - trackEvents: [], importEvents: [], batchErrorRespList: [], }); @@ -122,19 +121,6 @@ describe('Unit test cases for groupEventsByEndpoint', () => { }, }, ], - trackEvents: [ - { - message: { - endpoint: '/track', - body: { - JSON_ARRAY: { - batch: '[{prop:4}]', - }, - }, - userId: 'user1', - }, - }, - ], importEvents: [ { message: { diff --git a/src/v0/destinations/mssql/transform.js b/src/v0/destinations/mssql/transform.js index 2baadebdee..12dd7b40c6 100644 --- a/src/v0/destinations/mssql/transform.js +++ b/src/v0/destinations/mssql/transform.js @@ -1,28 +1,24 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const mssql = 'mssql'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} - +const provider = 'mssql'; function getDataTypeOverride() {} function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = mssql; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, getDataTypeOverride, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/one_signal/config.js b/src/v0/destinations/one_signal/config.js index 1a58f3f91f..fdaa3ecd7f 100644 --- a/src/v0/destinations/one_signal/config.js +++ b/src/v0/destinations/one_signal/config.js @@ -1,6 +1,7 @@ const { getMappingConfig } = require('../../util'); const BASE_URL = 'https://onesignal.com/api/v1'; +const BASE_URL_V2 = 'https://api.onesignal.com/apps/{{app_id}}/users'; const ENDPOINTS = { IDENTIFY: { @@ -16,13 +17,33 @@ const ENDPOINTS = { const ConfigCategory = { IDENTIFY: { name: 'OneSignalIdentifyConfig', endpoint: '/players' }, + IDENTIFY_V2: { name: 'OneSignalIdentifyConfigV2' }, + SUBSCRIPTION: { name: 'OneSignalSubscriptionConfig' }, }; const mappingConfig = getMappingConfig(ConfigCategory, __dirname); +// Used for User Model (V2) +const deviceTypesV2Enums = [ + 'iOSPush', + 'email', + 'sms', + 'AndroidPush', + 'HuaweiPush', + 'FireOSPush', + 'WindowsPush', + 'macOSPush', + 'ChromeExtensionPush', + 'ChromePush', + 'SafariLegacyPush', + 'FirefoxPush', + 'SafariPush', +]; module.exports = { BASE_URL, + BASE_URL_V2, ENDPOINTS, ConfigCategory, mappingConfig, + deviceTypesV2Enums, }; diff --git a/src/v0/destinations/one_signal/data/OneSignalIdentifyConfigV2.json b/src/v0/destinations/one_signal/data/OneSignalIdentifyConfigV2.json new file mode 100644 index 0000000000..61ab6e0109 --- /dev/null +++ b/src/v0/destinations/one_signal/data/OneSignalIdentifyConfigV2.json @@ -0,0 +1,54 @@ +[ + { "sourceKeys": "context.locale", "destKey": "properties.laguage", "required": false }, + { "sourceKeys": "context.ip", "destKey": "properties.ip", "required": false }, + { "sourceKeys": "context.timezone", "destKey": "properties.timezone_id", "required": false }, + { "sourceKeys": "context.location.latitude", "destKey": "properties.lat", "required": false }, + { "sourceKeys": "context.location.longitude", "destKey": "properties.long", "required": false }, + { + "sourceKeys": "createdAt", + "destKey": "properties.created_at", + "sourceFromGenericMap": true, + "metadata": { + "type": "secondTimestamp" + }, + "required": false + }, + { + "sourceKeys": "createdAt", + "destKey": "properties.last_active", + "sourceFromGenericMap": true, + "metadata": { + "type": "secondTimestamp" + }, + "required": false + }, + { + "sourceKeys": [ + "traits.country", + "context.traits.country", + "traits.address.country", + "context.traits.address.country" + ], + "destKey": "properties.country", + "required": false + }, + { + "sourceKeys": [ + "traits.firstActive", + "context.traits.firstActive", + "traits.first_active", + "context.traits.first_active" + ], + "metadata": { + "type": "secondTimestamp" + }, + "destKey": "properties.first_active", + "required": false + }, + { + "sourceKeys": "userIdOnly", + "destKey": "identity.external_id", + "sourceFromGenericMap": true, + "required": false + } +] diff --git a/src/v0/destinations/one_signal/data/OneSignalSubscriptionConfig.json b/src/v0/destinations/one_signal/data/OneSignalSubscriptionConfig.json new file mode 100644 index 0000000000..4a7a877daa --- /dev/null +++ b/src/v0/destinations/one_signal/data/OneSignalSubscriptionConfig.json @@ -0,0 +1,8 @@ +[ + { "sourceKeys": "enabled", "destKey": "enabled", "required": false }, + { "sourceKeys": "notification_types", "destKey": "notification_types", "required": false }, + { "sourceKeys": "session_time", "destKey": "session_time", "required": false }, + { "sourceKeys": "session_count", "destKey": "session_count", "required": false }, + { "sourceKeys": "app_version", "destKey": "app_version", "required": false }, + { "sourceKeys": "test_type", "destKey": "test_type", "required": false } +] diff --git a/src/v0/destinations/one_signal/transform.js b/src/v0/destinations/one_signal/transform.js index b025660fa4..aac48e3b4e 100644 --- a/src/v0/destinations/one_signal/transform.js +++ b/src/v0/destinations/one_signal/transform.js @@ -4,6 +4,7 @@ const { TransformationError, InstrumentationError, } = require('@rudderstack/integrations-lib'); +const { process: processV2 } = require('./transformV2'); const { EventType } = require('../../../constants'); const { ConfigCategory, mappingConfig, BASE_URL, ENDPOINTS } = require('./config'); const { @@ -186,10 +187,16 @@ const groupResponseBuilder = (message, { Config }) => { }; const processEvent = (message, destination) => { + const { Config } = destination; + const { version, appId } = Config; + if (version === 'V2') { + // This version is used to direct the request to user centric model + return processV2(message, destination); + } if (!message.type) { throw new InstrumentationError('Event type is required'); } - if (!destination.Config.appId) { + if (!appId) { throw new ConfigurationError('appId is a required field'); } const messageType = message.type.toLowerCase(); diff --git a/src/v0/destinations/one_signal/transformV2.js b/src/v0/destinations/one_signal/transformV2.js new file mode 100644 index 0000000000..3d084e3c8a --- /dev/null +++ b/src/v0/destinations/one_signal/transformV2.js @@ -0,0 +1,159 @@ +const get = require('get-value'); +const { + ConfigurationError, + TransformationError, + InstrumentationError, +} = require('@rudderstack/integrations-lib'); +const { EventType } = require('../../../constants'); +const { ConfigCategory, mappingConfig, BASE_URL_V2 } = require('./config'); +const { + defaultRequestConfig, + getFieldValueFromMessage, + constructPayload, + defaultPostRequestConfig, + removeUndefinedAndNullValues, +} = require('../../util'); +const { + populateTags, + getProductPurchasesDetails, + getSubscriptions, + getOneSignalAliases, +} = require('./util'); +const { JSON_MIME_TYPE } = require('../../util/constant'); + +const responseBuilder = (payload, Config) => { + const { appId } = Config; + if (payload) { + const response = defaultRequestConfig(); + response.endpoint = `${BASE_URL_V2.replace('{{app_id}}', appId)}`; + response.headers = { + Accept: JSON_MIME_TYPE, + 'Content-Type': JSON_MIME_TYPE, + }; + response.method = defaultPostRequestConfig.requestMethod; + response.body.JSON = removeUndefinedAndNullValues(payload); + return response; + } + throw new TransformationError('Payload could not be populated due to wrong input'); +}; + +/** + * This function is used for creating response for identify call, to create a new user or update an existing user. + * a responseArray for creating/updating user is being prepared. + * If the value of emailDeviceType/smsDeviceType(toggle in dashboard) is true, separate responses will also be created + * for new subscriptions to be added to user with email/sms as token. + * @param {*} message + * @param {*} param1 + * @returns + */ +const identifyResponseBuilder = (message, { Config }) => { + // Populating the tags + const tags = populateTags(message); + + const payload = constructPayload(message, mappingConfig[ConfigCategory.IDENTIFY_V2.name]); + if (!payload?.identity?.external_id) { + const alias = getOneSignalAliases(message); + if (Object.keys(alias).length === 0) { + throw new InstrumentationError('userId or any other alias is required for identify'); + } + payload.identity = alias; + } + // Following check is to intialise properties object in case we don't get properties object from construct payload + if (!payload.properties) { + payload.properties = {}; + } + payload.subscriptions = getSubscriptions(message, Config); + payload.properties.tags = tags; + return responseBuilder(removeUndefinedAndNullValues(payload), Config); +}; + +/** + * This function is used to build the response for track call and Group call. + * It is used to edit the OneSignal tags using external_id. + * It edits tags[event] as true for track call + * @param {*} message + * @param {*} param1 + * @returns + */ +const trackOrGroupResponseBuilder = (message, { Config }, msgtype) => { + const { eventAsTags, allowedProperties } = Config; + const event = get(message, 'event'); + const groupId = getFieldValueFromMessage(message, 'groupId'); + // validation and adding tags for track and group call respectively + const tags = {}; + const payload = { properties: {} }; + if (msgtype === EventType.TRACK) { + if (!event) { + throw new InstrumentationError('Event is not present in the input payloads'); + } + /* Populating event as true in tags. + eg. tags: { + "event_name": true + } + */ + tags[event] = true; + payload.properties.purchases = getProductPurchasesDetails(message); + } + if (msgtype === EventType.GROUP) { + if (!groupId) { + throw new InstrumentationError('groupId is required for group events'); + } + tags.groupId = groupId; + } + + const externalUserId = getFieldValueFromMessage(message, 'userIdOnly'); + if (!externalUserId) { + const alias = getOneSignalAliases(message); + if (Object.keys(alias).length === 0) { + throw new InstrumentationError('userId or any other alias is required for track and group'); + } + payload.identity = alias; + } else { + payload.identity = { + external_id: externalUserId, + }; + } + + // Populating tags using allowed properties(from dashboard) + const properties = get(message, 'properties'); + if (properties && allowedProperties && Array.isArray(allowedProperties)) { + allowedProperties.forEach((item) => { + if (typeof properties[item.propertyName] === 'string') { + const tagName = + event && eventAsTags ? `${event}_${[item.propertyName]}` : item.propertyName; + tags[tagName] = properties[item.propertyName]; + } + }); + } + payload.properties.tags = tags; + return responseBuilder(removeUndefinedAndNullValues(payload), Config); +}; + +const processEvent = (message, destination) => { + if (!message.type) { + throw new InstrumentationError('Event type is required'); + } + if (!destination.Config.appId) { + throw new ConfigurationError('appId is a required field'); + } + const messageType = message.type.toLowerCase(); + let response; + switch (messageType) { + case EventType.IDENTIFY: + response = identifyResponseBuilder(message, destination); + break; + case EventType.TRACK: + response = trackOrGroupResponseBuilder(message, destination, EventType.TRACK); + break; + case EventType.GROUP: + response = trackOrGroupResponseBuilder(message, destination, EventType.GROUP); + break; + default: + throw new InstrumentationError(`Message type ${messageType} is not supported`); + } + return response; +}; + +const process = (message, destination) => processEvent(message, destination); + +module.exports = { process }; diff --git a/src/v0/destinations/one_signal/util.js b/src/v0/destinations/one_signal/util.js index 2de57de1b4..69cbd5440c 100644 --- a/src/v0/destinations/one_signal/util.js +++ b/src/v0/destinations/one_signal/util.js @@ -1,6 +1,13 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib'); -const { getIntegrationsObj, getFieldValueFromMessage, getBrowserInfo } = require('../../util'); - +const { + getIntegrationsObj, + getFieldValueFromMessage, + getBrowserInfo, + constructPayload, + removeUndefinedAndNullValues, +} = require('../../util'); +const { ConfigCategory, mappingConfig, deviceTypesV2Enums } = require('./config'); +const { isDefinedAndNotNullAndNotEmpty } = require('../../util'); // For mapping device_type value const deviceTypeMapping = { android: 1, @@ -45,7 +52,7 @@ const populateTags = (message) => { const populateDeviceType = (message, payload) => { const integrationsObj = getIntegrationsObj(message, 'one_signal'); const devicePayload = payload; - if (integrationsObj && integrationsObj.deviceType && integrationsObj.identifier) { + if (integrationsObj?.deviceType && integrationsObj?.identifier) { devicePayload.device_type = parseInt(integrationsObj.deviceType, 10); devicePayload.identifier = integrationsObj.identifier; if (!validateDeviceType(devicePayload.device_type)) { @@ -72,4 +79,147 @@ const populateDeviceType = (message, payload) => { } }; -module.exports = { populateDeviceType, populateTags }; +/** + * This function is used to populate device type required for creating a subscription + * it checks from integrations object and fall back to message.channel and fif nothing is given it return a n empty object + * @param {*} message + * @param {*} payload + * returns Object + */ +const getDeviceDetails = (message) => { + const integrationsObj = getIntegrationsObj(message, 'one_signal'); + const devicePayload = {}; + if (integrationsObj?.deviceType && integrationsObj?.identifier) { + devicePayload.type = integrationsObj.deviceType; + devicePayload.token = integrationsObj.token || integrationsObj.identifier; + } + // Mapping device type when it is not present in the integrationsObject + if (!devicePayload.type) { + // if channel is mobile, checking for type from `context.device.type` + if (message.channel === 'mobile') { + devicePayload.type = message.context?.device?.type; + devicePayload.token = message.context?.device?.token + ? message.context.device.token + : message.context?.device?.id; + } + // Parsing the UA to get the browser info to map the device_type + if (message.channel === 'web' && message.context?.userAgent) { + const browser = getBrowserInfo(message.context.userAgent); + devicePayload.type = `${browser.name}Push`; // For chrome it would be like ChromePush + devicePayload.token = message.anonymousId; + } + } + if (!deviceTypesV2Enums.includes(devicePayload.type)) { + return {}; // No device related information available + } + return devicePayload; +}; +/** + * This function maps and returns the product purchases details built from input message.properties.products + * @param {*} message + * @returns + */ +const getProductPurchasesDetails = (message) => { + const { properties } = message; + const purchases = properties?.products; + if (purchases && Array.isArray(purchases)) { + return purchases.map((product) => ({ + sku: product.sku, + iso: product.iso, + count: product.quantity, + amount: product.amount, + })); + } + const purchaseObject = removeUndefinedAndNullValues({ + sku: properties?.sku, + iso: properties?.iso, + count: properties?.quantity, + amount: properties?.amount, + }); + return Object.keys(purchaseObject).length > 0 ? [purchaseObject] : []; +}; + +/** + * This function generates the subscriptions Payload for the given deviceType and token + * https://documentation.onesignal.com/reference/create-user#:~:text=string-,subscriptions,-array%20of%20objects + * @param {*} message + * @param {*} deviceType + * @param {*} token + * @returns + */ +const constructSubscription = (message, subscriptionType, token, subscriptionField) => { + const deviceModel = message.context?.device?.model; + const deviceOs = message.context?.os?.version; + let deviceSubscriptionPayload = { + type: subscriptionType, + token, + device_model: deviceModel, + device_os: deviceOs, + }; + // Following mapping is used to do paticular and specific property mapping for subscription + const traits = message.context?.traits || message.traits; + if (traits?.subscriptions?.[subscriptionField]) { + deviceSubscriptionPayload = { + ...deviceSubscriptionPayload, + ...constructPayload( + traits.subscriptions[subscriptionField], + mappingConfig[ConfigCategory.SUBSCRIPTION.name], + ), + }; + } + return deviceSubscriptionPayload; +}; + +/** + * This function constructs subscriptions list from message and returns subscriptions list + * @param {*} message + * @param {*} Config + * @returns + */ +const getSubscriptions = (message, Config) => { + const { emailDeviceType, smsDeviceType } = Config; + // Creating response for creation of new device or updation of an existing device + const subscriptions = []; + const deviceTypeSubscription = getDeviceDetails(message); + if (deviceTypeSubscription.token) { + subscriptions.push( + constructSubscription(message, deviceTypeSubscription.type, deviceTypeSubscription.token), + ); + } + + // Creating a device with email as an identifier + if (emailDeviceType) { + const token = getFieldValueFromMessage(message, 'email'); + if (isDefinedAndNotNullAndNotEmpty(token)) { + subscriptions.push(constructSubscription(message, 'Email', token, 'email')); + } + } + // Creating a device with phone as an identifier + if (smsDeviceType) { + const token = getFieldValueFromMessage(message, 'phone'); + if (isDefinedAndNotNullAndNotEmpty(token)) { + subscriptions.push(constructSubscription(message, 'SMS', token, 'phone')); + } + } + return subscriptions.length > 0 ? subscriptions : undefined; +}; + +/** + * This function fetched all the aliases to be passed to one signal from integrations object + * @param {*} message + * @returns object + */ +const getOneSignalAliases = (message) => { + const integrationsObj = getIntegrationsObj(message, 'one_signal'); + if (integrationsObj?.aliases) { + return integrationsObj.aliases; + } + return {}; +}; +module.exports = { + populateDeviceType, + populateTags, + getProductPurchasesDetails, + getSubscriptions, + getOneSignalAliases, +}; diff --git a/src/v0/destinations/one_signal/utils.test.js b/src/v0/destinations/one_signal/utils.test.js new file mode 100644 index 0000000000..afcf746ab6 --- /dev/null +++ b/src/v0/destinations/one_signal/utils.test.js @@ -0,0 +1,35 @@ +const { getOneSignalAliases } = require('./util'); + +describe('getOneSignalAliases', () => { + // returns aliases when integrationsObj contains aliases + it('should return aliases when integrationsObj contains aliases', () => { + const message = { + someKey: 'someValue', + integrations: { one_signal: { aliases: { alias1: 'value1' } } }, + }; + const result = getOneSignalAliases(message); + expect(result).toEqual({ alias1: 'value1' }); + }); + + // handles null or undefined message parameter gracefully + it('should handle null or undefined message parameter gracefully', () => { + let result = getOneSignalAliases(null); + expect(result).toEqual({}); + result = getOneSignalAliases(undefined); + expect(result).toEqual({}); + }); + + // returns an empty object when integrationsObj does not contain aliases + it('should return an empty object when integrationsObj does not contain aliases', () => { + const message = { someKey: 'someValue', integrations: { one_signal: {} } }; + const result = getOneSignalAliases(message); + expect(result).toEqual({}); + }); + + // handles message parameter with unexpected structure + it('should handle message parameter with unexpected structure', () => { + const message = null; + const result = getOneSignalAliases(message); + expect(result).toEqual({}); + }); +}); diff --git a/src/v0/destinations/pardot/networkHandler.js b/src/v0/destinations/pardot/networkHandler.js index edf713ce97..60d2f7ee23 100644 --- a/src/v0/destinations/pardot/networkHandler.js +++ b/src/v0/destinations/pardot/networkHandler.js @@ -46,6 +46,19 @@ const getStatus = (code) => { const pardotRespHandler = (destResponse, stageMsg) => { const { status, response } = destResponse; const respAttributes = response['@attributes']; + + // to handle errors like service unavilable, wrong url, no response + if (!respAttributes) { + throw new NetworkError( + `${JSON.stringify(response)} ${stageMsg}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + response, + ); + } + const { stat, err_code: errorCode } = respAttributes; if (isHttpStatusSuccess(status) && stat !== 'fail') { diff --git a/src/v0/destinations/pinterest_tag/data/pinterestUserConfig.json b/src/v0/destinations/pinterest_tag/data/pinterestUserConfig.json index 0ab963ac3c..b95f539e7c 100644 --- a/src/v0/destinations/pinterest_tag/data/pinterestUserConfig.json +++ b/src/v0/destinations/pinterest_tag/data/pinterestUserConfig.json @@ -1,7 +1,7 @@ [ { "destKey": "em", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/pinterest_tag/utils.js b/src/v0/destinations/pinterest_tag/utils.js index 340fba498e..57d595571f 100644 --- a/src/v0/destinations/pinterest_tag/utils.js +++ b/src/v0/destinations/pinterest_tag/utils.js @@ -41,8 +41,8 @@ const getHashedValue = (key, value) => { case 'fn': case 'ge': value = Array.isArray(value) - ? value.map((val) => val.toString().toLowerCase()) - : value.toString().toLowerCase(); + ? value.map((val) => val.toString().trim().toLowerCase()) + : value.toString().trim().toLowerCase(); break; case 'ph': // phone numbers should only contain digits & should not contain leading zeros @@ -53,7 +53,7 @@ const getHashedValue = (key, value) => { case 'zp': // zip fields should only contain digits value = Array.isArray(value) - ? value.map((val) => val.toString().replace(/\D/g, '')) + ? value.map((val) => val.toString().trim().replace(/\D/g, '')) : value.toString().replace(/\D/g, ''); break; case 'hashed_maids': diff --git a/src/v0/destinations/postgres/transform.js b/src/v0/destinations/postgres/transform.js index 32c6b0a069..b57bf4369a 100644 --- a/src/v0/destinations/postgres/transform.js +++ b/src/v0/destinations/postgres/transform.js @@ -1,10 +1,6 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const postgres = 'postgres'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'postgres'; function getDataTypeOverride(key, val, options, jsonKey = false) { if (key === 'violationErrors' || jsonKey) { @@ -17,8 +13,7 @@ function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; const destJsonPaths = event.destination?.Config?.jsonPaths || ''; - const provider = postgres; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -26,10 +21,12 @@ function process(event) { provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, destJsonPaths, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/redis/transform.js b/src/v0/destinations/redis/transform.js index 23c73f0ba4..ec0e858d3e 100644 --- a/src/v0/destinations/redis/transform.js +++ b/src/v0/destinations/redis/transform.js @@ -2,7 +2,7 @@ const lodash = require('lodash'); const flatten = require('flat'); const { InstrumentationError } = require('@rudderstack/integrations-lib'); -const { isEmpty, isObject } = require('../../util'); +const { isEmpty, isObject, getFieldValueFromMessage } = require('../../util'); const { EventType } = require('../../../constants'); // processValues: @@ -46,6 +46,19 @@ const transformSubEventTypeProfiles = (message, workspaceId, destinationId) => { }; }; +const getJSONValue = (message) => { + const eventType = message.type.toLowerCase(); + if (eventType === EventType.IDENTIFY) { + return getFieldValueFromMessage(message, 'traits'); + } + return {}; +}; + +const getTransformedPayloadForJSON = ({ key, path, value, userId }) => ({ + message: { key, path, value }, + userId, +}); + const process = (event) => { const { message, destination, metadata } = event; const messageType = message && message.type && message.type.toLowerCase(); @@ -58,15 +71,35 @@ const process = (event) => { throw new InstrumentationError('Blank userId passed in identify event'); } - const { prefix } = destination.Config; + const { prefix, useJSONModule } = destination.Config; const destinationId = destination.ID; const keyPrefix = isEmpty(prefix) ? '' : `${prefix.trim()}:`; + const jsonValue = getJSONValue(message); + if (isSubEventTypeProfiles(message)) { const { workspaceId } = metadata; + if (useJSONModule) { + // If redis should store information as JSON type + return getTransformedPayloadForJSON({ + key: `${workspaceId}:${destinationId}:${message.context.sources.profiles_entity}:${message.context.sources.profiles_id_type}:${message.userId}`, + path: message.context.sources.profiles_model, + value: jsonValue, + userId: message.userId, + }); + } return transformSubEventTypeProfiles(message, workspaceId, destinationId); } + if (useJSONModule) { + // If redis should store information as JSON type + return getTransformedPayloadForJSON({ + key: `${keyPrefix}user:${lodash.toString(message.userId)}`, + value: jsonValue, + userId: message.userId, + }); + } + const hmap = { key: `${keyPrefix}user:${lodash.toString(message.userId)}`, fields: {}, diff --git a/src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json b/src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json index f7e6fa7acc..b31f1355a6 100644 --- a/src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json +++ b/src/v0/destinations/revenue_cat/data/RCIdentifyConfig.json @@ -13,7 +13,7 @@ }, { "destKey": "$email.value", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json b/src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json index e405f1477d..cc3a4d5ec5 100644 --- a/src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json +++ b/src/v0/destinations/rockerbox/data/RockerboxTrackConfig.json @@ -12,7 +12,7 @@ "sourceFromGenericMap": false }, { - "sourceKeys": "email", + "sourceKeys": "emailOnly", "destKey": "email", "required": false, "sourceFromGenericMap": true diff --git a/src/v0/destinations/rs/transform.js b/src/v0/destinations/rs/transform.js index f051ff49d5..781600a8e2 100644 --- a/src/v0/destinations/rs/transform.js +++ b/src/v0/destinations/rs/transform.js @@ -2,11 +2,7 @@ const { processWarehouseMessage } = require('../../../warehouse'); // redshift destination string limit, if the string length crosses 512 we will change data type to text which is varchar(max) in redshift const RSStringLimit = 512; -const redshift = 'rs'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'rs'; function getDataTypeOverride(key, val, options, jsonKey = false) { if (jsonKey) { @@ -26,8 +22,7 @@ function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; const destJsonPaths = event.destination?.Config?.jsonPaths || ''; - const provider = redshift; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -35,10 +30,12 @@ function process(event) { provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, destJsonPaths, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/s3_datalake/transform.js b/src/v0/destinations/s3_datalake/transform.js index 7013224faa..8bbfa1556d 100644 --- a/src/v0/destinations/s3_datalake/transform.js +++ b/src/v0/destinations/s3_datalake/transform.js @@ -1,29 +1,25 @@ const { processWarehouseMessage } = require('../../../warehouse'); // use postgres providers for s3-datalake -const s3datalakeProvider = 's3_datalake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} - +const provider = 's3_datalake'; function getDataTypeOverride() {} function process(event) { const whSchemaVersion = event.request.query.whSchemaVersion || 'v1'; const whStoreEvent = event.destination.Config.storeFullEvent === true; - const provider = s3datalakeProvider; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, getDataTypeOverride, provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/sendgrid/data/SendgridIdentify.json b/src/v0/destinations/sendgrid/data/SendgridIdentify.json index e70378dd28..bfafd0da01 100644 --- a/src/v0/destinations/sendgrid/data/SendgridIdentify.json +++ b/src/v0/destinations/sendgrid/data/SendgridIdentify.json @@ -1,7 +1,7 @@ [ { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": true, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/serenytics/data/SerenyticsIdentifyConfig.json b/src/v0/destinations/serenytics/data/SerenyticsIdentifyConfig.json index 4c50cb4a54..9161a7c698 100644 --- a/src/v0/destinations/serenytics/data/SerenyticsIdentifyConfig.json +++ b/src/v0/destinations/serenytics/data/SerenyticsIdentifyConfig.json @@ -35,7 +35,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/sfmc/transform.js b/src/v0/destinations/sfmc/transform.js index 53925bc7ed..bf474ff3f0 100644 --- a/src/v0/destinations/sfmc/transform.js +++ b/src/v0/destinations/sfmc/transform.js @@ -7,8 +7,8 @@ const { isDefinedAndNotNull, isEmpty, } = require('@rudderstack/integrations-lib'); -const myAxios = require('../../../util/myAxios'); const { EventType } = require('../../../constants'); +const { handleHttpRequest } = require('../../../adapters/network'); const { CONFIG_CATEGORIES, MAPPING_CONFIG, ENDPOINTS } = require('./config'); const { removeUndefinedAndNullValues, @@ -22,10 +22,8 @@ const { getHashFromArray, simpleProcessRouterDest, } = require('../../util'); -const { - getDynamicErrorType, - nodeSysErrorToStatus, -} = require('../../../adapters/utils/networkUtils'); +const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../util'); const tags = require('../../util/tags'); const { JSON_MIME_TYPE } = require('../../util/constant'); @@ -34,51 +32,38 @@ const CONTACT_KEY_KEY = 'Contact Key'; // DOC: https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/access-token-s2s.htm const getToken = async (clientId, clientSecret, subdomain) => { - try { - const resp = await myAxios.post( - `https://${subdomain}.${ENDPOINTS.GET_TOKEN}`, - { - grant_type: 'client_credentials', - client_id: clientId, - client_secret: clientSecret, - }, - { - 'Content-Type': JSON_MIME_TYPE, - }, - { - destType: 'sfmc', - feature: 'transformation', - endpointPath: '/token', - requestMethod: 'POST', - module: 'router', - }, - ); - if (resp && resp.data) { - return resp.data.access_token; - } - const status = resp.status || 400; + const { processedResponse: processedResponseSfmc } = await handleHttpRequest( + 'post', + `https://${subdomain}.${ENDPOINTS.GET_TOKEN}`, + { + grant_type: 'client_credentials', + client_id: clientId, + client_secret: clientSecret, + }, + { + 'Content-Type': JSON_MIME_TYPE, + }, + { + destType: 'sfmc', + feature: 'transformation', + endpointPath: '/token', + requestMethod: 'POST', + module: 'router', + }, + ); + + if (!isHttpStatusSuccess(processedResponseSfmc.status)) { throw new NetworkError( 'Could not retrieve access token', - status, + processedResponseSfmc.status || 400, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(processedResponseSfmc.status || 400), }, - resp, + processedResponseSfmc.response, ); - } catch (error) { - if (!isEmpty(error.response)) { - const status = error.status || 400; - throw new NetworkError(`Authorization Failed ${error.response.statusText}`, status, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), - }); - } else { - const httpError = nodeSysErrorToStatus(error.code); - const status = httpError.status || 400; - throw new NetworkError(`Authorization Failed ${httpError.message}`, status, { - [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), - }); - } } + + return processedResponseSfmc.response.access_token; }; // DOC : https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/createContacts.htm diff --git a/src/v0/destinations/singular/data/SINGULARAndroidEventConfig.json b/src/v0/destinations/singular/data/SINGULARAndroidEventConfig.json index 78a8ed20d2..f66cda7ee5 100644 --- a/src/v0/destinations/singular/data/SINGULARAndroidEventConfig.json +++ b/src/v0/destinations/singular/data/SINGULARAndroidEventConfig.json @@ -88,17 +88,23 @@ { "destKey": "aifa", "sourceKeys": "context.device.advertisingId", - "required": true + "required": false, + "metadata": { + "defaultValue": "" + } }, { "destKey": "andi", "sourceKeys": "context.device.id", - "required": true + "required": false, + "metadata": { + "defaultValue": "" + } }, { "destKey": "asid", "sourceKeys": "properties.asid", - "required": true, + "required": false, "metadata": { "defaultValue": "" } diff --git a/src/v0/destinations/singular/data/SINGULARAndroidSessionConfig.json b/src/v0/destinations/singular/data/SINGULARAndroidSessionConfig.json index ed3b410f43..e40857c61b 100644 --- a/src/v0/destinations/singular/data/SINGULARAndroidSessionConfig.json +++ b/src/v0/destinations/singular/data/SINGULARAndroidSessionConfig.json @@ -42,7 +42,7 @@ { "destKey": "asid", "sourceKeys": "properties.asid", - "required": true, + "required": false, "metadata": { "defaultValue": "" } @@ -91,12 +91,18 @@ { "destKey": "aifa", "sourceKeys": "context.device.advertisingId", - "required": true + "required": false, + "metadata": { + "defaultValue": "" + } }, { "destKey": "andi", "sourceKeys": "context.device.id", - "required": true + "required": false, + "metadata": { + "defaultValue": "" + } }, { "destKey": "utime", diff --git a/src/v0/destinations/slack/util.js b/src/v0/destinations/slack/util.js index f5d407018b..2267aa0bcd 100644 --- a/src/v0/destinations/slack/util.js +++ b/src/v0/destinations/slack/util.js @@ -83,7 +83,7 @@ const buildDefaultTraitTemplate = (traitsList, traits, template) => { generatedStringFromTemplate += `${trait}: {{"${trait}"}} `; }); // else with all traits - if (traitsList.length === 0) { + if (traitsList.length === 0 && !!traits) { Object.keys(traits).forEach((traitKey) => { generatedStringFromTemplate += `${traitKey}: {{"${traitKey}"}} `; }); diff --git a/src/v0/destinations/snapchat_conversion/config.js b/src/v0/destinations/snapchat_conversion/config.js index e0126ea3b1..1cce713fbb 100644 --- a/src/v0/destinations/snapchat_conversion/config.js +++ b/src/v0/destinations/snapchat_conversion/config.js @@ -55,6 +55,7 @@ const eventNameMapping = { save: 'SAVE', subscribe: 'SUBSCRIBE', complete_tutorial: 'COMPLETE_TUTORIAL', + level_complete: 'LEVEL_COMPLETE', invite: 'INVITE', login: 'LOGIN', share: 'SHARE', diff --git a/src/v0/destinations/snowflake/transform.js b/src/v0/destinations/snowflake/transform.js index 7682d13db3..bf53c57978 100644 --- a/src/v0/destinations/snowflake/transform.js +++ b/src/v0/destinations/snowflake/transform.js @@ -1,10 +1,6 @@ const { processWarehouseMessage } = require('../../../warehouse'); -const snowflake = 'snowflake'; - -function processSingleMessage(message, options) { - return processWarehouseMessage(message, options); -} +const provider = 'snowflake'; function getDataTypeOverride(key, val, options, jsonKey = false) { if (key === 'violationErrors' || jsonKey) { @@ -18,8 +14,7 @@ function process(event) { const whIDResolve = event.request.query.whIDResolve === 'true' || false; const whStoreEvent = event.destination.Config.storeFullEvent === true; const destJsonPaths = event.destination?.Config?.jsonPaths || ''; - const provider = snowflake; - return processSingleMessage(event.message, { + return processWarehouseMessage(event.message, { metadata: event.metadata, whSchemaVersion, whStoreEvent, @@ -28,10 +23,12 @@ function process(event) { provider, sourceCategory: event.metadata ? event.metadata.sourceCategory : null, destJsonPaths, + destConfig: event.destination?.Config, }); } module.exports = { + provider, process, getDataTypeOverride, }; diff --git a/src/v0/destinations/splitio/data/EventConfig.json b/src/v0/destinations/splitio/data/EventConfig.json index d41398a5e2..6435458368 100644 --- a/src/v0/destinations/splitio/data/EventConfig.json +++ b/src/v0/destinations/splitio/data/EventConfig.json @@ -29,5 +29,17 @@ "type": "toFloat" }, "required": false + }, + { + "destKey": "trafficTypeName", + "sourceKeys": [ + "traits.trafficTypeName", + "context.traits.trafficTypeName", + "properties.trafficTypeName" + ], + "metadata": { + "type": "toString" + }, + "required": false } ] diff --git a/src/v0/destinations/splitio/transform.js b/src/v0/destinations/splitio/transform.js index 6641a62fe8..3dd5bea4f2 100644 --- a/src/v0/destinations/splitio/transform.js +++ b/src/v0/destinations/splitio/transform.js @@ -56,6 +56,7 @@ function prepareResponse(message, destination, category) { let outputPayload = {}; + // ref: https://docs.split.io/reference/events-overview outputPayload = constructPayload(message, MAPPING_CONFIG[category.name]); outputPayload.eventTypeId = outputPayload.eventTypeId.replace(/ /g, '_'); if (EVENT_TYPE_ID_REGEX.test(outputPayload.eventTypeId)) { @@ -93,7 +94,12 @@ function prepareResponse(message, destination, category) { if (isDefinedAndNotNullAndNotEmpty(environment)) { outputPayload.environmentName = environment; } - outputPayload.trafficTypeName = trafficType; + + // in case traffic type could not be mapped from the input payloads, falls back to the UI configured default traffic type. + if (!isDefinedAndNotNullAndNotEmpty(outputPayload.trafficTypeName)) { + outputPayload.trafficTypeName = trafficType; + } + outputPayload.properties = removeUndefinedNullValuesAndEmptyObjectArray( flattenJson(bufferProperty), ); diff --git a/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json b/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json index 530d6e392a..2910f1b44c 100644 --- a/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json +++ b/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json @@ -25,10 +25,7 @@ }, { "destKey": "properties.content_type", - "sourceKeys": ["properties.contentType", "properties.content_type"], - "metadata": { - "defaultValue": "product" - } + "sourceKeys": ["properties.contentType", "properties.content_type"] }, { "destKey": "properties.shop_id", diff --git a/src/v0/destinations/tiktok_ads/transform.js b/src/v0/destinations/tiktok_ads/transform.js index ba852b9a97..17a37984c3 100644 --- a/src/v0/destinations/tiktok_ads/transform.js +++ b/src/v0/destinations/tiktok_ads/transform.js @@ -19,6 +19,7 @@ const { getHashFromArrayWithDuplicate, handleRtTfSingleEventError, batchMultiplexedEvents, + validateEventName, } = require('../../util'); const { process: processV2, processRouterDest: processRouterDestV2 } = require('./transformV2'); const { getContents } = require('./util'); @@ -129,12 +130,9 @@ const getTrackResponse = (message, Config, event) => { const trackResponseBuilder = async (message, { Config }) => { const { eventsToStandard, sendCustomEvents } = Config; - if (!message.event || typeof message.event !== 'string') { - throw new InstrumentationError('Either event name is not present or it is not a string'); - } - let event = message.event?.toLowerCase().trim(); + validateEventName(message.event); + let event = message.event.toLowerCase().trim(); const standardEventsMap = getHashFromArrayWithDuplicate(eventsToStandard); - if (!sendCustomEvents && eventNameMapping[event] === undefined && !standardEventsMap[event]) { throw new InstrumentationError( `Event name (${event}) is not valid, must be mapped to one of standard events`, diff --git a/src/v0/destinations/tiktok_ads/transformV2.js b/src/v0/destinations/tiktok_ads/transformV2.js index 48c5b19e64..8760dee52c 100644 --- a/src/v0/destinations/tiktok_ads/transformV2.js +++ b/src/v0/destinations/tiktok_ads/transformV2.js @@ -14,6 +14,7 @@ const { getDestinationExternalID, getHashFromArrayWithDuplicate, handleRtTfSingleEventError, + validateEventName, } = require('../../util'); const { getContents, hashUserField } = require('./util'); const config = require('./config'); @@ -30,7 +31,7 @@ const { JSON_MIME_TYPE } = require('../../util/constant'); * @param {*} event * @returns track payload */ -const getTrackResponsePayload = (message, destConfig, event) => { +const getTrackResponsePayload = (message, destConfig, event, setDefaultForContentType = true) => { const payload = constructPayload(message, trackMappingV2); // if contents is not an array converting it into array @@ -41,6 +42,8 @@ const getTrackResponsePayload = (message, destConfig, event) => { // if contents is not present but we have properties.products present which has fields with superset of contents fields if (!payload.properties?.contents && message.properties?.products) { // retreiving data from products only when contents is not present + // properties object may be empty due which next line may give some error + payload.properties = payload.properties || {}; payload.properties.contents = getContents(message, false); } @@ -52,6 +55,12 @@ const getTrackResponsePayload = (message, destConfig, event) => { if (destConfig.hashUserProperties && isDefinedAndNotNullAndNotEmpty(payload.user)) { payload.user = hashUserField(payload.user); } + // setting content-type default value in case of all standard event except `page-view` + if (!payload.properties?.content_type && setDefaultForContentType) { + // properties object may be empty due which next line may give some error + payload.properties = payload.properties || {}; + payload.properties.content_type = 'product'; + } payload.event = event; // add partner name and return payload return removeUndefinedAndNullValues(payload); @@ -59,7 +68,7 @@ const getTrackResponsePayload = (message, destConfig, event) => { const trackResponseBuilder = async (message, { Config }) => { const { eventsToStandard, sendCustomEvents, accessToken, pixelCode } = Config; - + validateEventName(message.event); let event = message.event?.toLowerCase().trim(); if (!event) { throw new InstrumentationError('Event name is required'); @@ -89,13 +98,17 @@ const trackResponseBuilder = async (message, { Config }) => { }); } }); - } else { + } else if (!eventNameMapping[event]) { /* + Custom Event Case -> if there exists no event mapping we will build payload with custom event recieved For custom event we do not want to lower case the event or trim it we just want to send those as it is Doc https://ads.tiktok.com/help/article/standard-events-parameters?lang=en */ - event = eventNameMapping[event] || message.event; - // if there exists no event mapping we will build payload with custom event recieved + event = message.event; + responseList.push(getTrackResponsePayload(message, Config, event, false)); + } else { + // incoming event name is already a standard event name + event = eventNameMapping[event]; responseList.push(getTrackResponsePayload(message, Config, event)); } // set event source and event_source_id diff --git a/src/v0/destinations/user/data/USERGroupConfig.json b/src/v0/destinations/user/data/USERGroupConfig.json index c87653442d..c31020b68f 100644 --- a/src/v0/destinations/user/data/USERGroupConfig.json +++ b/src/v0/destinations/user/data/USERGroupConfig.json @@ -17,7 +17,7 @@ "required": true }, { - "sourceKeys": "email", + "sourceKeys": "emailOnly", "destKey": "email", "required": false, "sourceFromGenericMap": true diff --git a/src/v0/destinations/user/data/USERIdentifyConfig.json b/src/v0/destinations/user/data/USERIdentifyConfig.json index 2b89c2a3a6..f87bdd01fd 100644 --- a/src/v0/destinations/user/data/USERIdentifyConfig.json +++ b/src/v0/destinations/user/data/USERIdentifyConfig.json @@ -7,7 +7,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "required": false, "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/vero/data/VeroIdentifyConfig.json b/src/v0/destinations/vero/data/VeroIdentifyConfig.json index 8ea456cc1c..8ec589aaed 100644 --- a/src/v0/destinations/vero/data/VeroIdentifyConfig.json +++ b/src/v0/destinations/vero/data/VeroIdentifyConfig.json @@ -5,7 +5,7 @@ "sourceFromGenericMap": true }, { - "sourceKeys": "email", + "sourceKeys": "emailOnly", "destKey": "email", "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/vero/data/VeroTrackConfig.json b/src/v0/destinations/vero/data/VeroTrackConfig.json index a3a23a6e66..04974c5eb2 100644 --- a/src/v0/destinations/vero/data/VeroTrackConfig.json +++ b/src/v0/destinations/vero/data/VeroTrackConfig.json @@ -5,7 +5,7 @@ "sourceFromGenericMap": true }, { - "sourceKeys": "email", + "sourceKeys": "emailOnly", "destKey": "identity.email", "sourceFromGenericMap": true }, diff --git a/src/v0/destinations/webengage/data/WEBENGAGEIdentifyConfig.json b/src/v0/destinations/webengage/data/WEBENGAGEIdentifyConfig.json index a99fc47a3f..4e04703fc0 100644 --- a/src/v0/destinations/webengage/data/WEBENGAGEIdentifyConfig.json +++ b/src/v0/destinations/webengage/data/WEBENGAGEIdentifyConfig.json @@ -55,7 +55,7 @@ }, { "destKey": "email", - "sourceKeys": "email", + "sourceKeys": "emailOnly", "sourceFromGenericMap": true, "required": false }, diff --git a/src/v0/destinations/yahoo_dsp/util.js b/src/v0/destinations/yahoo_dsp/util.js index 255f84d1c9..54002a3bce 100644 --- a/src/v0/destinations/yahoo_dsp/util.js +++ b/src/v0/destinations/yahoo_dsp/util.js @@ -51,7 +51,7 @@ const populateIdentifiers = (audienceList, Config) => { } // here, hashing the data if is not hashed and pushing in the seedList array. if (hashRequired) { - seedList.push(sha256(userTraits[audienceAttribute])); + seedList.push(sha256(userTraits[audienceAttribute].trim())); } else { seedList.push(userTraits[audienceAttribute]); } diff --git a/src/v0/destinations/zendesk/transform.js b/src/v0/destinations/zendesk/transform.js index 5862014784..cadb1d3964 100644 --- a/src/v0/destinations/zendesk/transform.js +++ b/src/v0/destinations/zendesk/transform.js @@ -4,6 +4,7 @@ const { NetworkInstrumentationError, InstrumentationError, NetworkError, + isDefinedAndNotNull, } = require('@rudderstack/integrations-lib'); const myAxios = require('../../../util/myAxios'); @@ -405,6 +406,9 @@ async function getUserMembershipPayload(message, headers, orgId, destinationConf } async function createOrganization(message, category, headers, destinationConfig, baseEndpoint) { + if (!isDefinedAndNotNull(message.traits)) { + throw new InstrumentationError('Organisation Traits are missing. Aborting.'); + } await checkAndCreateUserFields( message.traits, category.organizationFieldsEndpoint, diff --git a/src/v0/sources/adjust/config.ts b/src/v0/sources/adjust/config.ts new file mode 100644 index 0000000000..d1c6ab8242 --- /dev/null +++ b/src/v0/sources/adjust/config.ts @@ -0,0 +1,16 @@ +export const excludedFieldList = [ + 'activity_kind', + 'event', + 'event_name', + 'gps_adid', + 'idfa', + 'idfv', + 'adid', + 'tracker', + 'tracker_name', + 'app_name', + 'ip_address', + 'tracking_enabled', + 'tracker_token', + 'created_at', +]; diff --git a/src/v0/sources/adjust/mapping.json b/src/v0/sources/adjust/mapping.json new file mode 100644 index 0000000000..60ea66281e --- /dev/null +++ b/src/v0/sources/adjust/mapping.json @@ -0,0 +1,52 @@ +[ + { + "sourceKeys": "activity_kind", + "destKeys": "properties.activity_kind" + }, + { + "sourceKeys": "event", + "destKeys": "properties.event_token" + }, + { + "sourceKeys": "event_name", + "destKeys": "event" + }, + { + "sourceKeys": "gps_adid", + "destKeys": "properties.gps_adid" + }, + { + "sourceKeys": "idfa", + "destKeys": "context.device.advertisingId" + }, + { + "sourceKeys": "idfv", + "destKeys": "context.device.id" + }, + { + "sourceKeys": "adid", + "destKeys": "context.device.id " + }, + { + "sourceKeys": "tracker", + "destKeys": "properties.tracker" + }, + { + "sourceKeys": "tracker_name", + "destKeys": "properties.tracker_name" + }, + { "sourceKeys": "tracker_token", "destKeys": "properties.tracker_token" }, + + { + "sourceKeys": "app_name", + "destKeys": "context.app.name" + }, + { + "sourceKeys": "ip_address", + "destKeys": ["context.ip", "request_ip"] + }, + { + "sourceKeys": "tracking_enabled", + "destKeys": "properties.tracking_enabled" + } +] diff --git a/src/v0/sources/adjust/transform.js b/src/v0/sources/adjust/transform.js new file mode 100644 index 0000000000..9da90751b7 --- /dev/null +++ b/src/v0/sources/adjust/transform.js @@ -0,0 +1,62 @@ +const lodash = require('lodash'); +const path = require('path'); +const fs = require('fs'); +const { TransformationError } = require('@rudderstack/integrations-lib'); +const logger = require('../../../logger'); +const Message = require('../message'); +const { CommonUtils } = require('../../../util/common'); +const { excludedFieldList } = require('./config'); +const { extractCustomFields, generateUUID } = require('../../util'); + +// ref : https://help.adjust.com/en/article/global-callbacks#general-recommended-placeholders +// import mapping json using JSON.parse to preserve object key order +const mapping = JSON.parse(fs.readFileSync(path.resolve(__dirname, './mapping.json'), 'utf-8')); + +const formatProperties = (input) => { + const { query_parameters: qParams } = input; + logger.debug(`[Adjust] Input event: query_params: ${JSON.stringify(qParams)}`); + if (!qParams) { + throw new TransformationError('Query_parameters is missing'); + } + const formattedOutput = {}; + Object.entries(qParams).forEach(([key, [value]]) => { + formattedOutput[key] = value; + }); + return formattedOutput; +}; + +const processEvent = (inputEvent) => { + const message = new Message(`Adjust`); + const event = lodash.cloneDeep(inputEvent); + const formattedPayload = formatProperties(event); + // event type is always track + const eventType = 'track'; + message.setEventType(eventType); + message.setPropertiesV2(formattedPayload, mapping); + let customProperties = {}; + customProperties = extractCustomFields( + formattedPayload, + customProperties, + 'root', + excludedFieldList, + ); + message.properties = { ...message.properties, ...customProperties }; + + if (formattedPayload.created_at) { + const ts = new Date(formattedPayload.created_at * 1000).toISOString(); + message.setProperty('originalTimestamp', ts); + message.setProperty('timestamp', ts); + } + + // adjust does not has the concept of user but we need to set some random anonymousId in order to make the server accept the message + message.anonymousId = generateUUID(); + return message; +}; + +// This fucntion just converts the incoming payload to array of already not and sends it to processEvent +const process = (events) => { + const eventsArray = CommonUtils.toArray(events); + return eventsArray.map(processEvent); +}; + +module.exports = { process }; diff --git a/src/v0/sources/auth0/mapping.json b/src/v0/sources/auth0/mapping.json index 45dcf939ad..dcbd389945 100644 --- a/src/v0/sources/auth0/mapping.json +++ b/src/v0/sources/auth0/mapping.json @@ -62,5 +62,9 @@ { "sourceKeys": "date", "destKeys": ["originalTimestamp", "sentAt"] + }, + { + "sourceKeys": "type", + "destKeys": "properties.source_type" } ] diff --git a/src/v0/sources/auth0/transform.js b/src/v0/sources/auth0/transform.js index 4b78621418..5a1bf42e28 100644 --- a/src/v0/sources/auth0/transform.js +++ b/src/v0/sources/auth0/transform.js @@ -1,11 +1,11 @@ const path = require('path'); const fs = require('fs'); -const { InstrumentationError } = require('@rudderstack/integrations-lib'); const { removeUndefinedAndNullValues } = require('../../util'); const { getGroupId } = require('./util'); // import mapping json using JSON.parse to preserve object key order const mapping = JSON.parse(fs.readFileSync(path.resolve(__dirname, './mapping.json'), 'utf-8')); const Message = require('../message'); +const { generateUUID } = require('../../util'); // Ref: https://auth0.com/docs/logs/references/log-event-type-codes const eventNameMap = JSON.parse( @@ -59,11 +59,11 @@ function processEvents(eventList) { } else { response = prepareTrackPayload(data); } - if (response?.userId) { - // eslint-disable-next-line camelcase - response.properties.log_id = log_id; - responses.push(removeUndefinedAndNullValues(response)); - } + + // eslint-disable-next-line camelcase + response.properties.log_id = log_id; + response.anonymousId = generateUUID(); + responses.push(removeUndefinedAndNullValues(response)); } }); return responses; @@ -74,11 +74,7 @@ function process(events) { if (!Array.isArray(events)) { eventList = events.logs || [events]; } - const responses = processEvents(eventList); - if (responses.length === 0) { - throw new InstrumentationError('UserId is not present'); - } - return responses; + return processEvents(eventList); } exports.process = process; diff --git a/src/v0/sources/canny/transform.js b/src/v0/sources/canny/transform.js index 9188f5ac34..aad5a881c1 100644 --- a/src/v0/sources/canny/transform.js +++ b/src/v0/sources/canny/transform.js @@ -2,7 +2,7 @@ const sha256 = require('sha256'); const { TransformationError } = require('@rudderstack/integrations-lib'); const Message = require('../message'); const { voterMapping, authorMapping, checkForRequiredFields } = require('./util'); -const { logger } = require('../../../logger'); +const logger = require('../../../logger'); const CannyOperation = { VOTE_CREATED: 'vote.created', diff --git a/src/v0/sources/revenuecat/transform.js b/src/v0/sources/revenuecat/transform.js index 36944e10fa..c746f13b1a 100644 --- a/src/v0/sources/revenuecat/transform.js +++ b/src/v0/sources/revenuecat/transform.js @@ -1,6 +1,8 @@ const { camelCase } = require('lodash'); const moment = require('moment'); -const { removeUndefinedAndNullValues, isDefinedAndNotNull } = require('../../util'); +const { isDefinedAndNotNullAndNotEmpty } = require('@rudderstack/integrations-lib'); +const { removeUndefinedAndNullValues, isDefinedAndNotNull, generateUUID } = require('../../util'); + const Message = require('../message'); function process(event) { @@ -9,6 +11,11 @@ function process(event) { // we are setting event type as track always message.setEventType('track'); + message.userId = event?.event?.app_user_id || event?.event?.original_app_user_id || ''; + if (!isDefinedAndNotNullAndNotEmpty(message.userId)) { + message.anonymousId = generateUUID(); + } + const properties = {}; // dump all event properties to message.properties after converting them to camelCase if (event.event) { diff --git a/src/v0/sources/shopify/shopify_redis.util.test.js b/src/v0/sources/shopify/shopify_redis.util.test.js index db596e1dfb..fb99837932 100644 --- a/src/v0/sources/shopify/shopify_redis.util.test.js +++ b/src/v0/sources/shopify/shopify_redis.util.test.js @@ -1,5 +1,9 @@ const { getAnonymousIdAndSessionId, checkAndUpdateCartItems } = require('./util'); jest.mock('ioredis', () => require('../../../../test/__mocks__/redis')); +const metricMetadata = { + writeKey: 'dummyKey', + source: 'src', +}; describe('Shopify Utils Test', () => { describe('Check for valid cart update event test cases', () => { it('Event containing token and nothing is retreived from redis and less than req. time difference between created_at and uadated_at', async () => { @@ -14,7 +18,7 @@ describe('Shopify Utils Test', () => { created_at: '2023-02-10T12:05:04.402Z', }; const expectedOutput = false; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); it('Event containing token and nothing is retreived from redis', async () => { @@ -28,7 +32,7 @@ describe('Shopify Utils Test', () => { ], }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -44,7 +48,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -60,7 +64,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = false; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); @@ -76,7 +80,7 @@ describe('Shopify Utils Test', () => { }; const expectedOutput = true; - const output = await checkAndUpdateCartItems(input); + const output = await checkAndUpdateCartItems(input, null, metricMetadata); expect(output).toEqual(expectedOutput); }); }); diff --git a/src/v0/sources/shopify/transform.js b/src/v0/sources/shopify/transform.js index 013580d7a3..bc2135d215 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ const lodash = require('lodash'); const get = require('get-value'); +const { RedisError } = require('@rudderstack/integrations-lib'); const stats = require('../../../util/stats'); const { getShopifyTopic, @@ -12,10 +13,10 @@ const { getHashLineItems, getDataFromRedis, } = require('./util'); +const logger = require('../../../logger'); const { RedisDB } = require('../../../util/redis/redisConnector'); const { removeUndefinedAndNullValues, isDefinedAndNotNull } = require('../../util'); const Message = require('../message'); -const logger = require('../../../logger'); const { EventType } = require('../../../constants'); const { INTEGERATION, @@ -143,7 +144,7 @@ const processEvent = async (inputEvent, metricMetadata) => { break; case 'carts_update': if (useRedisDatabase) { - redisData = await getDataFromRedis(event.id || event.token); + redisData = await getDataFromRedis(event.id || event.token, metricMetadata); const isValidEvent = await checkAndUpdateCartItems(inputEvent, redisData, metricMetadata); if (!isValidEvent) { return NO_OPERATION_SUCCESS; @@ -154,8 +155,9 @@ const processEvent = async (inputEvent, metricMetadata) => { default: if (!SUPPORTED_TRACK_EVENTS.includes(shopifyTopic)) { stats.increment('invalid_shopify_event', { - event: shopifyTopic, - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); return NO_OPERATION_SUCCESS; } @@ -215,7 +217,8 @@ const processIdentifierEvent = async (event, metricMetadata) => { stats.increment('shopify_redis_calls', { type: 'set', field: 'itemsHash', - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); /* cart_token: { anonymousId: 'anon_id1', @@ -236,25 +239,26 @@ const processIdentifierEvent = async (event, metricMetadata) => { stats.increment('shopify_redis_calls', { type: 'set', field, - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); await RedisDB.setVal(`${event.cartToken}`, value); } catch (e) { - logger.debug(`{{SHOPIFY::}} cartToken map set call Failed due redis error ${e}`); + logger.debug(`{{SHOPIFY::}} cartToken map set call Failed due redis error ${e}`, { + type: 'set', + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, + }); stats.increment('shopify_redis_failures', { type: 'set', - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); + // returning 500 as status code in case of redis failure + throw new RedisError(`${e}`, 500); } } - const result = { - outputToSource: { - body: Buffer.from('OK').toString('base64'), - contentType: 'text/plain', - }, - statusCode: 200, - }; - return result; + return NO_OPERATION_SUCCESS; }; const process = async (event) => { const metricMetadata = { diff --git a/src/v0/sources/shopify/util.js b/src/v0/sources/shopify/util.js index 6f31ade4a7..6aea0d19bd 100644 --- a/src/v0/sources/shopify/util.js +++ b/src/v0/sources/shopify/util.js @@ -12,7 +12,6 @@ const { isDefinedAndNotNull, } = require('../../util'); const { RedisDB } = require('../../../util/redis/redisConnector'); -const logger = require('../../../logger'); const { lineItemsMappingJSON, productMappingJSON, @@ -23,13 +22,15 @@ const { useRedisDatabase, maxTimeToIdentifyRSGeneratedCall, } = require('./config'); +const logger = require('../../../logger'); const getDataFromRedis = async (key, metricMetadata) => { try { stats.increment('shopify_redis_calls', { type: 'get', field: 'all', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); const redisData = await RedisDB.getVal(key); if ( @@ -37,7 +38,8 @@ const getDataFromRedis = async (key, metricMetadata) => { (typeof redisData === 'object' && Object.keys(redisData).length === 0) ) { stats.increment('shopify_redis_no_val', { - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } return redisData; @@ -45,7 +47,8 @@ const getDataFromRedis = async (key, metricMetadata) => { logger.debug(`{{SHOPIFY::}} Get call Failed due redis error ${e}`); stats.increment('shopify_redis_failures', { type: 'get', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } return null; @@ -166,7 +169,9 @@ const getAnonymousIdAndSessionId = async (message, metricMetadata, redisData = n if (isDefinedAndNotNull(anonymousId) && isDefinedAndNotNull(sessionId)) { stats.increment('shopify_anon_id_resolve', { method: 'note_attributes', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); return { anonymousId, sessionId }; } @@ -198,7 +203,9 @@ const getAnonymousIdAndSessionId = async (message, metricMetadata, redisData = n // and for how many stats.increment('shopify_anon_id_resolve', { method: 'database', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); } return { anonymousId, sessionId }; @@ -215,14 +222,16 @@ const updateCartItemsInRedis = async (cartToken, newCartItemsHash, metricMetadat stats.increment('shopify_redis_calls', { type: 'set', field: 'itemsHash', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); await RedisDB.setVal(`${cartToken}`, value); } catch (e) { logger.debug(`{{SHOPIFY::}} itemsHash set call Failed due redis error ${e}`); stats.increment('shopify_redis_failures', { type: 'set', - ...metricMetadata, + writeKey: metricMetadata.writeKey, + source: metricMetadata.source, }); } }; diff --git a/src/v0/sources/slack/mapping.json b/src/v0/sources/slack/mapping.json new file mode 100644 index 0000000000..f7825bd88d --- /dev/null +++ b/src/v0/sources/slack/mapping.json @@ -0,0 +1,50 @@ +[ + { + "sourceKeys": "event.type", + "destKeys": "event" + }, + { + "sourceKeys": "event.user.tz", + "destKeys": "timezone" + }, + { + "sourceKeys": "event.user.profile.email", + "destKeys": "context.traits.email" + }, + { + "sourceKeys": "event.user.profile.phone", + "destKeys": "context.traits.phone" + }, + { + "sourceKeys": "event.user.profile.real_name_normalized", + "destKeys": "context.traits.name" + }, + { + "sourceKeys": "event.user.profile.real_name", + "destKeys": "context.traits.name" + }, + { + "sourceKeys": "event.user.profile.display_name_normalized", + "destKeys": "context.traits.name" + }, + { + "sourceKeys": "event.user.profile.display_name", + "destKeys": "context.traits.name" + }, + { + "sourceKeys": "event.user.profile.first_name", + "destKeys": "context.traits.firstName" + }, + { + "sourceKeys": "event.user.profile.last_name", + "destKeys": "context.traits.lastName" + }, + { + "sourceKeys": "event.user.profile.image_original", + "destKeys": "context.traits.avatar" + }, + { + "sourceKeys": "event.user.profile.title", + "destKeys": "context.traits.title" + } +] diff --git a/src/v0/sources/slack/transform.js b/src/v0/sources/slack/transform.js new file mode 100644 index 0000000000..98324a7b65 --- /dev/null +++ b/src/v0/sources/slack/transform.js @@ -0,0 +1,110 @@ +const sha256 = require('sha256'); +const { TransformationError } = require('@rudderstack/integrations-lib'); +const Message = require('../message'); +const { mapping, tsToISODate, normalizeEventName } = require('./util'); +const { generateUUID, removeUndefinedAndNullValues } = require('../../util'); +const { JSON_MIME_TYPE } = require('../../util/constant'); +const { EventType } = require('../../../constants'); + +/** + * Transform event data to RudderStack supported standard event schema + * @param {Object} slackPayload - The complete data received on the webhook from Slack + * @param {Object} slackPayload.event - The data object specific to the Slack event received. Has different schema for different event types. + * @returns {Object} Event data transformed to RudderStack supported standard event schema + */ +function processNormalEvent(slackPayload) { + const message = new Message(`SLACK`); + if (!slackPayload?.event) { + throw new TransformationError('Missing the required event data'); + } + switch (slackPayload.event.type) { + case 'team_join': + message.setEventType(EventType.IDENTIFY); + break; + case 'user_change': + message.setEventType(EventType.IDENTIFY); + break; + default: + message.setEventType(EventType.TRACK); + break; + } + message.setEventName(normalizeEventName(slackPayload.event.type)); + if (!slackPayload.event.user) { + throw new TransformationError('UserId not found'); + } + const stringifiedUserId = + typeof slackPayload.event.user === 'object' + ? slackPayload.event.user.id + : slackPayload.event.user; + message.setProperty( + 'anonymousId', + stringifiedUserId ? sha256(stringifiedUserId).toString().substring(0, 36) : generateUUID(), + ); + // Set the user id received from Slack into externalId + message.context.externalId = [ + { + type: 'slackUserId', + id: stringifiedUserId, + }, + ]; + // Set the standard common event fields. More info at https://www.rudderstack.com/docs/event-spec/standard-events/common-fields/ + // originalTimestamp - The actual time (in UTC) when the event occurred + message.setProperty( + 'originalTimestamp', + tsToISODate(slackPayload.event.ts || slackPayload.event.event_ts || slackPayload.event_time), + ); + // sentAt - Time, client-side, when the event was sent from the client to RudderStack + message.setProperty('sentAt', tsToISODate(slackPayload.event_time)); + // Map the remaining standard event properties according to mappings for the payload properties + message.setPropertiesV2(slackPayload, mapping); + // Copy the complete Slack event payload to message.properties + if (!message.properties) message.properties = {}; + Object.assign(message.properties, slackPayload.event); + return message; +} + +/** + * Handles a special event for webhook url verification. + * Responds back with the challenge key received in the request. + * Reference - https://api.slack.com/apis/connections/events-api#subscribing + * @param {Object} event - Event data received from Slack + * @param {string} event.challenge - The challenge key received in the request + * @returns response that needs to be sent back to the source, alongwith the same challenge key received int the request + */ +function processUrlVerificationEvent(event) { + const response = { challenge: event?.challenge }; + return { + outputToSource: { + body: Buffer.from(JSON.stringify(response)).toString('base64'), + contentType: JSON_MIME_TYPE, + }, + statusCode: 200, + }; +} + +/** + * Checks if the event is a special url verification event or not. + * Slack sends this event at the time of webhook setup to verify webhook url ownership for the security purpose. + * Reference - https://api.slack.com/apis/connections/events-api#subscribing + * @param {Object} event - Event data received from Slack + * @param {string} event.challenge - The challenge key received in the request + * @param {string} event.type - The type of Slack event. `url_verification` when it is a special webhook url verification event. + * @returns {boolean} true if it is a valid challenge event for url verification event + */ +function isWebhookUrlVerificationEvent(event) { + return event?.type === 'url_verification' && !!event?.challenge; +} + +/** + * Processes the event with needed transformation and sends back the response + * Reference - https://api.slack.com/apis/connections/events-api + * @param {Object} event + */ +function process(event) { + const response = isWebhookUrlVerificationEvent(event) + ? processUrlVerificationEvent(event) + : processNormalEvent(event); + return removeUndefinedAndNullValues(response); +} + +exports.process = process; diff --git a/src/v0/sources/slack/util.js b/src/v0/sources/slack/util.js new file mode 100644 index 0000000000..b9c39db223 --- /dev/null +++ b/src/v0/sources/slack/util.js @@ -0,0 +1,62 @@ +/* eslint-disable no-restricted-syntax */ +const path = require('path'); +const fs = require('fs'); + +const mapping = JSON.parse(fs.readFileSync(path.resolve(__dirname, './mapping.json'), 'utf-8')); + +/** + * Converts a Slack timestamp to RudderStack's standard timestamp format - ISO 8601 date string. + * The Slack timestamp is a string that represents unix timestamp (seconds since the Unix Epoch) + * with fractional seconds for millisecond precision. + * If the timestamp is not provided, the function returns the current date and time in ISO 8601 format. + * + * @param {string} [slackTs] - The Slack timestamp to be converted. + * @returns {string} The ISO 8601 formatted date string corresponding to the given Slack timestamp + * or the current date and time if no timestamp is provided. + * + * @example + * // Convert a Slack timestamp to an ISO 8601 date string + * const slackTimestamp = "1609459200.123000"; + * const isoDate = tsToISODate(slackTimestamp); + * console.log(isoDate); // Output: "2021-01-01T00:00:00.123Z" (depending on your timezone) + */ +function tsToISODate(slackTs) { + // Default to current date if slackTs is not provided + if (!slackTs) return new Date().toISOString(); + + // Convert slackTs string into unix timestamp in milliseconds + const msTimestamp = parseFloat(slackTs) * 1000; + // Convert to a date object + if (Number.isNaN(msTimestamp)) { + // If timestamp was not a valid float, the parser will return NaN, stop processing the timestamp further and return null + return null; + } + const date = new Date(msTimestamp); + + // Return the date in ISO 8601 format + return date.toISOString(); +} + +/** + * Converts an event name from snake_case to a RudderStack format - space-separated string with each word capitalized. + * @param {string} evtName - The event name in snake_case format to be normalized. + * @returns {string} The normalized event name with spaces between words and each word capitalized. + * + * @example + * // Convert a slack event name to RudderStack format + * const eventName = "member_joined_channel"; + * const normalizedEventName = normalizeEventName(eventName); + * console.log(normalizedEventName); // Output: "Member Joined Channel" + */ +function normalizeEventName(evtName) { + try { + return evtName + .split('_') + .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) + .join(' '); + } catch (e) { + return 'undefined'; + } +} + +module.exports = { mapping, tsToISODate, normalizeEventName }; diff --git a/src/v0/sources/slack/util.test.js b/src/v0/sources/slack/util.test.js new file mode 100644 index 0000000000..b83f22f058 --- /dev/null +++ b/src/v0/sources/slack/util.test.js @@ -0,0 +1,51 @@ +const { tsToISODate, normalizeEventName } = require('./util.js'); + +describe('Unit test cases for tsToISODate', () => { + it('should return a valid iso date string for a valid slack timestamp input', () => { + const result = tsToISODate('1609459200.123000'); + expect(result).toBe('2021-01-01T00:00:00.123Z'); + }); + + it('should return iso date string of today when slack timestamp argument is not provided', () => { + const result = tsToISODate(); + expect(result).not.toBeNull(); + expect(typeof result).toBe('string'); + expect(result).not.toHaveLength(0); + // Check if the result is a valid date + const dateObject = new Date(result); + const resultTime = dateObject.getTime(); + expect(resultTime).not.toBeNaN(); + // Check if the result is close to the current time with precision tolerance of upto a minute + const nowTime = new Date().getTime(); + const TOLERANCE = 60000; // In ms + const timeDiff = Math.abs(nowTime - resultTime); + expect(timeDiff).toBeLessThanOrEqual(TOLERANCE); + }); + + it('should return null if the slack timestamp argument is invalid', () => { + const result = tsToISODate('invalid.slack.timestamp'); + expect(result).toBeNull(); + }); +}); + +describe('Unit test cases for normalizeEventName', () => { + it('should normalize a valid snake case string "member_joined_channel" to RudderStack format "Member Joined Channel"', () => { + const result = normalizeEventName('member_joined_channel'); + expect(result).toBe('Member Joined Channel'); + }); + + it('should return undefined string when event name is undefined', () => { + const result = normalizeEventName(undefined); + expect(result).toBe('undefined'); + }); + + it('should return undefined string when event name is null', () => { + const result = normalizeEventName(null); + expect(result).toBe('undefined'); + }); + + it('should return undefined string when event name argument cannot be parsed to string', () => { + const result = normalizeEventName({}); + expect(result).toBe('undefined'); + }); +}); diff --git a/src/v0/util/data/GenericFieldMapping.json b/src/v0/util/data/GenericFieldMapping.json index 87dd5e5e55..b903e6587d 100644 --- a/src/v0/util/data/GenericFieldMapping.json +++ b/src/v0/util/data/GenericFieldMapping.json @@ -72,12 +72,16 @@ "traits.DOB", "context.traits.DOB" ], - "state": ["traits.state", "context.traits.state"], "country": ["traits.country", "context.traits.country"], "region": ["traits.region", "context.traits.region"], "city": ["traits.address.city", "context.traits.address.city"], - + "street": [ + "traits.street", + "traits.address.street", + "context.traits.street", + "context.traits.address.street" + ], "avatar": [ "traits.avatar", "context.traits.avatar", @@ -116,5 +120,11 @@ "context.traits.address.postal_code", "context.traits.address.postalCode" ], - "sessionId": ["session_id", "context.sessionId"] + "sessionId": ["session_id", "context.sessionId"], + "countryCode": [ + "traits.countryCode", + "traits.address.countryCode", + "context.traits.address.countryCode", + "context.traits.countryCode" + ] } diff --git a/src/v0/util/deleteUserUtils.js b/src/v0/util/deleteUserUtils.js index 6cf16d7f9e..22b5ba6a81 100644 --- a/src/v0/util/deleteUserUtils.js +++ b/src/v0/util/deleteUserUtils.js @@ -18,4 +18,16 @@ const getUserIdBatches = (userAttributes, MAX_BATCH_SIZE) => { return userIdBatches; }; -module.exports = { getUserIdBatches }; +const getCustomIdBatches = (userAttributes, customIdentifier, MAX_BATCH_SIZE) => { + const identifierArray = []; + userAttributes.forEach((userAttribute) => { + // Dropping the user if customIdentifier is not present + if (userAttribute[customIdentifier]) { + identifierArray.push(userAttribute[customIdentifier]); + } + }); + const identifierBatches = lodash.chunk(identifierArray, MAX_BATCH_SIZE); + return identifierBatches; +}; + +module.exports = { getUserIdBatches, getCustomIdBatches }; diff --git a/src/v0/util/facebookUtils/index.js b/src/v0/util/facebookUtils/index.js index 7fa1e898fe..7462320cca 100644 --- a/src/v0/util/facebookUtils/index.js +++ b/src/v0/util/facebookUtils/index.js @@ -292,10 +292,17 @@ const formingFinalResponse = ( throw new TransformationError('Payload could not be constructed'); }; +const isHtmlFormat = (string) => { + const htmlTags = /<(?!(!doctype\s*html|html))\b[^>]*>[\S\s]*?<\/[^>]*>/i; + return htmlTags.test(string); +}; + module.exports = { + isHtmlFormat, getContentType, getContentCategory, transformedPayloadData, formingFinalResponse, fetchUserData, + deduceFbcParam, }; diff --git a/src/v0/util/facebookUtils/index.test.js b/src/v0/util/facebookUtils/index.test.js index 98e4ccec40..1a2de4ed12 100644 --- a/src/v0/util/facebookUtils/index.test.js +++ b/src/v0/util/facebookUtils/index.test.js @@ -1,5 +1,12 @@ -const { transformedPayloadData } = require('./index'); +const { + transformedPayloadData, + fetchUserData, + deduceFbcParam, + getContentType, + isHtmlFormat, +} = require('./index'); const sha256 = require('sha256'); +const { MAPPING_CONFIG, CONFIG_CATEGORIES } = require('../../destinations/facebook_pixel/config'); describe('transformedPayloadData_function', () => { // Tests with default values for all parameters @@ -301,3 +308,385 @@ describe('transformedPayloadData_function', () => { expect(result).toEqual({}); }); }); + +describe('deduceFbcParam', () => { + // Should return undefined if message.context.page.url is undefined + it('should return undefined when message.context.page.url is undefined', () => { + const message = {}; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return undefined if URL constructor throws an error + it('should return undefined when URL constructor throws an error', () => { + const message = { + context: { + page: { + url: 'invalid-url', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return undefined if fbclid is undefined + it('should return undefined when fbclid is undefined', () => { + const message = { + context: { + page: { + url: 'https://example.com', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty context object + it('should handle message with empty context object', () => { + const message = { + context: {}, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty page object + it('should handle message with empty page object', () => { + const message = { + context: { + page: {}, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should handle message with empty url string + it('should handle message with empty url string', () => { + const message = { + context: { + page: { + url: '', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toBeUndefined(); + }); + + // Should return fbc parameter when all conditions are met + it('should return fbc parameter when all conditions are met', () => { + const message = { + context: { + page: { + url: 'https://example.com?fbclid=123456', + }, + }, + }; + const result = deduceFbcParam(message); + expect(result).toEqual(expect.stringContaining('fb.1.')); + }); +}); + +describe('fetchUserData', () => { + const message = { + channel: 'web', + context: { + traits: { + name: 'Rudder Test', + email: 'abc@gmail.com', + firstname: 'Rudder', + lastname: 'Test', + phone: 9000000000, + gender: 'female', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + properties: { + plan: 'standard plan', + name: 'rudder test', + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + originalTimestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '123456', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }; + + const Config = { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }; + + // Returns a valid user data object when given valid inputs. + it('should return a valid user data object when given valid inputs without integrations object', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = 'fb_pixel'; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + external_id: '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + fbc: undefined, + }); + }); + + it('should return a valid user data object when given valid inputs with integrations object', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = 'fb_pixel'; + message.integrations.FacebookPixel = { hashed: true }; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + em: 'abc@gmail.com', + external_id: '123456', + ph: '9000000000', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: 'Test', + fn: 'Rudder', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + fbc: undefined, + }); + }); + + it('should return null when mappingJson is undefined', () => { + const mappingJson = undefined; + const destinationName = 'fb_pixel'; + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toBeNull(); + }); + + it('should return hashed data when destinationName is undefined', () => { + const mappingJson = MAPPING_CONFIG[CONFIG_CATEGORIES.USERDATA.name]; + const destinationName = undefined; + + const result = fetchUserData(message, Config, mappingJson, destinationName); + + expect(result).toEqual({ + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + external_id: '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + fbc: undefined, + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + }); + }); +}); + +describe('getContentType', () => { + // Returns default value when no category or categoryToContent is provided + it('should return default value when no category or categoryToContent is provided', () => { + const message = { + properties: { + produtcs: [ + { + product_id: '123', + }, + ], + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); + + // Returns default value when categoryToContent is not an array + it('should return default value when categoryToContent is not an array', () => { + const message = { + properties: { + products: [ + { + product_id: '123', + }, + ], + }, + }; + const defaultValue = 'product'; + const categoryToContent = 'not an array'; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); + + // Returns categoryToContent value when category is provided and matches with categoryToContent + it('should return categoryToContent value when category is provided and matches with categoryToContent', () => { + const message = { + properties: { + category: 'clothing', + }, + }; + const defaultValue = 'product'; + const categoryToContent = [{ from: 'clothing', to: 'garments' }]; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(categoryToContent[0].to); + }); + + // Returns integrationsObj.contentType when it exists + it('should return integrationsObj.contentType when it exists', () => { + const message = { + properties: { + products: [ + { + product_id: '123', + }, + ], + }, + integrations: { + fb_pixel: { + contentType: 'content_type_value', + }, + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + const integrationsObj = { + contentType: 'content_type_value', + }; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(integrationsObj.contentType); + }); + + // Returns 'product' when category is 'clothing' and categoryToContent is not provided + it("should return 'product' when category is 'clothing' and categoryToContent is not provided", () => { + const message = { + properties: { + category: 'clothing', + }, + }; + const defaultValue = 'product'; + const categoryToContent = []; + const destinationName = 'fb_pixel'; + + const result = getContentType(message, defaultValue, categoryToContent, destinationName); + + expect(result).toBe(defaultValue); + }); +}); + +describe('isHtmlFormat', () => { + it('should return false for Json', () => { + expect(isHtmlFormat('{"a": 1, "b":2}')).toBe(false); + }); + + it('should return false for empty Json', () => { + expect(isHtmlFormat('{}')).toBe(false); + }); + + it('should return false for undefined', () => { + expect(isHtmlFormat(undefined)).toBe(false); + }); + + it('should return false for null', () => { + expect(isHtmlFormat(null)).toBe(false); + }); + + it('should return false for empty array', () => { + expect(isHtmlFormat([])).toBe(false); + }); + + it('should return true for html doctype', () => { + expect( + isHtmlFormat( + '

Sorry, something went wrong.

We\'re working on it and we\'ll get it fixed as soon as we can.

Go Back

', + ), + ).toBe(true); + }); + + it('should return true for html', () => { + expect( + isHtmlFormat( + ' Hello, World!

Hello World!

', + ), + ).toBe(true); + }); + + it('should return true for html', () => { + expect( + isHtmlFormat( + '

Hello World!

', + ), + ).toBe(true); + }); + + it('should return false json type', () => { + expect(isHtmlFormat('{"": 12, "b": "test, "arr": [1,2]}')).toBe(false); + }); +}); diff --git a/src/v0/util/facebookUtils/networkHandler.js b/src/v0/util/facebookUtils/networkHandler.js index d5a731067f..5338364fe2 100644 --- a/src/v0/util/facebookUtils/networkHandler.js +++ b/src/v0/util/facebookUtils/networkHandler.js @@ -14,6 +14,7 @@ const { } = require('../../../adapters/utils/networkUtils'); const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); const { ErrorDetailsExtractorBuilder } = require('../../../util/error-extractor'); +const { isHtmlFormat } = require('./index'); /** * Only under below mentioned scenario(s), add the errorCodes, subCodes etc,. to this map @@ -84,10 +85,7 @@ const errorDetailsMap = { "Object with ID 'PIXEL_ID' / 'DATASET_ID' / 'AUDIENCE_ID' does not exist, cannot be loaded due to missing permissions, or does not support this operation", ) .build(), - default: new ErrorDetailsExtractorBuilder() - .setStatus(400) - .setMessage('Invalid Parameter') - .build(), + default: new ErrorDetailsExtractorBuilder().setStatus(400).setMessageField('message').build(), }, 1: { // An unknown error occurred. @@ -277,6 +275,18 @@ const errorResponseHandler = (destResponse) => { const destResponseHandler = (responseParams) => { const { destinationResponse } = responseParams; + + // check If the response is in html format + if (isHtmlFormat(destinationResponse.response) || isHtmlFormat(destinationResponse)) { + throw new NetworkError( + 'Invalid response format (HTML) during response transformation', + 500, + { + [TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(destinationResponse.status), + }, + destinationResponse, + ); + } errorResponseHandler(destinationResponse); return { destinationResponse: destinationResponse.response, diff --git a/src/v0/util/googleUtils/index.js b/src/v0/util/googleUtils/index.js index de73b0fb05..ef7c244c17 100644 --- a/src/v0/util/googleUtils/index.js +++ b/src/v0/util/googleUtils/index.js @@ -1,36 +1,118 @@ const GOOGLE_ALLOWED_CONSENT_STATUS = ['UNSPECIFIED', 'UNKNOWN', 'GRANTED', 'DENIED']; +const GA4_ALLOWED_CONSENT_STATUS = ['GRANTED', 'DENIED']; + +const UNSPECIFIED_CONSENT = 'UNSPECIFIED'; +const UNKNOWN_CONSENT = 'UNKNOWN'; /** - * Populates the consent object based on the provided properties. + * Populates the consent object based on the provided configuration and consent mapping. * - * @param {object} properties - message.properties containing properties related to consent. - * @returns {object} - An object containing consent information. - * ref : https://developers.google.com/google-ads/api/rest/reference/rest/v15/Consent + * @param {Object} config - The configuration object containing consent values. + * @param {Object} consentConfigMap - The mapping of consent keys to consent types. + * @returns {Object} - The consent object populated with consent values based on the configuration. + * * ref : https://developers.google.com/google-ads/api/rest/reference/rest/v16/Consent */ - -const populateConsentForGoogleDestinations = (config) => { +const populateConsentFromConfig = (config, consentConfigMap) => { const consent = {}; - if (config?.userDataConsent) { - if (GOOGLE_ALLOWED_CONSENT_STATUS.includes(config.userDataConsent)) { - consent.adUserData = config.userDataConsent; + Object.keys(consentConfigMap).forEach((key) => { + const consentType = consentConfigMap[key]; + if (config?.[key]) { + if (GOOGLE_ALLOWED_CONSENT_STATUS.includes(config[key])) { + consent[consentType] = config[key]; + } else { + consent[consentType] = UNKNOWN_CONSENT; + } } else { - consent.adUserData = 'UNKNOWN'; + consent[consentType] = UNSPECIFIED_CONSENT; } - } else { - consent.adUserData = 'UNSPECIFIED'; + }); + + return consent; +}; + +/** + * Generates the final consent object based on the provided consent configuration map, event-level consent, and destination configuration. + * + * @param {Object} consentConfigMap - The map of consent configuration keys and their corresponding consent types. + * @param {Object} [eventLevelConsent={}] - The event-level consent object. + * @param {Object} [destConfig={}] - The destination configuration object. + * @returns {Object} The final consent object. + * ref : + * 1) For click conversion : + * a) https://developers.google.com/google-ads/api/rest/reference/rest/v16/customers/uploadClickConversions#ClickConversion + * b) https://developers.google.com/google-ads/api/reference/rpc/v16/ClickConversion#consent + * 2) For Call conversion : + * a) https://developers.google.com/google-ads/api/rest/reference/rest/v16/customers/uploadCallConversions#CallConversion + * b) https://developers.google.com/google-ads/api/reference/rpc/v16/CallConversion#consent + * 3) For Store sales conversion : + * a) https://developers.google.com/google-ads/api/reference/rpc/v16/UserData + * b) https://developers.google.com/google-ads/api/reference/rpc/v16/UserData#consent + */ +const finaliseConsent = (consentConfigMap, eventLevelConsent = {}, destConfig = {}) => { + // Initialize defaultConsentBlock with unspecified consent for all keys defined in consentConfigMap + const defaultConsentBlock = Object.keys(consentConfigMap).reduce((acc, key) => { + const consentType = consentConfigMap[key]; + acc[consentType] = UNSPECIFIED_CONSENT; + return acc; + }, {}); + + // If destConfig is provided, update defaultConsentBlock based on it using populateConsentFromConfig + if (Object.keys(destConfig).length > 0) { + const populatedConsent = populateConsentFromConfig(destConfig, consentConfigMap); + Object.assign(defaultConsentBlock, populatedConsent); } - if (config?.personalizationConsent) { - if (GOOGLE_ALLOWED_CONSENT_STATUS.includes(config.personalizationConsent)) { - consent.adPersonalization = config.personalizationConsent; + const consentObj = {}; + + // Iterate through each key in consentConfigMap to determine the final consent + Object.keys(consentConfigMap).forEach((configKey) => { + const consentKey = consentConfigMap[configKey]; // e.g., 'adUserData' + + // Prioritize event-level consent if available + if (eventLevelConsent && eventLevelConsent.hasOwnProperty(consentKey)) { + consentObj[consentKey] = GOOGLE_ALLOWED_CONSENT_STATUS.includes(eventLevelConsent[consentKey]) + ? eventLevelConsent[consentKey] + : UNKNOWN_CONSENT; } else { - consent.adPersonalization = 'UNKNOWN'; + // Fallback to default consent block + consentObj[consentKey] = defaultConsentBlock[consentKey]; } - } else { - consent.adPersonalization = 'UNSPECIFIED'; - } - return consent; + }); + + return consentObj; }; -module.exports = { populateConsentForGoogleDestinations }; +/** + * Populates the consent object based on the provided configuration and consent mapping. + * @param {*} consentConfigMap + * @param {*} eventLevelConsent + * @returns + */ +const finaliseAnalyticsConsents = (consentConfigMap, eventLevelConsent = {}) => { + const consentObj = {}; + // Iterate through each key in consentConfigMap to set the consent + Object.keys(consentConfigMap).forEach((configKey) => { + const consentKey = consentConfigMap[configKey]; // e.g., 'ad_user_data' + + // Set consent only if valid + if ( + eventLevelConsent && + eventLevelConsent.hasOwnProperty(consentKey) && + GA4_ALLOWED_CONSENT_STATUS.includes(eventLevelConsent[consentKey]) + ) { + consentObj[consentKey] = eventLevelConsent[consentKey]; + } + }); + + return consentObj; +}; + +module.exports = { + populateConsentFromConfig, + UNSPECIFIED_CONSENT, + UNKNOWN_CONSENT, + GOOGLE_ALLOWED_CONSENT_STATUS, + finaliseConsent, + finaliseAnalyticsConsents, +}; diff --git a/src/v0/util/googleUtils/index.test.js b/src/v0/util/googleUtils/index.test.js index 9d1aa5e51a..76ec624311 100644 --- a/src/v0/util/googleUtils/index.test.js +++ b/src/v0/util/googleUtils/index.test.js @@ -1,8 +1,16 @@ -const { populateConsentForGoogleDestinations } = require('./index'); +const { + finaliseConsent, + populateConsentFromConfig, + finaliseAnalyticsConsents, +} = require('./index'); -describe('unit test for populateConsentForGoogleDestinations', () => { +describe('unit test for populateConsentFromConfig', () => { + const consentConfigMap = { + personalizationConsent: 'adPersonalization', + userDataConsent: 'adUserData', + }; it('should return an UNSPECIFIED object when no properties are provided', () => { - const result = populateConsentForGoogleDestinations({}); + const result = populateConsentFromConfig({}, consentConfigMap); expect(result).toEqual({ adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED', @@ -11,18 +19,18 @@ describe('unit test for populateConsentForGoogleDestinations', () => { it('should set adUserData property of consent object when userDataConsent property is provided and its value is one of the allowed consent statuses', () => { const properties = { userDataConsent: 'GRANTED' }; - const result = populateConsentForGoogleDestinations(properties); + const result = populateConsentFromConfig(properties, consentConfigMap); expect(result).toEqual({ adUserData: 'GRANTED', adPersonalization: 'UNSPECIFIED' }); }); it('should set adPersonalization property of consent object when personalizationConsent property is provided and its value is one of the allowed consent statuses', () => { const properties = { personalizationConsent: 'DENIED' }; - const result = populateConsentForGoogleDestinations(properties); + const result = populateConsentFromConfig(properties, consentConfigMap); expect(result).toEqual({ adPersonalization: 'DENIED', adUserData: 'UNSPECIFIED' }); }); it('should return an UNSPECIFIED object when properties parameter is not provided', () => { - const result = populateConsentForGoogleDestinations(); + const result = populateConsentFromConfig(undefined, consentConfigMap); expect(result).toEqual({ adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED', @@ -30,7 +38,7 @@ describe('unit test for populateConsentForGoogleDestinations', () => { }); it('should return an UNSPECIFIED object when properties parameter is null', () => { - const result = populateConsentForGoogleDestinations(null); + const result = populateConsentFromConfig(null, consentConfigMap); expect(result).toEqual({ adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED', @@ -38,7 +46,7 @@ describe('unit test for populateConsentForGoogleDestinations', () => { }); it('should return an UNSPECIFIED object when properties parameter is an UNSPECIFIED object', () => { - const result = populateConsentForGoogleDestinations({}); + const result = populateConsentFromConfig({}, consentConfigMap); expect(result).toEqual({ adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED', @@ -46,13 +54,242 @@ describe('unit test for populateConsentForGoogleDestinations', () => { }); it('should return UNKNOWN when properties parameter contains adUserData and adPersonalization with non-allowed values', () => { - const result = populateConsentForGoogleDestinations({ - userDataConsent: 'RANDOM', - personalizationConsent: 'RANDOM', + const result = populateConsentFromConfig( + { + userDataConsent: 'RANDOM', + personalizationConsent: 'RANDOM', + }, + consentConfigMap, + ); + expect(result).toEqual({ + adPersonalization: 'UNKNOWN', + adUserData: 'UNKNOWN', }); + }); +}); + +describe('finaliseConsent', () => { + const consentConfigMap = { + personalizationConsent: 'adPersonalization', + userDataConsent: 'adUserData', + }; + // Returns an object containing consent information. + it('should return an object containing consent information when eventLevelConsent, destConfig, and destinationAllowedConsentKeys are provided', () => { + const eventLevelConsent = { + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }; + const destConfig = { + userDataConsent: 'UNKNOWN', + personalizationConsent: 'GRANTED', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + expect(result).toEqual({ + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }); + }); + + it('should return an object containing consent information from destConfig when evenLevelConsent is empty object', () => { + const eventLevelConsent = {}; // for store conversion we will use this + const destConfig = { + userDataConsent: 'UNKNOWN', + personalizationConsent: 'GRANTED', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + adUserData: 'UNKNOWN', + adPersonalization: 'GRANTED', + }); + }); + + // If destConfig is not provided, it does not return UNSPECIFIED_CONSENT. + it('should not return UNSPECIFIED_CONSENT when destConfig is not provided but event level consent is provided', () => { + const eventLevelConsent = { + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }; + const result = finaliseConsent(consentConfigMap, eventLevelConsent, undefined); + + // Assert + expect(result).toEqual({ + adUserData: 'GRANTED', + adPersonalization: 'DENIED', + }); + }); + + it('should return UNSPECIFIED_CONSENT when both destConfig and event level consent is not provided', () => { + const result = finaliseConsent(consentConfigMap, undefined, undefined); + + // Assert + expect(result).toEqual({ + adUserData: 'UNSPECIFIED', + adPersonalization: 'UNSPECIFIED', + }); + }); + + it('should return UNKWOWN_CONSENT when destConfig is provided with wrong consent value', () => { + const destConfig = { + userDataConsent: 'UNKNOWN', + personalizationConsent: 'WRONG CONSENT', + }; + + const result = finaliseConsent(consentConfigMap, undefined, destConfig); + + expect(result).toEqual({ + adUserData: 'UNKNOWN', adPersonalization: 'UNKNOWN', + }); + }); + + it('should return UNKWOWN_CONSENT when destConfig is provided with wrong consent value', () => { + const destConfig = { + userDataConsent: 'UNKNOWN', + personalizationConsent: 'WRONG CONSENT', + }; + + const result = finaliseConsent(consentConfigMap, undefined, destConfig); + + expect(result).toEqual({ + adPersonalization: 'UNKNOWN', + adUserData: 'UNKNOWN', + }); + }); + + it('should return consent block with appropriate fields and values from destConfig', () => { + const consentConfigMap = { + personalizationConsent: 'newKey1', + userDataConsent: 'newKey2', + }; + const destConfig = { + userDataConsent: 'GRANTED', + personalizationConsent: 'GRANTED', + }; + + const result = finaliseConsent(consentConfigMap, undefined, destConfig); + + expect(result).toEqual({ + newKey1: 'GRANTED', + newKey2: 'GRANTED', + }); + }); + + it('should return consent block with appropriate fields from consentConfigMap and values from eventLevel consent', () => { + const consentConfigMap = { + personalizationConsent: 'newKey1', + userDataConsent: 'newKey2', + }; + const destConfig = { + userDataConsent: 'GRANTED', + personalizationConsent: 'GRANTED', + }; + + const eventLevelConsent = { + newKey1: 'UNKNOWN', + newKey2: 'UNSPECIFIED', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + newKey1: 'UNKNOWN', + newKey2: 'UNSPECIFIED', + }); + }); + + it('consentConfig and eventLevelConsent should have parity, also the values should be within allowed values otherwise UNKNOWN is returned ', () => { + const consentConfigMap = { + personalizationConsent: 'newKey1', + userDataConsent: 'newKey2', + }; + const destConfig = { + userDataConsent: 'GRANTED', + personalizationConsent: 'GRANTED', + }; + + const eventLevelConsent = { adUserData: 'UNKNOWN', + adPersonalization: 'UNSPECIFIED', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + newKey1: 'GRANTED', + newKey2: 'GRANTED', + }); + }); + + it('consentConfig and eventLevelConsent should have parity, otherwise it will take values from destConfig ', () => { + const consentConfigMap = { + personalizationConsent: 'newKey1', + userDataConsent: 'newKey2', + }; + const destConfig = { + userDataConsent: 'GRANTED', + personalizationConsent: 'GRANTED', + }; + + const eventLevelConsent = { + newKey1: 'DENIED', + newKey2: 'RANDOM', + }; + + const result = finaliseConsent(consentConfigMap, eventLevelConsent, destConfig); + + expect(result).toEqual({ + newKey1: 'DENIED', + newKey2: 'UNKNOWN', + }); + }); +}); + +describe('unit test for finaliseAnalyticsConsents', () => { + const consentConfigMap = { + personalizationConsent: 'ad_personalization', + userDataConsent: 'ad_user_data', + }; + it('Should return an empty object when no valid consents are provided', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, {}); + expect(result).toEqual({}); + }); + + it('Should set ad_user_data property of consent object when userDataConsent property is provided and its value is one of the allowed consent statuses', () => { + const properties = { ad_user_data: 'GRANTED' }; + const result = finaliseAnalyticsConsents(consentConfigMap, properties); + expect(result).toEqual({ ad_user_data: 'GRANTED' }); + }); + + it('Should set ad_personalization property of consent object when personalizationConsent property is provided and its value is one of the allowed consent statuses', () => { + const properties = { ad_personalization: 'DENIED' }; + const result = finaliseAnalyticsConsents(consentConfigMap, properties); + expect(result).toEqual({ ad_personalization: 'DENIED' }); + }); + + it('Should return an empty object when properties parameter is not provided', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, undefined); + expect(result).toEqual({}); + }); + + it('Should return an empty object when properties parameter is null', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, null); + expect(result).toEqual({}); + }); + + it('Should return an empty object when properties parameter is an UNSPECIFIED object', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, {}); + expect(result).toEqual({}); + }); + + it('should return empty object when properties parameter contains ad_user_data and ad_personalization with non-allowed values', () => { + const result = finaliseAnalyticsConsents(consentConfigMap, { + userDataConsent: 'RANDOM', + personalizationConsent: 'RANDOM', }); + expect(result).toEqual({}); }); }); diff --git a/src/v0/util/index.js b/src/v0/util/index.js index c1debce088..12b8d4dd7e 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -24,6 +24,8 @@ const { OAuthSecretError, getErrorRespEvents, } = require('@rudderstack/integrations-lib'); + +const { JsonTemplateEngine, PathType } = require('@rudderstack/json-template-engine'); const logger = require('../../logger'); const stats = require('../../util/stats'); const { DestCanonicalNames, DestHandlerMap } = require('../../constants/destinationCanonicalNames'); @@ -57,6 +59,18 @@ const isNull = (x) => lodash.isNull(x); // GENERIC UTLITY // ======================================================================== +const removeUndefinedAndNullRecurse = (obj) => { + // eslint-disable-next-line no-restricted-syntax + for (const key in obj) { + if (obj[key] === null || obj[key] === undefined) { + // eslint-disable-next-line no-param-reassign + delete obj[key]; + } else if (typeof obj[key] === 'object') { + removeUndefinedAndNullRecurse(obj[key]); + } + } +}; + const getEventTime = (message) => { try { return new Date(message.timestamp).toISOString(); @@ -1328,12 +1342,19 @@ const generateExclusionList = (mappingConfig) => */ function extractCustomFields(message, payload, keys, exclusionFields) { const mappingKeys = []; + // Define reserved words + const reservedWords = ['__proto__', 'constructor', 'prototype']; + + const isReservedWord = (key) => reservedWords.includes(key); + if (Array.isArray(keys)) { keys.forEach((key) => { const messageContext = get(message, key); if (messageContext) { Object.keys(messageContext).forEach((k) => { - if (!exclusionFields.includes(k)) mappingKeys.push(k); + if (!exclusionFields.includes(k) && !isReservedWord(k)) { + mappingKeys.push(k); + } }); mappingKeys.forEach((mappingKey) => { if (!(typeof messageContext[mappingKey] === 'undefined')) { @@ -1344,7 +1365,9 @@ function extractCustomFields(message, payload, keys, exclusionFields) { }); } else if (keys === 'root') { Object.keys(message).forEach((k) => { - if (!exclusionFields.includes(k)) mappingKeys.push(k); + if (!exclusionFields.includes(k) && !isReservedWord(k)) { + mappingKeys.push(k); + } }); mappingKeys.forEach((mappingKey) => { if (!(typeof message[mappingKey] === 'undefined')) { @@ -1419,13 +1442,18 @@ function getStringValueOfJSON(json) { return output; } -const getMetadata = (metadata) => ({ +const getTrackingPlanMetadata = (metadata) => ({ + trackingPlanId: metadata.trackingPlanId, + workspaceId: metadata.workspaceId, +}); + +const getMetadata = (metadata = {}) => ({ sourceType: metadata.sourceType, destinationType: metadata.destinationType, k8_namespace: metadata.namespace, }); -const getTransformationMetadata = (metadata) => ({ +const getTransformationMetadata = (metadata = {}) => ({ transformationId: metadata.transformationId, workspaceId: metadata.workspaceId, }); @@ -2220,6 +2248,9 @@ const validateEventAndLowerCaseConversion = (event, isMandatory, convertToLowerC return convertToLowerCase ? event.toString().toLowerCase() : event.toString(); }; +const applyCustomMappings = (message, mappings) => + JsonTemplateEngine.createAsSync(mappings, { defaultPathType: PathType.JSON }).evaluate(message); + // ======================================================================== // EXPORTS // ======================================================================== @@ -2228,6 +2259,7 @@ module.exports = { ErrorMessage, addExternalIdToTraits, adduserIdFromExternalId, + applyCustomMappings, base64Convertor, batchMultiplexedEvents, checkEmptyStringInarray, @@ -2267,6 +2299,7 @@ module.exports = { getMappingConfig, getMetadata, getTransformationMetadata, + getTrackingPlanMetadata, getParsedIP, getStringValueOfJSON, getSuccessRespEvents, @@ -2303,6 +2336,7 @@ module.exports = { removeUndefinedNullEmptyExclBoolInt, removeUndefinedNullValuesAndEmptyObjectArray, removeUndefinedValues, + removeUndefinedAndNullRecurse, returnArrayOfSubarrays, stripTrailingSlash, toTitleCase, diff --git a/src/v0/util/index.test.js b/src/v0/util/index.test.js index 810eb5a9d4..c34d513325 100644 --- a/src/v0/util/index.test.js +++ b/src/v0/util/index.test.js @@ -506,3 +506,187 @@ describe('validateEventAndLowerCaseConversion Tests', () => { }).toThrow(InstrumentationError); }); }); + +describe('extractCustomFields', () => { + // Handle reserved words in message keys + it('should handle reserved word "prototype" in message keys when keys are provided', () => { + const message = { + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + prototype: 'reserved', + }, + context: { + traits: { + phone: '1234567890', + city: 'New York', + country: 'USA', + prototype: 'reserved', + }, + }, + properties: { + title: 'Developer', + organization: 'ABC Company', + zip: '12345', + prototype: 'reserved', + }, + }; + + const payload = {}; + + const keys = ['properties', 'context.traits', 'traits']; + + const exclusionFields = [ + 'firstName', + 'lastName', + 'phone', + 'title', + 'organization', + 'city', + 'region', + 'country', + 'zip', + 'image', + 'timezone', + ]; + + const result = utilities.extractCustomFields(message, payload, keys, exclusionFields); + + expect(result).toEqual({ + email: 'john.doe@example.com', + }); + }); + + it('should handle reserved word "__proto__" in message keys when keys are provided', () => { + const message = { + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + __proto__: 'reserved', + }, + context: { + traits: { + phone: '1234567890', + city: 'New York', + country: 'USA', + __proto__: 'reserved', + }, + }, + properties: { + title: 'Developer', + organization: 'ABC Company', + zip: '12345', + __proto__: 'reserved', + }, + }; + + const payload = {}; + + const keys = ['properties', 'context.traits', 'traits']; + + const exclusionFields = [ + 'firstName', + 'lastName', + 'phone', + 'title', + 'organization', + 'city', + 'region', + 'country', + 'zip', + 'image', + 'timezone', + ]; + const result = utilities.extractCustomFields(message, payload, keys, exclusionFields); + expect(result).toEqual({ + email: 'john.doe@example.com', + }); + }); + + it('should handle reserved word "constructor" in message keys when keys are provided', () => { + const message = { + traits: { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + constructor: 'reserved', + }, + context: { + traits: { + phone: '1234567890', + city: 'New York', + country: 'USA', + constructor: 'reserved', + }, + }, + properties: { + title: 'Developer', + organization: 'ABC Company', + zip: '12345', + constructor: 'reserved', + }, + }; + + const payload = {}; + + const keys = ['properties', 'context.traits', 'traits']; + + const exclusionFields = [ + 'firstName', + 'lastName', + 'phone', + 'title', + 'organization', + 'city', + 'region', + 'country', + 'zip', + 'image', + 'timezone', + ]; + const result = utilities.extractCustomFields(message, payload, keys, exclusionFields); + expect(result).toEqual({ + email: 'john.doe@example.com', + }); + }); + + it('should handle reserved words in message keys when key is root', () => { + const message = { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + prototype: 'reserved', + phone: '1234567890', + city: 'New York', + country: 'USA', + __proto__: 'reserved', + constructor: 'reserved', + }; + + const payload = {}; + + const keys = 'root'; + + const exclusionFields = [ + 'firstName', + 'lastName', + 'phone', + 'title', + 'organization', + 'city', + 'region', + 'country', + 'zip', + 'image', + 'timezone', + ]; + + const result = utilities.extractCustomFields(message, payload, keys, exclusionFields); + + expect(result).toEqual({ + email: 'john.doe@example.com', + }); + }); +}); diff --git a/src/v0/util/mapWithJSONPath.js b/src/v0/util/mapWithJSONPath.js new file mode 100644 index 0000000000..7265eb2c85 --- /dev/null +++ b/src/v0/util/mapWithJSONPath.js @@ -0,0 +1,58 @@ +/* eslint-disable no-plusplus */ +const jsonpath = require('rs-jsonpath'); + +function mapWithJsonPath(message, targetObject, sourcePath, targetPath) { + const values = jsonpath.query(message, sourcePath); + const matchTargetPath = targetPath.split('$.events[0].')[1] || targetPath; + const regexMatch = /\[[^\n\]]*]/; + if (regexMatch.test(sourcePath) && regexMatch.test(matchTargetPath)) { + // both paths are arrays + // eslint-disable-next-line unicorn/no-for-loop + for (let i = 0; i < values.length; i++) { + const targetPathWithIndex = targetPath.replace(/\[\*]/g, `[${i}]`); + const tragetValue = values[i] ? values[i] : null; + jsonpath.value(targetObject, targetPathWithIndex, tragetValue); + } + } else if (!regexMatch.test(sourcePath) && regexMatch.test(matchTargetPath)) { + // source path is not array and target path is + const targetPathArr = targetPath.split('.'); + const holdingArr = []; + // eslint-disable-next-line unicorn/no-for-loop + for (let i = 0; i < targetPathArr.length; i++) { + if (/\[\*]/.test(targetPathArr[i])) { + holdingArr.push(targetPathArr[i]); + break; + } else { + holdingArr.push(targetPathArr[i]); + } + } + const parentTargetPath = holdingArr.join('.'); + const exisitngTargetValues = jsonpath.query(targetObject, parentTargetPath); + if (exisitngTargetValues.length > 0) { + for (let i = 0; i < exisitngTargetValues.length; i++) { + const targetPathWithIndex = targetPath.replace(/\[\*]/g, `[${i}]`); + jsonpath.value(targetObject, targetPathWithIndex, values[0]); + } + } else { + const targetPathWithIndex = targetPath.replace(/\[\*]/g, '[0]'); + jsonpath.value(targetObject, targetPathWithIndex, values[0]); + } + } else if (regexMatch.test(sourcePath)) { + // source path is an array but target path is not + + // filter out null values + const filteredValues = values.filter((value) => value !== null); + if (filteredValues.length > 1) { + jsonpath.value(targetObject, targetPath, filteredValues); + } else { + jsonpath.value(targetObject, targetPath, filteredValues[0]); + } + } else { + // both paths are not arrays + jsonpath.value(targetObject, targetPath, values[0]); + } +} + +module.exports = { + mapWithJsonPath, +}; diff --git a/src/v0/util/recordUtils.js b/src/v0/util/recordUtils.js new file mode 100644 index 0000000000..a3dd65ef33 --- /dev/null +++ b/src/v0/util/recordUtils.js @@ -0,0 +1,55 @@ +const { InstrumentationError, getErrorRespEvents } = require('@rudderstack/integrations-lib'); +const { generateErrorObject } = require('./index'); + +const eventTypes = ['update', 'insert', 'delete']; + +function getErrorMetaData(inputs, acceptedOperations) { + const metadata = []; + // eslint-disable-next-line no-restricted-syntax + for (const key in inputs) { + if (!acceptedOperations.includes(key)) { + inputs[key].forEach((input) => { + metadata.push(input.metadata); + }); + } + } + return metadata; +} + +function getErrorResponse(groupedRecordsByAction) { + const errorMetaData = []; + const errorMetaDataObject = getErrorMetaData(groupedRecordsByAction, eventTypes); + if (errorMetaDataObject.length > 0) { + errorMetaData.push(errorMetaDataObject); + } + + const error = new InstrumentationError('Invalid action type in record event'); + const errorObj = generateErrorObject(error); + const errorResponseList = errorMetaData.map((data) => + getErrorRespEvents(data, errorObj.status, errorObj.message, errorObj.statTags), + ); + + return errorResponseList; +} + +function createFinalResponse(deleteResponse, insertResponse, updateResponse, errorResponseList) { + const finalResponse = []; + if (deleteResponse && deleteResponse.batchedRequest.length > 0) { + finalResponse.push(deleteResponse); + } + if (insertResponse && insertResponse.batchedRequest.length > 0) { + finalResponse.push(insertResponse); + } + if (updateResponse && updateResponse.batchedRequest.length > 0) { + finalResponse.push(updateResponse); + } + if (errorResponseList.length > 0) { + finalResponse.push(...errorResponseList); + } + return finalResponse; +} + +module.exports = { + getErrorResponse, + createFinalResponse, +}; diff --git a/src/v1/destinations/algolia/networkHandler.js b/src/v1/destinations/algolia/networkHandler.js new file mode 100644 index 0000000000..de25993fb1 --- /dev/null +++ b/src/v1/destinations/algolia/networkHandler.js @@ -0,0 +1,81 @@ +/* eslint-disable no-restricted-syntax */ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); + +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + const message = `[ALGOLIA Response V1 Handler] - Request Processed Successfully`; + const responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + if (isHttpStatusSuccess(status)) { + for (const mData of rudderJobMetadata) { + const proxyOutputObj = { + statusCode: 200, + metadata: mData, + error: 'success', + }; + responseWithIndividualEvents.push(proxyOutputObj); + } + + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + + // in case of non 2xx status sending 500 for every event, populate response and update dontBatch to true + const errorMessage = response?.error?.message || response?.message || 'unknown error format'; + for (const metadata of rudderJobMetadata) { + metadata.dontBatch = true; + responseWithIndividualEvents.push({ + statusCode: 500, + metadata, + error: errorMessage, + }); + } + + // At least one event in the batch is invalid. + if (status === 422) { + // sending back 500 for retry + throw new TransformerProxyError( + `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, + 500, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(500), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + + throw new TransformerProxyError( + `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +module.exports = { networkHandler }; diff --git a/src/v1/destinations/bloomreach/networkHandler.js b/src/v1/destinations/bloomreach/networkHandler.js new file mode 100644 index 0000000000..a3c17a167b --- /dev/null +++ b/src/v1/destinations/bloomreach/networkHandler.js @@ -0,0 +1,83 @@ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { proxyRequest, prepareProxyRequest } = require('../../../adapters/network'); +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); +const tags = require('../../../v0/util/tags'); + +// { +// "results": [ +// { +// "success": true +// }, +// { +// "success": false, +// "errors": [ +// "At least one id should be specified." +// ] +// } +// ], +// "start_time": 1710750816.8504393, +// "end_time": 1710750816.8518236, +// "success": true +// } +const checkIfEventIsAbortableAndExtractErrorMessage = (element) => { + if (element.success) { + return { isAbortable: false, errorMsg: '' }; + } + + const errorMsg = element.errors.join(', '); + return { isAbortable: true, errorMsg }; +}; + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + + const message = '[BLOOMREACH Response V1 Handler] - Request Processed Successfully'; + const responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + if (isHttpStatusSuccess(status)) { + // check for Partial Event failures and Successes + const { results } = response; + results.forEach((event, idx) => { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[idx], + error: 'success', + }; + // update status of partial event if abortable + const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage(event); + if (isAbortable) { + proxyOutput.statusCode = 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + }); + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + throw new TransformerProxyError( + `BLOOMREACH: Error encountered in transformer proxy V1`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); +}; +function networkHandler() { + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.prepareProxy = prepareProxyRequest; + this.responseHandler = responseHandler; +} +module.exports = { networkHandler }; diff --git a/src/v1/destinations/campaign_manager/networkHandler.js b/src/v1/destinations/campaign_manager/networkHandler.js index 79f7e7f93b..eee3869fb5 100644 --- a/src/v1/destinations/campaign_manager/networkHandler.js +++ b/src/v1/destinations/campaign_manager/networkHandler.js @@ -9,6 +9,7 @@ const { getDynamicErrorType, } = require('../../../adapters/utils/networkUtils'); const tags = require('../../../v0/util/tags'); +const logger = require('../../../logger'); function isEventAbortableAndExtractErrMsg(element, proxyOutputObj) { let isAbortable = false; @@ -38,7 +39,16 @@ const responseHandler = (responseParams) => { const { destinationResponse, rudderJobMetadata } = responseParams; const message = `[CAMPAIGN_MANAGER Response V1 Handler] - Request Processed Successfully`; const responseWithIndividualEvents = []; - const { response, status } = destinationResponse; + const { response, status, headers } = destinationResponse; + + logger.responseLog('[campaign_manager] response handling', { + metadata: rudderJobMetadata, + responseDetails: { + headers, + response, + status, + }, + }); if (isHttpStatusSuccess(status)) { // check for Partial Event failures and Successes @@ -69,7 +79,7 @@ const responseHandler = (responseParams) => { const errorMessage = response.error?.message || 'unknown error format'; for (const metadata of rudderJobMetadata) { responseWithIndividualEvents.push({ - statusCode: 500, + statusCode: status, metadata, error: errorMessage, }); @@ -77,7 +87,7 @@ const responseHandler = (responseParams) => { throw new TransformerProxyError( `Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation`, - 500, + status, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, diff --git a/src/v1/destinations/emarsys/networkHandler.js b/src/v1/destinations/emarsys/networkHandler.js new file mode 100644 index 0000000000..cef8013028 --- /dev/null +++ b/src/v1/destinations/emarsys/networkHandler.js @@ -0,0 +1,122 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +const { isObject } = require('@rudderstack/integrations-lib'); +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); + +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); + +// ref : https://dev.emarsys.com/docs/emarsys-core-api-guides/c47a64a8ea7dc-http-200-errors +function checkIfEventIsAbortableAndExtractErrorMessage(event, destinationResponse, keyId) { + const { errors } = destinationResponse.response.data; + + // Determine if event is a string or an object, then fetch the corresponding key or value + let errorKey; + if (typeof event === 'string') { + errorKey = event; + } else if (typeof event === 'object' && event[keyId]) { + errorKey = event[keyId]; + } else { + return { isAbortable: false, errorMsg: '' }; // Early return if neither condition is met or if keyId is missing in the object + } + + // Check if this key has a corresponding error in the errors object + if (errors && isObject(errors) && errors[errorKey]) { + // const errorCode = Object.keys(errors[errorKey])[0]; // Assume there is at least one error code + const errorMsg = JSON.stringify(errors[errorKey]); + return { isAbortable: true, errorMsg }; + } + + // if '' is present in the error object, that means, it is a root level error, and none of the events are supposed to be successful + if (errors && isObject(errors) && errors['']) { + const errorMsg = JSON.stringify(errors['']); + return { isAbortable: true, errorMsg }; + } + + // Return false and an empty error message if no error is found + return { isAbortable: false, errorMsg: '' }; +} + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata, destinationRequest } = responseParams; + const message = `[EMARSYS Response V1 Handler] - Request Processed Successfully`; + let responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + // ref : https://dev.emarsys.com/docs/emarsys-core-api-guides/5e68295991665-http-400-errors + if (!isHttpStatusSuccess(status)) { + const errorMessage = response.replyText || 'unknown error format'; + responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + throw new TransformerProxyError( + `EMARSYS: Error transformer proxy v1 during EMARSYS response transformation. ${errorMessage}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + + if (isHttpStatusSuccess(status)) { + // check for Partial Event failures and Successes + // eslint-disable-next-line @typescript-eslint/naming-convention + const { contacts, external_ids, key_id } = destinationRequest.body.JSON; + const finalData = contacts || external_ids; + finalData.forEach((event, idx) => { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[idx], + error: 'success', + }; + // update status of partial event if abortable + const { isAbortable, errorMsg } = checkIfEventIsAbortableAndExtractErrorMessage( + event, + destinationResponse, + key_id, + ); + if (isAbortable) { + proxyOutput.statusCode = 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + }); + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + + // ref : https://dev.emarsys.com/docs/emarsys-core-api-guides/45c776d275862-http-500-errors + + throw new TransformerProxyError( + `EMARSYS: Error transformer proxy v1 during EMARSYS response transformation`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +module.exports = { networkHandler, checkIfEventIsAbortableAndExtractErrorMessage }; diff --git a/src/v1/destinations/linkedin_ads/networkHandler.js b/src/v1/destinations/linkedin_ads/networkHandler.js new file mode 100644 index 0000000000..8219e18fcb --- /dev/null +++ b/src/v1/destinations/linkedin_ads/networkHandler.js @@ -0,0 +1,112 @@ +const lodash = require('lodash'); +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); + +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const tags = require('../../../v0/util/tags'); +const { + constructPartialStatus, + createResponseArray, + getAuthErrCategoryFromStCode, +} = require('../../../cdk/v2/destinations/linkedin_ads/utils'); + +// eslint-disable-next-line consistent-return +// ref : +// 1) https://learn.microsoft.com/en-us/linkedin/shared/api-guide/concepts/error-handling +// 2) https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/conversions-api?view=li-lms-2024-02&tabs=http#api-error-details +// statusCode : 422 we have found by trial and error, not documented in their doc + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + const message = `[LINKEDIN_CONVERSION_API Response V1 Handler] - Request Processed Successfully`; + let responseWithIndividualEvents = []; + const { response, status } = destinationResponse; + + // even if a single event is unsuccessful, the entire batch will fail, we will filter that event out and retry others + if (!isHttpStatusSuccess(status)) { + const errorMessage = response.message || 'unknown error format'; + responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: status, + metadata, + error: errorMessage, + })); + if (status === 401 || status === 403) { + const finalStatus = status === 401 && response.code !== 'REVOKED_ACCESS_TOKEN' ? 500 : 400; + const finalMessage = + status === 401 + ? 'Invalid or expired access token. Retrying' + : 'Lack of permissions to perform the operation. Aborting'; + throw new TransformerProxyError( + `LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. ${finalMessage}`, + finalStatus, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(finalStatus), + }, + destinationResponse, + getAuthErrCategoryFromStCode(destinationResponse), + responseWithIndividualEvents, + ); + } + // if the status is 422, we need to parse the error message and construct the response array + if (status === 422) { + const destPartialStatus = constructPartialStatus(response?.message); + // if the error message is not in the expected format, we will abort all of the events + if (!destPartialStatus || lodash.isEmpty(destPartialStatus)) { + throw new TransformerProxyError( + `LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. Error parsing error message`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + getAuthErrCategoryFromStCode(status), + responseWithIndividualEvents, + ); + } + responseWithIndividualEvents = [...createResponseArray(rudderJobMetadata, destPartialStatus)]; + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; + } + throw new TransformerProxyError( + `LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. ${errorMessage}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + getAuthErrCategoryFromStCode(status), + responseWithIndividualEvents, + ); + } + + // otherwise all events are successful + responseWithIndividualEvents = rudderJobMetadata.map((metadata) => ({ + statusCode: 200, + metadata, + error: 'success', + })); + + return { + status, + message, + destinationResponse, + response: responseWithIndividualEvents, + }; +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.responseHandler = responseHandler; +} + +module.exports = { networkHandler }; diff --git a/src/v1/destinations/monday/networkHandler.js b/src/v1/destinations/monday/networkHandler.js new file mode 100644 index 0000000000..5a0313a27b --- /dev/null +++ b/src/v1/destinations/monday/networkHandler.js @@ -0,0 +1,117 @@ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { proxyRequest, prepareProxyRequest } = require('../../../adapters/network'); +const { + processAxiosResponse, + getDynamicErrorType, +} = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../../v0/util/index'); +const tags = require('../../../v0/util/tags'); +const logger = require('../../../logger'); + +const checkIfUpdationOfStatusRequired = (response) => { + let errorMsg = ''; + const responseBodyStatusCode = response.status_code; + if ( + response.hasOwnProperty('error_message') || + response.hasOwnProperty('error_code') || + response.hasOwnProperty('errors') + ) { + errorMsg = response.error_message || response.errors?.map((error) => error.message).join(', '); + return { hasError: true, errorMsg, responseBodyStatusCode }; + } + return { hasError: false, errorMsg, responseBodyStatusCode }; +}; + +// { +// response: { +// errors: [ +// { +// message: "Field 'region' doesn't exist on type 'User'", +// locations: [{ line: 322, column: 5 }], +// fields: ['query', 'me', 'region'], +// }, +// ], +// account_id: 123456789, +// }, +// status: 200, +// } +// Ref: https://developer.monday.com/api-reference/docs/errors + +const responseHandler = (responseParams) => { + const { destinationResponse, rudderJobMetadata } = responseParams; + + const message = '[MONDAY Response V1 Handler] - Request Processed Successfully'; + const responseWithIndividualEvents = []; + const { response, status, headers } = destinationResponse; + + logger.responseLog('[monday] proxy response', { + metadata: rudderJobMetadata, + responseDetails: { + headers, + response, + status, + }, + }); + // batching not supported + if (isHttpStatusSuccess(status)) { + const proxyOutput = { + statusCode: 200, + metadata: rudderJobMetadata[0], + error: 'success', + }; + // update status of event if abortable or retryable + const { hasError, errorMsg, responseBodyStatusCode } = + checkIfUpdationOfStatusRequired(response); + if (hasError) { + proxyOutput.statusCode = responseBodyStatusCode || 400; + proxyOutput.error = errorMsg; + } + responseWithIndividualEvents.push(proxyOutput); + + if (responseBodyStatusCode === 500 || responseBodyStatusCode === 429) { + throw new TransformerProxyError( + `MONDAY: Error encountered in transformer proxy V1 with error: ${errorMsg}`, + responseBodyStatusCode, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(responseBodyStatusCode), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); + } + + return { + status, + message, + response: responseWithIndividualEvents, + }; + } + + const errorMsg = + response.error_message || response.errors?.map((error) => error.message).join(', '); + + responseWithIndividualEvents.push({ + statusCode: status, + metadata: rudderJobMetadata, + error: errorMsg, + }); + + throw new TransformerProxyError( + `MONDAY: Error encountered in transformer proxy V1 with error: ${errorMsg}`, + status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + '', + responseWithIndividualEvents, + ); +}; +function networkHandler() { + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + this.prepareProxy = prepareProxyRequest; + this.responseHandler = responseHandler; +} +module.exports = { networkHandler }; diff --git a/src/v1/sources/close_crm/config.js b/src/v1/sources/close_crm/config.js new file mode 100644 index 0000000000..410aff8b40 --- /dev/null +++ b/src/v1/sources/close_crm/config.js @@ -0,0 +1,3 @@ +const excludedFieldList = ['changed_fields', 'previous_data']; + +module.exports = { excludedFieldList }; diff --git a/src/v1/sources/close_crm/transform.js b/src/v1/sources/close_crm/transform.js new file mode 100644 index 0000000000..b597285e62 --- /dev/null +++ b/src/v1/sources/close_crm/transform.js @@ -0,0 +1,57 @@ +const moment = require('moment'); +const { + removeUndefinedAndNullValues, + removeUndefinedAndNullRecurse, + generateUUID, + formatTimeStamp, +} = require('../../../v0/util'); +const { excludedFieldList } = require('./config'); +const Message = require('../../../v0/sources/message'); + +function processEvent(inputEvent) { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { event, subscription_id } = inputEvent; + + const message = new Message('CloseCRM'); + + // Set event type track + message.setEventType('track'); + + // Set event name + const eventName = `${event.object_type} ${event.action}`; + message.setEventName(eventName); + + // Set userId + if (event.lead_id) { + message.setProperty('userId', event.lead_id); + } else { + message.setProperty('anonymousId', generateUUID()); + } + + // Set messageId + message.setProperty('messageId', event.id); + + // Set Timestamp + const timestamp = moment.utc(event.date_updated); + message.setProperty('originalTimestamp', formatTimeStamp(timestamp, 'yyyy-MM-ddTHH:mm:ss.SSSZ')); + + // Set properties + removeUndefinedAndNullRecurse(event); + message.setProperty('properties', event); + message.setProperty('properties.subscription_id', subscription_id); + + // Remove excluding fields + excludedFieldList.forEach((field) => { + delete message.properties[field]; + }); + + return message; +} + +function process(inputEvent) { + const { event } = inputEvent; + const response = processEvent(event); + return removeUndefinedAndNullValues(response); +} + +exports.process = process; diff --git a/src/warehouse/index.js b/src/warehouse/index.js index b3d1c5e4bc..f62f02af79 100644 --- a/src/warehouse/index.js +++ b/src/warehouse/index.js @@ -154,7 +154,7 @@ function setDataFromColumnMappingAndComputeColumnTypes( const columnName = utils.safeColumnName(options, key); // do not set column if val is null/empty/object if (typeof val === 'object' || isBlank(val)) { - // delete in output and columnTypes, so as to remove if we user + // delete in output and columnTypes, to remove if the user // has set property with same name // eslint-disable-next-line no-param-reassign delete output[columnName]; @@ -565,8 +565,10 @@ function processWarehouseMessage(message, options) { : {}; const responses = []; const eventType = message.type?.toLowerCase(); - const skipTracksTable = options.integrationOptions.skipTracksTable || false; - const skipUsersTable = options.integrationOptions.skipUsersTable || false; + const skipTracksTable = + options.destConfig?.skipTracksTable || options.integrationOptions.skipTracksTable || false; + const skipUsersTable = + options.destConfig?.skipUsersTable || options.integrationOptions.skipUsersTable || false; const skipReservedKeywordsEscaping = options.integrationOptions.skipReservedKeywordsEscaping || false; diff --git a/test/__tests__/auth0_source.test.js b/test/__tests__/auth0_source.test.js deleted file mode 100644 index 75d103d46f..0000000000 --- a/test/__tests__/auth0_source.test.js +++ /dev/null @@ -1,23 +0,0 @@ -const integration = "auth0"; - -const fs = require("fs"); -const path = require("path"); - -const transformer = require(`../../src/v0/sources/${integration}/transform`); - -const testDataFile = fs.readFileSync( - path.resolve(__dirname, `./data/${integration}_source.json`) -); - -const testData = JSON.parse(testDataFile); - -testData.forEach((data, index) => { - it(`${index}. ${integration} - ${data.description}`, () => { - try { - const output = transformer.process(data.input); - expect(output).toEqual(data.output); - } catch (error) { - expect(error.message).toEqual(data.output.error); - } - }); -}); diff --git a/test/__tests__/data/auth0_source.json b/test/__tests__/data/auth0_source.json deleted file mode 100644 index e3190bd683..0000000000 --- a/test/__tests__/data/auth0_source.json +++ /dev/null @@ -1,1221 +0,0 @@ -[ - { - "description": "successful signup", - "input": [ - { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "data": { - "date": "2022-10-31T05:57:06.859Z", - "type": "ss", - "description": "", - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "ip": "35.166.202.113", - "user_agent": "unknown", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "user_id": "auth0|dummyPassword", - "user_name": "testRudderlabs+21@gmail.com", - "strategy": "auth0", - "strategy_type": "database", - "log_id": "90020221031055712103169676686005480714681762668315934738" - } - }, - { - "log_id": "90020221031055712103169676686007898566320991926665347090", - "data": { - "date": "2022-10-31T05:57:06.874Z", - "type": "sapi", - "description": "Create a User", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "ip": "35.166.202.113", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "details": { - "request": { - "ip": "35.166.202.113", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "auth0|dummyPassword" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f", - "scopes": [ - "create:actions", - "create:actions_log_sessions", - "create:client_credentials", - "create:client_grants", - "create:clients", - "create:connections", - "create:custom_domains", - "create:email_provider", - "create:email_templates", - "create:guardian_enrollment_tickets", - "create:integrations", - "create:log_streams", - "create:organization_connections", - "create:organization_invitations", - "create:organization_member_roles", - "create:organization_members", - "create:organizations", - "create:requested_scopes", - "create:resource_servers", - "create:roles", - "create:rules", - "create:shields", - "create:signing_keys", - "create:tenant_invitations", - "create:test_email_dispatch", - "create:users", - "delete:actions", - "delete:anomaly_blocks", - "delete:branding", - "delete:client_credentials", - "delete:client_grants", - "delete:clients", - "delete:connections", - "delete:custom_domains", - "delete:device_credentials", - "delete:email_provider", - "delete:email_templates", - "delete:grants", - "delete:guardian_enrollments", - "delete:integrations", - "delete:log_streams", - "delete:organization_connections", - "delete:organization_invitations", - "delete:organization_member_roles", - "delete:organization_members", - "delete:organizations", - "delete:owners", - "delete:requested_scopes", - "delete:resource_servers", - "delete:roles", - "delete:rules", - "delete:rules_configs", - "delete:shields", - "delete:tenant_invitations", - "delete:tenant_members", - "delete:tenants", - "delete:users", - "read:actions", - "read:anomaly_blocks", - "read:attack_protection", - "read:branding", - "read:checks", - "read:client_credentials", - "read:client_grants", - "read:client_keys", - "read:clients", - "read:connections", - "read:custom_domains", - "read:device_credentials", - "read:email_provider", - "read:email_templates", - "read:email_triggers", - "read:entity_counts", - "read:grants", - "read:guardian_factors", - "read:insights", - "read:integrations", - "read:log_streams", - "read:logs", - "read:mfa_policies", - "read:organization_connections", - "read:organization_invitations", - "read:organization_member_roles", - "read:organization_members", - "read:organizations", - "read:prompts", - "read:requested_scopes", - "read:resource_servers", - "read:roles", - "read:rules", - "read:rules_configs", - "read:shields", - "read:signing_keys", - "read:stats", - "read:tenant_invitations", - "read:tenant_members", - "read:tenant_settings", - "read:triggers", - "read:users", - "run:checks", - "update:actions", - "update:attack_protection", - "update:branding", - "update:client_credentials", - "update:client_grants", - "update:client_keys", - "update:clients", - "update:connections", - "update:custom_domains", - "update:email_provider", - "update:email_templates", - "update:email_triggers", - "update:guardian_factors", - "update:integrations", - "update:log_streams", - "update:mfa_policies", - "update:organization_connections", - "update:organizations", - "update:prompts", - "update:requested_scopes", - "update:resource_servers", - "update:roles", - "update:rules", - "update:rules_configs", - "update:shields", - "update:signing_keys", - "update:tenant_members", - "update:tenant_settings", - "update:triggers", - "update:users" - ] - } - }, - "body": { - "email": "testRudderlabs+21@gmail.com", - "password": "dummyPassword", - "connection": "Username-Password-Authentication" - }, - "path": "/api/v2/users", - "query": {}, - "method": "post", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": { - "name": "testRudderlabs+21@gmail.com", - "email": "testRudderlabs+21@gmail.com", - "picture": "https://s.gravatar.com/avatar/0902f9d02b92aed9f0ac59aaf9475b60?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fbh.png", - "user_id": "auth0|dummyPassword", - "nickname": "testRudderlabs+21", - "created_at": "2022-10-31T05:57:06.864Z", - "identities": [ - { - "user_id": "auth0|dummyPassword", - "isSocial": false, - "provider": "auth0", - "connection": "Username-Password-Authentication" - } - ], - "updated_at": "2022-10-31T05:57:06.864Z", - "email_verified": false - }, - "statusCode": 201 - } - }, - "user_id": "auth0|dummyPassword", - "log_id": "90020221031055712103169676686007898566320991926665347090" - } - } - ], - "output": [ - { - "type": "identify", - "sentAt": "2022-10-31T05:57:06.859Z", - "traits": { - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S" - }, - "userId": "auth0|dummyPassword", - "context": { - "traits": { - "userId": "auth0|dummyPassword", - "user_name": "testRudderlabs+21@gmail.com" - }, - "library": { - "name": "unknown", - "version": "unknown" - }, - "userAgent": "unknown", - "request_ip": "35.166.202.113", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "description": "" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T05:57:06.859Z" - }, - { - "type": "track", - "event": "Success API Operation", - "sentAt": "2022-10-31T05:57:06.874Z", - "userId": "auth0|dummyPassword", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "traits": { - "userId": "auth0|dummyPassword" - }, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "request_ip": "35.166.202.113", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031055712103169676686007898566320991926665347090", - "details": { - "request": { - "ip": "35.166.202.113", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "auth0|dummyPassword" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f", - "scopes": [ - "create:actions", - "create:actions_log_sessions", - "create:client_credentials", - "create:client_grants", - "create:clients", - "create:connections", - "create:custom_domains", - "create:email_provider", - "create:email_templates", - "create:guardian_enrollment_tickets", - "create:integrations", - "create:log_streams", - "create:organization_connections", - "create:organization_invitations", - "create:organization_member_roles", - "create:organization_members", - "create:organizations", - "create:requested_scopes", - "create:resource_servers", - "create:roles", - "create:rules", - "create:shields", - "create:signing_keys", - "create:tenant_invitations", - "create:test_email_dispatch", - "create:users", - "delete:actions", - "delete:anomaly_blocks", - "delete:branding", - "delete:client_credentials", - "delete:client_grants", - "delete:clients", - "delete:connections", - "delete:custom_domains", - "delete:device_credentials", - "delete:email_provider", - "delete:email_templates", - "delete:grants", - "delete:guardian_enrollments", - "delete:integrations", - "delete:log_streams", - "delete:organization_connections", - "delete:organization_invitations", - "delete:organization_member_roles", - "delete:organization_members", - "delete:organizations", - "delete:owners", - "delete:requested_scopes", - "delete:resource_servers", - "delete:roles", - "delete:rules", - "delete:rules_configs", - "delete:shields", - "delete:tenant_invitations", - "delete:tenant_members", - "delete:tenants", - "delete:users", - "read:actions", - "read:anomaly_blocks", - "read:attack_protection", - "read:branding", - "read:checks", - "read:client_credentials", - "read:client_grants", - "read:client_keys", - "read:clients", - "read:connections", - "read:custom_domains", - "read:device_credentials", - "read:email_provider", - "read:email_templates", - "read:email_triggers", - "read:entity_counts", - "read:grants", - "read:guardian_factors", - "read:insights", - "read:integrations", - "read:log_streams", - "read:logs", - "read:mfa_policies", - "read:organization_connections", - "read:organization_invitations", - "read:organization_member_roles", - "read:organization_members", - "read:organizations", - "read:prompts", - "read:requested_scopes", - "read:resource_servers", - "read:roles", - "read:rules", - "read:rules_configs", - "read:shields", - "read:signing_keys", - "read:stats", - "read:tenant_invitations", - "read:tenant_members", - "read:tenant_settings", - "read:triggers", - "read:users", - "run:checks", - "update:actions", - "update:attack_protection", - "update:branding", - "update:client_credentials", - "update:client_grants", - "update:client_keys", - "update:clients", - "update:connections", - "update:custom_domains", - "update:email_provider", - "update:email_templates", - "update:email_triggers", - "update:guardian_factors", - "update:integrations", - "update:log_streams", - "update:mfa_policies", - "update:organization_connections", - "update:organizations", - "update:prompts", - "update:requested_scopes", - "update:resource_servers", - "update:roles", - "update:rules", - "update:rules_configs", - "update:shields", - "update:signing_keys", - "update:tenant_members", - "update:tenant_settings", - "update:triggers", - "update:users" - ] - } - }, - "body": { - "email": "testRudderlabs+21@gmail.com", - "password": "dummyPassword", - "connection": "Username-Password-Authentication" - }, - "path": "/api/v2/users", - "query": {}, - "method": "post", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": { - "name": "testRudderlabs+21@gmail.com", - "email": "testRudderlabs+21@gmail.com", - "picture": "https://s.gravatar.com/avatar/0902f9d02b92aed9f0ac59aaf9475b60?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fbh.png", - "user_id": "auth0|dummyPassword", - "nickname": "testRudderlabs+21", - "created_at": "2022-10-31T05:57:06.864Z", - "identities": [ - { - "user_id": "auth0|dummyPassword", - "isSocial": false, - "provider": "auth0", - "connection": "Username-Password-Authentication" - } - ], - "updated_at": "2022-10-31T05:57:06.864Z", - "email_verified": false - }, - "statusCode": 201 - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "description": "Create a User" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T05:57:06.874Z" - } - ] - }, - { - "description": "Add member to an organization", - "input": [ - { - "log_id": "90020221031061004280169676882609459981150114445973782546", - "data": { - "date": "2022-10-31T06:09:59.135Z", - "type": "sapi", - "description": "Add members to an organization", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "ip": "35.167.74.121", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "details": { - "request": { - "ip": "35.167.74.121", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "google-oauth2|123456" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f" - } - }, - "body": { - "members": ["auth0|123456"] - }, - "path": "/api/v2/organizations/org_eoe8p2atZ7furBxg/members", - "query": {}, - "method": "post", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": {}, - "statusCode": 204 - } - }, - "user_id": "google-oauth2|123456", - "log_id": "90020221031061004280169676882609459981150114445973782546" - } - } - ], - "output": [ - { - "type": "group", - "sentAt": "2022-10-31T06:09:59.135Z", - "userId": "google-oauth2|123456", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "traits": { - "userId": "google-oauth2|123456" - }, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "request_ip": "35.167.74.121", - "integration": { - "name": "Auth0" - } - }, - "groupId": "org_eoe8p2atZ7furBxg", - "properties": { - "log_id": "90020221031061004280169676882609459981150114445973782546", - "details": { - "request": { - "ip": "35.167.74.121", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "google-oauth2|123456" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f" - } - }, - "body": { - "members": ["auth0|123456"] - }, - "path": "/api/v2/organizations/org_eoe8p2atZ7furBxg/members", - "query": {}, - "method": "post", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": {}, - "statusCode": 204 - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "description": "Add members to an organization" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T06:09:59.135Z" - } - ] - }, - { - "description": "Update tenant settings", - "input": [ - { - "log_id": "90020221031061527239169676960191065529099349299958906898", - "data": { - "date": "2022-10-31T06:15:25.201Z", - "type": "sapi", - "description": "Update tenant settings", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "ip": "35.160.3.103", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "details": { - "request": { - "ip": "35.160.3.103", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "google-oauth2|123456" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f", - "scopes": [ - "create:actions", - "create:actions_log_sessions", - "create:client_credentials", - "create:client_grants", - "create:clients", - "create:connections", - "create:custom_domains", - "create:email_provider", - "create:email_templates", - "create:guardian_enrollment_tickets", - "create:integrations", - "create:log_streams", - "create:organization_connections", - "create:organization_invitations", - "create:organization_member_roles", - "create:organization_members", - "create:organizations", - "create:requested_scopes", - "create:resource_servers", - "create:roles", - "create:rules", - "create:shields", - "create:signing_keys", - "create:tenant_invitations", - "create:test_email_dispatch", - "create:users", - "delete:actions", - "delete:anomaly_blocks", - "delete:branding", - "delete:client_credentials", - "delete:client_grants", - "delete:clients", - "delete:connections", - "delete:custom_domains", - "delete:device_credentials", - "delete:email_provider", - "delete:email_templates", - "delete:grants", - "delete:guardian_enrollments", - "delete:integrations", - "delete:log_streams", - "delete:organization_connections", - "delete:organization_invitations", - "delete:organization_member_roles", - "delete:organization_members", - "delete:organizations", - "delete:owners", - "delete:requested_scopes", - "delete:resource_servers", - "delete:roles", - "delete:rules", - "delete:rules_configs", - "delete:shields", - "delete:tenant_invitations", - "delete:tenant_members", - "delete:tenants", - "delete:users", - "read:actions", - "read:anomaly_blocks", - "read:attack_protection", - "read:branding", - "read:checks", - "read:client_credentials", - "read:client_grants", - "read:client_keys", - "read:clients", - "read:connections", - "read:custom_domains", - "read:device_credentials", - "read:email_provider", - "read:email_templates", - "read:email_triggers", - "read:entity_counts", - "read:grants", - "read:guardian_factors", - "read:insights", - "read:integrations", - "read:log_streams", - "read:logs", - "read:mfa_policies", - "read:organization_connections", - "read:organization_invitations", - "read:organization_member_roles", - "read:organization_members", - "read:organizations", - "read:prompts", - "read:requested_scopes", - "read:resource_servers", - "read:roles", - "read:rules", - "read:rules_configs", - "read:shields", - "read:signing_keys", - "read:stats", - "read:tenant_invitations", - "read:tenant_members", - "read:tenant_settings", - "read:triggers", - "read:users", - "run:checks", - "update:actions", - "update:attack_protection", - "update:branding", - "update:client_credentials", - "update:client_grants", - "update:client_keys", - "update:clients", - "update:connections", - "update:custom_domains", - "update:email_provider", - "update:email_templates", - "update:email_triggers", - "update:guardian_factors", - "update:integrations", - "update:log_streams", - "update:mfa_policies", - "update:organization_connections", - "update:organizations", - "update:prompts", - "update:requested_scopes", - "update:resource_servers", - "update:roles", - "update:rules", - "update:rules_configs", - "update:shields", - "update:signing_keys", - "update:tenant_members", - "update:tenant_settings", - "update:triggers", - "update:users" - ] - } - }, - "body": { - "picture_url": "", - "support_url": "", - "friendly_name": "mecro-action", - "support_email": "support@test.com" - }, - "path": "/api/v2/tenants/settings", - "query": {}, - "method": "patch", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": { - "flags": { - "enable_sso": true, - "universal_login": true, - "disable_impersonation": true, - "allow_changing_enable_sso": false, - "revoke_refresh_token_grant": false, - "disable_clickjack_protection_headers": false, - "new_universal_login_experience_enabled": true, - "enforce_client_authentication_on_passwordless_start": true, - "cannot_change_enforce_client_authentication_on_passwordless_start": true - }, - "picture_url": "", - "support_url": "", - "friendly_name": "mecro-action", - "support_email": "support@test.com", - "enabled_locales": ["en"], - "sandbox_version": "16", - "universal_login": {} - }, - "statusCode": 200 - } - }, - "user_id": "google-oauth2|123456", - "log_id": "90020221031061527239169676960191065529099349299958906898" - } - }, - { - "log_id": "90020221031061530247169676961198100736838335677367058450", - "data": { - "date": "2022-10-31T06:15:25.196Z", - "type": "gd_tenant_update", - "description": "Guardian - Updates tenant settings", - "ip": "35.160.3.103", - "details": { - "request": { - "ip": "35.160.3.103", - "auth": { - "scopes": [ - "read:authenticators", - "remove:authenticators", - "update:authenticators", - "create:authenticators", - "read:enrollments", - "delete:enrollments", - "read:factors", - "update:factors", - "update:tenant_settings", - "update:users", - "create:enrollment_tickets", - "create:users" - ], - "subject": "google-oauth2|123456", - "strategy": "jwt_api2_internal_token" - }, - "body": { - "picture_url": "[REDACTED]", - "friendly_name": "[REDACTED]" - }, - "path": "/api/tenants/settings", - "query": {}, - "method": "PATCH" - }, - "response": { - "body": { - "name": "dev-cu4jy2zgao6yx15x", - "picture_url": "[REDACTED]", - "friendly_name": "[REDACTED]", - "guardian_mfa_page": "[REDACTED]" - }, - "statusCode": 200 - } - }, - "user_id": "google-oauth2|123456", - "log_id": "90020221031061530247169676961198100736838335677367058450" - } - } - ], - "output": [ - { - "type": "track", - "event": "Success API Operation", - "sentAt": "2022-10-31T06:15:25.201Z", - "userId": "google-oauth2|123456", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "traits": { - "userId": "google-oauth2|123456" - }, - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "request_ip": "35.160.3.103", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031061527239169676960191065529099349299958906898", - "details": { - "request": { - "ip": "35.160.3.103", - "auth": { - "user": { - "name": "rudder test", - "email": "test@rudderstack.com", - "user_id": "google-oauth2|123456" - }, - "strategy": "jwt", - "credentials": { - "jti": "571921bf7833a97efabf08d765a0ec8f", - "scopes": [ - "create:actions", - "create:actions_log_sessions", - "create:client_credentials", - "create:client_grants", - "create:clients", - "create:connections", - "create:custom_domains", - "create:email_provider", - "create:email_templates", - "create:guardian_enrollment_tickets", - "create:integrations", - "create:log_streams", - "create:organization_connections", - "create:organization_invitations", - "create:organization_member_roles", - "create:organization_members", - "create:organizations", - "create:requested_scopes", - "create:resource_servers", - "create:roles", - "create:rules", - "create:shields", - "create:signing_keys", - "create:tenant_invitations", - "create:test_email_dispatch", - "create:users", - "delete:actions", - "delete:anomaly_blocks", - "delete:branding", - "delete:client_credentials", - "delete:client_grants", - "delete:clients", - "delete:connections", - "delete:custom_domains", - "delete:device_credentials", - "delete:email_provider", - "delete:email_templates", - "delete:grants", - "delete:guardian_enrollments", - "delete:integrations", - "delete:log_streams", - "delete:organization_connections", - "delete:organization_invitations", - "delete:organization_member_roles", - "delete:organization_members", - "delete:organizations", - "delete:owners", - "delete:requested_scopes", - "delete:resource_servers", - "delete:roles", - "delete:rules", - "delete:rules_configs", - "delete:shields", - "delete:tenant_invitations", - "delete:tenant_members", - "delete:tenants", - "delete:users", - "read:actions", - "read:anomaly_blocks", - "read:attack_protection", - "read:branding", - "read:checks", - "read:client_credentials", - "read:client_grants", - "read:client_keys", - "read:clients", - "read:connections", - "read:custom_domains", - "read:device_credentials", - "read:email_provider", - "read:email_templates", - "read:email_triggers", - "read:entity_counts", - "read:grants", - "read:guardian_factors", - "read:insights", - "read:integrations", - "read:log_streams", - "read:logs", - "read:mfa_policies", - "read:organization_connections", - "read:organization_invitations", - "read:organization_member_roles", - "read:organization_members", - "read:organizations", - "read:prompts", - "read:requested_scopes", - "read:resource_servers", - "read:roles", - "read:rules", - "read:rules_configs", - "read:shields", - "read:signing_keys", - "read:stats", - "read:tenant_invitations", - "read:tenant_members", - "read:tenant_settings", - "read:triggers", - "read:users", - "run:checks", - "update:actions", - "update:attack_protection", - "update:branding", - "update:client_credentials", - "update:client_grants", - "update:client_keys", - "update:clients", - "update:connections", - "update:custom_domains", - "update:email_provider", - "update:email_templates", - "update:email_triggers", - "update:guardian_factors", - "update:integrations", - "update:log_streams", - "update:mfa_policies", - "update:organization_connections", - "update:organizations", - "update:prompts", - "update:requested_scopes", - "update:resource_servers", - "update:roles", - "update:rules", - "update:rules_configs", - "update:shields", - "update:signing_keys", - "update:tenant_members", - "update:tenant_settings", - "update:triggers", - "update:users" - ] - } - }, - "body": { - "picture_url": "", - "support_url": "", - "friendly_name": "mecro-action", - "support_email": "support@test.com" - }, - "path": "/api/v2/tenants/settings", - "query": {}, - "method": "patch", - "channel": "https://manage.auth0.com/", - "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - "response": { - "body": { - "flags": { - "enable_sso": true, - "universal_login": true, - "disable_impersonation": true, - "allow_changing_enable_sso": false, - "revoke_refresh_token_grant": false, - "disable_clickjack_protection_headers": false, - "new_universal_login_experience_enabled": true, - "enforce_client_authentication_on_passwordless_start": true, - "cannot_change_enforce_client_authentication_on_passwordless_start": true - }, - "picture_url": "", - "support_url": "", - "friendly_name": "mecro-action", - "support_email": "support@test.com", - "enabled_locales": ["en"], - "sandbox_version": "16", - "universal_login": {} - }, - "statusCode": 200 - } - }, - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "description": "Update tenant settings" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T06:15:25.201Z" - }, - { - "type": "track", - "event": "Guardian tenant update", - "sentAt": "2022-10-31T06:15:25.196Z", - "userId": "google-oauth2|123456", - "context": { - "library": { - "name": "unknown", - "version": "unknown" - }, - "traits": { - "userId": "google-oauth2|123456" - }, - "request_ip": "35.160.3.103", - "integration": { - "name": "Auth0" - } - }, - "properties": { - "log_id": "90020221031061530247169676961198100736838335677367058450", - "details": { - "request": { - "ip": "35.160.3.103", - "auth": { - "scopes": [ - "read:authenticators", - "remove:authenticators", - "update:authenticators", - "create:authenticators", - "read:enrollments", - "delete:enrollments", - "read:factors", - "update:factors", - "update:tenant_settings", - "update:users", - "create:enrollment_tickets", - "create:users" - ], - "subject": "google-oauth2|123456", - "strategy": "jwt_api2_internal_token" - }, - "body": { - "picture_url": "[REDACTED]", - "friendly_name": "[REDACTED]" - }, - "path": "/api/tenants/settings", - "query": {}, - "method": "PATCH" - }, - "response": { - "body": { - "name": "dev-cu4jy2zgao6yx15x", - "picture_url": "[REDACTED]", - "friendly_name": "[REDACTED]", - "guardian_mfa_page": "[REDACTED]" - }, - "statusCode": 200 - } - }, - "description": "Guardian - Updates tenant settings" - }, - "integrations": { - "Auth0": false - }, - "originalTimestamp": "2022-10-31T06:15:25.196Z" - } - ] - }, - { - "description": "Missing userId", - "input": { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "data": { - "date": "2022-10-31T05:57:06.859Z", - "type": "ss", - "description": "", - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "ip": "35.166.202.113", - "user_agent": "unknown", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "user_id": "", - "user_name": "testRudderlabs+21@gmail.com", - "strategy": "auth0", - "strategy_type": "database", - "log_id": "90020221031055712103169676686005480714681762668315934738" - } - }, - "output": { - "statusCode": 400, - "error": "UserId is not present", - "statTags": { - "errorCategory": "dataValidation", - "errorType": "instrumentation", - "module": "source", - "implementation": "native", - "destinationId": "Non determinable", - "workspaceId": "Non determinable" - } - } - }, - { - "description": "UserId is missing for all the requests in a batch", - "input": [ - { - "log_id": "90020221031055712103169676686005480714681762668315934738", - "data": { - "date": "2022-10-31T05:57:06.859Z", - "type": "ss", - "description": "", - "connection": "Username-Password-Authentication", - "connection_id": "con_djwCjiwyID0vZy1S", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "All Applications", - "ip": "35.166.202.113", - "user_agent": "unknown", - "details": { - "body": { - "email": "testRudderlabs+21@gmail.com", - "tenant": "dev-cu4jy2zgao6yx15x", - "password": "dummyPassword", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "connection": "Username-Password-Authentication" - } - }, - "user_id": "", - "user_name": "testRudderlabs+21@gmail.com", - "strategy": "auth0", - "strategy_type": "database", - "log_id": "90020221031055712103169676686005480714681762668315934738" - } - }, - { - "log_id": "90020221031055712103169676686007898566320991926665347090", - "data": { - "date": "2022-10-31T05:57:06.874Z", - "type": "sapi", - "description": "Create a User", - "client_id": "vQcJNDTxsM1W72eHFonRJdzyOvawlwIt", - "client_name": "", - "ip": "35.166.202.113", - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "log_id": "90020221031055712103169676686007898566320991926665347090" - } - } - ], - "output": { - "statusCode": 400, - "error": "UserId is not present", - "statTags": { - "errorCategory": "dataValidation", - "errorType": "instrumentation", - "module": "source", - "implementation": "native", - "destinationId": "Non determinable", - "workspaceId": "Non determinable" - } - } - } -] diff --git a/test/__tests__/data/user_transformation_input_credentials.json b/test/__tests__/data/user_transformation_input_credentials.json new file mode 100644 index 0000000000..d210a3715c --- /dev/null +++ b/test/__tests__/data/user_transformation_input_credentials.json @@ -0,0 +1,67 @@ +[ + { + "message": { + "channel": "web", + "context": { + "app": { + "build": "1.0.0", + "name": "RudderLabs JavaScript SDK", + "namespace": "com.rudderlabs.javascript", + "version": "1.0.0" + }, + "traits": { + "email": "test@rudderstack.com", + "anonymousId": "12345" + }, + "library": { + "name": "RudderLabs JavaScript SDK", + "version": "1.0.0" + }, + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", + "locale": "en-US", + "ip": "0.0.0.0", + "os": { + "name": "", + "version": "" + }, + "screen": { + "density": 2 + } + }, + "type": "track", + "messageId": "ec5481b6-a926-4d2e-b293-0b3a77c4d3be", + "originalTimestamp": "2019-10-14T11:15:18.300Z", + "anonymousId": "00000000000000000000000000", + "userId": "12345", + "event": "test track event GA3", + "properties": { + "user_actual_role": "system_admin, system_user", + "user_actual_id": 12345 + }, + "integrations": { + "All": true + }, + "sentAt": "2019-10-14T11:15:53.296Z" + }, + "destination": { + "Config": { + "trackingID": "UA-149602794-1" + }, + "Enabled": true + }, + "credentials": [ + { + "id": "id1", + "key": "key1", + "value": "value1", + "isSecret": false + }, + { + "id": "id2", + "key": "key2", + "value": "value2", + "isSecret": true + } + ] + } +] diff --git a/test/__tests__/data/warehouse/dest_config_scenarios.js b/test/__tests__/data/warehouse/dest_config_scenarios.js new file mode 100644 index 0000000000..0e290b7153 --- /dev/null +++ b/test/__tests__/data/warehouse/dest_config_scenarios.js @@ -0,0 +1,223 @@ +const _ = require("lodash"); + +const trackMessage = { + destination: { Config: {} }, + message: { + type: "track", + messageId: "my-track-message-id-1", + userId: "9bb5d4c2-a7aa-4a36-9efb-dd2b1aec5d33", + anonymousId: "e6ab2c5e-2cda-44a9-a962-e2f67df78bca", + channel: "web", + context: { + app: { + build: "1.0.0", + name: "RudderLabs JavaScript SDK", + namespace: "com.rudderlabs.javascript", + version: "1.0.5" + }, + ip: "0.0.0.0", + library: { + name: "RudderLabs JavaScript SDK", + version: "1.0.5" + }, + locale: "en-GB", + os: { + name: "", + version: "" + }, + screen: { density: 2 }, + traits: { + city: "Disney", + country: "USA", + email: "mickey@disney.com", + firstname: "Mickey" + }, + userAgent: "Mozilla/5.0 Chrome/79.0.3945.117 Safari/537.36" + }, + event: "groups", + integrations: { All: true }, + originalTimestamp: "2020-01-24T06:29:02.364Z", + properties: { currency: "USD" }, + receivedAt: "2020-01-24T11:59:02.403+05:30", + request_ip: "[::1]:53708", + sentAt: "2020-01-24T06:29:02.364Z", + timestamp: "2020-01-24T11:59:02.403+05:30" + }, + request: { query: { whSchemaVersion: "v1" } } +}; + +const identifyMessage = { + destination: { Config: {} }, + message: { + type: "identify", + messageId: "my-identify-message-id-1", + sentAt: "2021-01-03T17:02:53.195Z", + userId: "user123", + channel: "web", + integrations: { All: true }, + context: { + os: { + "name": "android", + "version": "1.12.3" + }, + app: { + name: "RudderLabs JavaScript SDK", + build: "1.0.0", + version: "1.1.11", + namespace: "com.rudderlabs.javascript" + }, + traits: { + email: "user123@email.com", + phone: "+917836362334", + userId: "user123" + }, + locale: "en-US", + device: { + token: "token", + id: "id", + type: "ios" + }, + library: { + name: "RudderLabs JavaScript SDK", + version: "1.1.11" + }, + userAgent: "Gecko/20100101 Firefox/84.0" + }, + rudderId: "8f8fa6b5-8e24-489c-8e22-61f23f2e364f", + anonymousId: "97c46c81-3140-456d-b2a9-690d70aaca35", + originalTimestamp: "2020-01-24T06:29:02.364Z", + receivedAt: "2020-01-24T11:59:02.403+05:30", + request_ip: "[::1]:53708", + timestamp: "2020-01-24T11:59:02.403+05:30" + }, + request: { query: { whSchemaVersion: "v1" } } +}; + +const scenarios = [ + { + name: "track event is not skipped when options are not provided", + skipUsersTable: null, + skipTracksTable: null, + event: _.cloneDeep(trackMessage), + expected: [ + { + id: "my-track-message-id-1", + table: "tracks" + }, + { + id: "my-track-message-id-1", + table: "_groups" + } + ] + }, + { + name: "track event is not skipped when skipTracksTable is false", + skipUsersTable: null, + skipTracksTable: false, + event: _.cloneDeep(trackMessage), + expected: [ + { + id: "my-track-message-id-1", + table: "tracks" + }, + { + id: "my-track-message-id-1", + table: "_groups" + } + ] + }, + { + name: "track event is skipped when skipTracksTable is true", + skipUsersTable: null, + skipTracksTable: true, + event: _.cloneDeep(trackMessage), + expected: [ + { + id: "my-track-message-id-1", + table: "_groups" + } + ] + }, + { + name: "track event is not affected by skipUsersTable", + skipUsersTable: true, + skipTracksTable: null, + event: _.cloneDeep(trackMessage), + expected: [ + { + id: "my-track-message-id-1", + table: "tracks" + }, + { + id: "my-track-message-id-1", + table: "_groups" + } + ] + }, + { + name: "user event is not skipped when options are not provided", + skipUsersTable: null, + skipTracksTable: null, + event: _.cloneDeep(identifyMessage), + expected: [ + { + id: "my-identify-message-id-1", + table: "identifies" + }, + { + id: "user123", + table: "users" + } + ] + }, + { + name: "user event is not skipped when skipUsersTable is false", + skipUsersTable: false, + skipTracksTable: null, + event: _.cloneDeep(identifyMessage), + expected: [ + { + id: "my-identify-message-id-1", + table: "identifies" + }, + { + id: "user123", + table: "users" + } + ] + }, + { + name: "user event is skipped when skipUsersTable is true", + skipUsersTable: true, + skipTracksTable: null, + event: _.cloneDeep(identifyMessage), + expected: [ + { + id: "my-identify-message-id-1", + table: "identifies" + } + ] + }, + { + name: "user event is not affected by skipTracksTable", + skipUsersTable: null, + skipTracksTable: true, + event: _.cloneDeep(identifyMessage), + expected: [ + { + id: "my-identify-message-id-1", + table: "identifies" + }, + { + id: "user123", + table: "users" + } + ] + }, +]; + +module.exports = { + scenarios: function() { + return _.cloneDeep(scenarios); + } +}; \ No newline at end of file diff --git a/test/__tests__/eventValidation.test.js b/test/__tests__/eventValidation.test.js index eb58879eb7..b802b6f886 100644 --- a/test/__tests__/eventValidation.test.js +++ b/test/__tests__/eventValidation.test.js @@ -73,6 +73,97 @@ const trackingPlan = { create_time: "2021-12-14T19:19:13.666Z", update_time: "2021-12-15T13:29:59.272Z" }; + +const newTrackingPlan = { + name: "Demo Tracking Plan", + version: 1, + events: [ + { + id: "ev_22HzyIUtuhfoI80iDfAgf47GHpw", + name: "Product clicked new", + eventType: "track", + description: "Fired when an product is clicked.", + rules: { + type: "object", + $schema: "http://json-schema.org/draft-07/schema#", + required: ["properties"], + properties: { + properties: { + $schema: "http://json-schema.org/draft-07/schema#", + additionalProperties: false, + properties: { + email: { + type: ["string"] + }, + name: { + type: ["string"] + }, + prop_float: { + type: ["number"] + }, + prop_integer: { + type: ["number"] + }, + revenue: { + type: ["number"] + } + }, + type: "object", + required: [ + "email", + "name", + "prop_float", + "prop_integer", + "revenue" + ], + allOf: [ + { + properties: { + prop_integer: { + const: 2 + }, + prop_float: { + const: 2.3 + } + } + } + ] + } + } + } + }, + { + id: "ev_22HzyIUtuhfoI80iDfAgf47GHpx", + name: "", + eventType: "group", + rules: { + type: "object", + $schema: "http://json-schema.org/draft-07/schema#", + required: ["traits"], + properties: { + traits: { + additionalProperties: false, + properties: { + company: { + type: ["string"] + }, + org: { + type: ["string"] + }, + }, + type: "object", + required: [ + "company", + ] + } + } + } + } + ], + workspaceId: "dummy_workspace_id", + createdAt: "2021-12-14T19:19:13.666Z", + updatedAt: "2021-12-15T13:29:59.272Z" +}; const sourceTpConfig = { track: { allowUnplannedEvents: "true", @@ -1364,6 +1455,134 @@ const eventValidationTestCases = [ } ]; +const eventValidationWithNewPlanTestCases = [ + { + testCase: "Group is part of new Tracking Plan + additional property violation", + event: { + metadata: { + trackingPlanId: "dummy_tracking_plan_id_new", + trackingPlanVersion: "dummy_version_new", + workspaceId: "dummy_workspace_id", + mergedTpConfig, + sourceTpConfig + }, + message: { + type: "group", + userId: "user12345", + groupId: "group1", + traits: { + company: "Company", + employees: 123 + }, + context: { + traits: { + trait1: "new-val" + }, + ip: "14.5.67.21", + library: { + name: "http" + } + }, + timestamp: "2020-01-21T00:21:34.208Z" + } + }, + trackingPlan: newTrackingPlan, + output: { + dropEvent: true, + violationType: violationTypes.AdditionalProperties + } + }, + { + testCase: + "Compatibility for Spread sheet plugin + Track is not part of new Tracking Plan and allowUnplannedEvents is set to text TRUE", + event: { + metadata: { + trackingPlanId: "dummy_tracking_plan_id_new", + trackingPlanVersion: "dummy_version_new", + workspaceId: "dummy_workspace_id", + mergedTpConfig: { + allowUnplannedEvents: "TRUE", + ajvOptions: {} + }, + sourceTpConfig: { + track: { + allowUnplannedEvents: "TRUE", + ajvOptions: {} + }, + global: { + allowUnplannedEvents: "FALSE", + ajvOptions: {} + } + } + }, + message: { + type: "track", + userId: "user-demo", + event: "New Product clicked", + properties: { + name: "Rubik's Cube", + revenue: 4.99, + prop_integer: 2, + prop_float: 2.3, + email: "demo@rudderstack.com" + }, + context: { + ip: "14.5.67.21" + }, + timestamp: "2020-02-02T00:23:09.544Z" + } + }, + trackingPlan: newTrackingPlan, + output: { + dropEvent: false, + violationType: "None" + } + }, + { + testCase: + "Track is part of new Tracking Plan + no track config and unplannedProperties is set to drop", + event: { + metadata: { + trackingPlanId: "dummy_tracking_plan_id_new", + trackingPlanVersion: "dummy_version_new", + workspaceId: "dummy_workspace_id", + mergedTpConfig: { + unplannedProperties: "drop", + ajvOptions: {} + }, + sourceTpConfig: { + global: { + unplannedProperties: "drop", + ajvOptions: {} + } + } + }, + message: { + type: "track", + userId: "user-demo", + event: "Product clicked new", + properties: { + name: "Rubik's Cube", + revenue: 4.99, + prop_integer: 2, + prop_float: 2.3, + email: "demo@rudderstack.com", + mobile: "999888777666" + }, + context: { + ip: "14.5.67.21" + }, + timestamp: "2020-02-02T00:23:09.544Z" + } + }, + trackingPlan: newTrackingPlan, + output: { + dropEvent: true, + violationType: violationTypes.AdditionalProperties + } + }, +]; + describe("Supported Event types testing", () => { eventTypesTestCases.forEach(testCase => { it(`should return isSupportedOrNot ${testCase.output} for this input eventType ${testCase.eventType} everytime`, () => { @@ -1389,6 +1608,22 @@ describe("Handle validation", () => { }); }); +describe("Handle validation with new tracking plan payload", () => { + eventValidationWithNewPlanTestCases.forEach(testCase => { + it(`should return dropEvent: ${testCase.output.dropEvent}, violationType: ${testCase.output.violationType}`, async () => { + fetch.mockResolvedValue({ + json: jest.fn().mockResolvedValue(testCase.trackingPlan), + status: 200 + }); + const { dropEvent, violationType } = await handleValidation( + testCase.event + ); + expect(dropEvent).toEqual(testCase.output.dropEvent); + expect(violationType).toEqual(testCase.output.violationType); + }); + }); +}); + describe("HandleValidationErrors", () => { validationErrorsTestCases.forEach(testCase => { it(`should return dropEvent ${testCase.output} for ${testCase.test}`, () => { diff --git a/test/__tests__/pinterestConversion-cdk.test.ts b/test/__tests__/pinterestConversion-cdk.test.ts index f4da92eea9..2afde331d5 100644 --- a/test/__tests__/pinterestConversion-cdk.test.ts +++ b/test/__tests__/pinterestConversion-cdk.test.ts @@ -1,7 +1,8 @@ import fs from 'fs'; import path from 'path'; -import { processCdkV2Workflow, getWorkflowEngine, executeWorkflow } from '../../src/cdk/v2/handler'; +import { executeWorkflow, getWorkflowEngine, processCdkV2Workflow } from '../../src/cdk/v2/handler'; import tags from '../../src/v0/util/tags'; +import logger from '../../src/logger'; const integration = 'pinterest_tag'; const name = 'Pinterest Conversion API'; @@ -22,7 +23,12 @@ describe(`${name} Tests`, () => { it(`${name} - payload: ${index}`, async () => { const expected = expectedData[index]; try { - const output = await processCdkV2Workflow(integration, input, tags.FEATURES.PROCESSOR); + const output = await processCdkV2Workflow( + integration, + input, + tags.FEATURES.PROCESSOR, + logger, + ); expect(output).toEqual(expected); } catch (error: any) { expect(error.message).toEqual(expected.error); @@ -46,7 +52,12 @@ describe(`${name} Tests`, () => { it(`${name} - payload: ${index}`, async () => { const expected = expectedData[index]; try { - const output = await processCdkV2Workflow(integration, input, tags.FEATURES.PROCESSOR); + const output = await processCdkV2Workflow( + integration, + input, + tags.FEATURES.PROCESSOR, + logger, + ); expect(output).toEqual(expected); } catch (error: any) { expect(error.message).toEqual(expected.error); @@ -91,6 +102,7 @@ describe(`${name} Tests`, () => { integration, inputRouterErrorData, tags.FEATURES.ROUTER, + logger, ); expect(output).toEqual(expectedRouterErrorData); }); @@ -98,7 +110,12 @@ describe(`${name} Tests`, () => { describe('Default Batch size', () => { inputRouterData.forEach((input, index) => { it(`Payload: ${index}`, async () => { - const output = await processCdkV2Workflow(integration, input, tags.FEATURES.ROUTER); + const output = await processCdkV2Workflow( + integration, + input, + tags.FEATURES.ROUTER, + logger, + ); expect(output).toEqual(expectedRouterData[index]); }); }); diff --git a/test/__tests__/user_transformation.integration.test.js b/test/__tests__/user_transformation.integration.test.js index b0b8fc9cae..6598b9fa0d 100644 --- a/test/__tests__/user_transformation.integration.test.js +++ b/test/__tests__/user_transformation.integration.test.js @@ -159,6 +159,7 @@ describe("Function invocation & creation tests", () => { versionId, [], trRevCode, + [], true ); expect(response).toEqual(outputEvents); diff --git a/test/__tests__/user_transformation.test.js b/test/__tests__/user_transformation.test.js index 8b781cda9a..24ed1ae1ff 100644 --- a/test/__tests__/user_transformation.test.js +++ b/test/__tests__/user_transformation.test.js @@ -7,9 +7,11 @@ jest.mock("axios", () => ({ ...jest.requireActual("axios"), get: jest.fn(), post: jest.fn(), - delete: jest.fn() + delete: jest.fn(), + put: jest.fn() })); +const { generateFunctionName } = require('../../src/util/customTransformer-faas.js'); const { Response, Headers } = jest.requireActual("node-fetch"); const lodashCore = require("lodash/core"); const _ = require("lodash"); @@ -35,8 +37,13 @@ const { } = require("../../src/util/customTransformer"); const { parserForImport } = require("../../src/util/parser"); const { RetryRequestError, RespStatusError } = require("../../src/util/utils"); +const { buildOpenfaasFn, setFunctionInCache, invalidateFnCache } = require("../../src/util/openfaas/index"); const OPENFAAS_GATEWAY_URL = "http://localhost:8080"; +const defaultBasicAuth = { + "username": "", + "password": "" +}; const randomID = () => Math.random() @@ -84,8 +91,12 @@ const pyLibCode = (name, versionId) => { } } -const pyfaasFuncName = (workspaceId, versionId, libraryVersionIds=[]) => { - const ids = [workspaceId, versionId].concat(libraryVersionIds.sort()); +const pyfaasFuncName = (workspaceId, versionId, libraryVersionIds=[], hashSecret="") => { + let ids = [workspaceId, versionId].concat(libraryVersionIds.sort()); + if (hashSecret !== "") { + ids = ids.concat([hashSecret]); + } + const hash = crypto.createHash('md5').update(`${ids}`).digest('hex'); return `fn-${workspaceId}-${hash}` @@ -101,6 +112,19 @@ const getfetchResponse = (resp, url) => let importNameLibraryVersionIdsMap; +describe("User transformation utils", () => { + + it("generates the openfaas-fn name correctly", () => { + const fnName = generateFunctionName( + {workspaceId: 'workspaceId', transformationId: 'transformationId'}, + [], + false, + 'hash-secret'); + expect(fnName).toEqual('fn-workspaceid-34a32ade07ebbc7bc5ea795b8200de9f'); + }); + +}); + describe("User transformation", () => { beforeEach(() => { jest.resetAllMocks(); @@ -1089,6 +1113,281 @@ describe("User transformation", () => { ); expect(output).toEqual(expectedData); }); + + describe("UserTransformation With Credentials for code version 1", () => { + it(`successfully executes transformation with credential lookup with valid key`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential('key1'); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toEqual("value1"); + }); + + it(`throws TypeError if the key provided for credential lookup is null or undefined`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential(); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].error).toMatch(/Key should be valid and defined/); + }); + + it(`allows user to pass multiple arguments to functions and performs lookup with first key passed`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential('key1', 'key2'); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toEqual("value1"); + }); + + it(`allows user to pass valid key of any type and performs lookup accordingly`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValueForNumkey = credential(1); + event.credentialValueForBoolkey = credential(true); + event.credentialValueForArraykey = credential([]); + event.credentialValueForObjkey = credential({}); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValueForNumkey).toBeUndefined(); + expect(output[0].transformedEvent.credentialValueForBoolkey).toBeUndefined(); + expect(output[0].transformedEvent.credentialValueForArraykey).toBeUndefined(); + expect(output[0].transformedEvent.credentialValueForObjkey).toBeUndefined(); + }); + + it(`returns undefined when the key doesn't match any credential lookup`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential('key3'); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toBeUndefined(); + }); + + it(`returns undefined when the credentials are not passed in the request`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformEvent(event, metadata) { + event.credentialValue = credential('key1'); + return event; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue).toBeUndefined(); + }); + + describe('Batch UserTransformation with Credentials', () => { + it(`successfully executes transformation with credential lookup with valid key`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformBatch(events, metadata) { + events.forEach((event) => { + event.credentialValue1 = credential("key1"); + event.credentialValue2 = credential("key3"); + }); + return events; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].transformedEvent.credentialValue1).toEqual("value1"); + expect(output[0].transformedEvent.credentialValue2).toBeUndefined(); + }); + + it(`throws TypeError if the key provided for credential lookup is null or undefined`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "1", + name, + code: ` + export function transformBatch(events, metadata) { + events.forEach((event) => { + event.credentialValue = credential(); + }); + return events; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(fetch).toHaveBeenCalledWith( + `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}` + ); + expect(output[0].error).toMatch(/Key should be valid and defined/); + }); + + }); + + it(`throws error when credentials function is used with code version 0`, async () => { + const versionId = randomID(); + + const inputData = require(`./data/${integration}_input_credentials.json`); + + const respBody = { + versionId: versionId, + codeVersion: "0", + name, + code: ` + function transform(events) { + events.forEach((event) => { + event.credentialValue = credential('key1'); + }); + return events; + } + ` + }; + fetch.mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + try { + await userTransformHandler(inputData, versionId, []); + } catch (e) { + expect(e).toEqual('credential is not defined'); + } + }); + }); }); // Running timeout tests @@ -1382,6 +1681,7 @@ describe("Geolocation function", () => { // Running tests for python transformations with openfaas mocks describe("Python transformations", () => { beforeEach(() => { + invalidateFnCache(); jest.resetAllMocks(); }); afterAll(() => {}); @@ -1400,12 +1700,14 @@ describe("Python transformations", () => { expect(axios.post).toHaveBeenCalledTimes(1); expect(axios.post).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/system/functions`, - expect.objectContaining({ name: funcName, service: funcName }) + expect.objectContaining({ name: funcName, service: funcName }), + { auth: defaultBasicAuth }, ); expect(axios.get).toHaveBeenCalledTimes(1); expect(axios.get).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}} + {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, + { auth: defaultBasicAuth }, ); }); @@ -1415,6 +1717,7 @@ describe("Python transformations", () => { const expectedData = { success: true, publishedVersion: funcName }; + setFunctionInCache(funcName); const output = await setupUserTransformHandler([], trRevCode); expect(output).toEqual(expectedData); expect(axios.post).toHaveBeenCalledTimes(0); @@ -1598,7 +1901,7 @@ describe("Python transformations", () => { expect(axios.delete).toHaveBeenCalledTimes(1); }); - it("Simple transformation run - invokes faas function", async () => { + it("Simple transformation run with function in cache - invokes faas function", async () => { const inputData = require(`./data/${integration}_input.json`); const outputData = require(`./data/${integration}_output.json`); @@ -1606,6 +1909,8 @@ describe("Python transformations", () => { const respBody = pyTrRevCode(versionId); const funcName = pyfaasFuncName(respBody.workspaceId, versionId); + setFunctionInCache(funcName); + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; when(fetch) .calledWith(transformerUrl) @@ -1619,19 +1924,24 @@ describe("Python transformations", () => { const output = await userTransformHandler(inputData, versionId, []); expect(output).toEqual(outputData); - expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - inputData + inputData, + { auth: defaultBasicAuth }, ); }); - it("Simple transformation run - function not found", async () => { + + it("Simple transformation run with clean cache - reconciles fn with 200OK and then invokes faas function", async () => { + const inputData = require(`./data/${integration}_input.json`); + const outputData = require(`./data/${integration}_output.json`); const versionId = randomID(); const respBody = pyTrRevCode(versionId); - const funcName = pyfaasFuncName(respBody.workspaceId, respBody.versionId); + const funcName = pyfaasFuncName(respBody.workspaceId, versionId); + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; when(fetch) @@ -1641,34 +1951,170 @@ describe("Python transformations", () => { json: jest.fn().mockResolvedValue(respBody) }); - axios.post - .mockRejectedValueOnce({ - response: { status: 404, data: `error finding function ${funcName}` } // invoke function not found - }) - .mockResolvedValueOnce({}); // create function + axios.put.mockResolvedValue({}); axios.get.mockResolvedValue({}); // awaitFunctionReadiness() + axios.post.mockResolvedValue({ data: { transformedEvents: outputData } }); - await expect(async () => { - await userTransformHandler(inputData, versionId, []); - }).rejects.toThrow(RetryRequestError); + const output = await userTransformHandler(inputData, versionId, []); + expect(output).toEqual(outputData); - expect(axios.post).toHaveBeenCalledTimes(2); - expect(axios.post).toHaveBeenCalledWith( - `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - inputData - ); - expect(axios.post).toHaveBeenCalledWith( - `${OPENFAAS_GATEWAY_URL}/system/functions`, - expect.objectContaining({ name: funcName, service: funcName }) - ); expect(axios.get).toHaveBeenCalledTimes(1); expect(axios.get).toHaveBeenCalledWith( `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, - {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}} + {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, + { auth: defaultBasicAuth }, + ); + expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + buildOpenfaasFn(funcName, null, versionId, [], false, {}), + { auth: defaultBasicAuth }); + expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + inputData, + { auth: defaultBasicAuth }, ); }); + describe("Simple transformation run with clean cache - function not found", () => { + + it('eventually sets up the function on 404 from update and then invokes it', async () => { + const inputData = require(`./data/${integration}_input.json`); + + const versionId = randomID(); + const respBody = pyTrRevCode(versionId); + const funcName = pyfaasFuncName(respBody.workspaceId, respBody.versionId); + + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; + when(fetch) + .calledWith(transformerUrl) + .mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + + axios.put.mockRejectedValueOnce({ + response: { status: 404, data: `deployment not found`} + }); + + axios.post + .mockRejectedValueOnce({ + response: { status: 404, data: `error finding function ${funcName}` } // invoke function not found + }) + .mockResolvedValueOnce({}); // create function + axios.get.mockResolvedValue({}); // awaitFunctionReadiness() + + await expect(async () => { + await userTransformHandler(inputData, versionId, []); + }).rejects.toThrow(RetryRequestError); + + expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + buildOpenfaasFn(funcName, null, versionId, [], false, {}), + { auth: defaultBasicAuth }, + ); + expect(axios.post).toHaveBeenCalledTimes(2); + expect(axios.post).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + inputData, + { auth: defaultBasicAuth }, + ); + expect(axios.post).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + expect.objectContaining({ name: funcName, service: funcName }), + { auth: defaultBasicAuth }, + ); + + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, + { auth: defaultBasicAuth }, + ); + }); + + it('sets up the function on 202 from update and then invokes it', async() => { + const inputData = require(`./data/${integration}_input.json`); + const outputData = require(`./data/${integration}_output.json`); + + const versionId = randomID(); + const respBody = pyTrRevCode(versionId); + const funcName = pyfaasFuncName(respBody.workspaceId, respBody.versionId); + + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; + when(fetch) + .calledWith(transformerUrl) + .mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + + axios.put.mockResolvedValueOnce({ + response: { status: 202, data: `deployment created`} + }); + axios.get.mockResolvedValue({}); // awaitFunctionReadiness() + axios.post.mockResolvedValue({ data: { transformedEvents: outputData } }); + + const output = await userTransformHandler(inputData, versionId, []); + expect(output).toEqual(outputData); + + expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + buildOpenfaasFn(funcName, null, versionId, [], false, {}), + { auth: defaultBasicAuth }, + ); + expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + inputData, + { auth: defaultBasicAuth }, + ); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/function/${funcName}`, + {"headers": {"X-REQUEST-TYPE": "HEALTH-CHECK"}}, + { auth: defaultBasicAuth }, + ); + }); + + it('throws from the userTransform handler when reconciles errors with anything other than 404', async() => { + const inputData = require(`./data/${integration}_input.json`); + const outputData = require(`./data/${integration}_output.json`); + + const versionId = randomID(); + const respBody = pyTrRevCode(versionId); + const funcName = pyfaasFuncName(respBody.workspaceId, respBody.versionId); + + const transformerUrl = `https://api.rudderlabs.com/transformation/getByVersionId?versionId=${versionId}`; + when(fetch) + .calledWith(transformerUrl) + .mockResolvedValue({ + status: 200, + json: jest.fn().mockResolvedValue(respBody) + }); + + + axios.put.mockRejectedValueOnce({response: {status: 400, data: 'bad request'}}); + await expect(async () => { + await userTransformHandler(inputData, versionId, []); + }).rejects.toThrow(RespStatusError); + + expect(axios.put).toHaveBeenCalledTimes(1); + expect(axios.put).toHaveBeenCalledWith( + `${OPENFAAS_GATEWAY_URL}/system/functions`, + buildOpenfaasFn(funcName, null, versionId, [], false, {}), + { auth: defaultBasicAuth }, + ); + }); + + }); + it("Simple transformation run - error requests", async () => { const inputData = require(`./data/${integration}_input.json`); @@ -1684,6 +2130,8 @@ describe("Python transformations", () => { json: jest.fn().mockResolvedValue(respBody) }); + setFunctionInCache(funcName); + axios.post .mockRejectedValueOnce({ response: { status: 429, data: `Rate limit exceeded` } // invoke function with rate limit diff --git a/test/__tests__/warehouse.test.js b/test/__tests__/warehouse.test.js index 772e59e65a..2c89120686 100644 --- a/test/__tests__/warehouse.test.js +++ b/test/__tests__/warehouse.test.js @@ -8,6 +8,7 @@ const { opOutput } = require(`./data/warehouse/integration_options_events.js`); const { names } = require(`./data/warehouse/names.js`); +const destConfig = require(`./data/warehouse/dest_config_scenarios.js`); const { largeNoOfColumnsevent } = require(`./data/warehouse/event_columns_length`); @@ -1009,6 +1010,28 @@ describe("Add receivedAt for events missing it", () => { }); describe("Integration options", () => { + describe("Destination config options", () => { + destConfig.scenarios().forEach(scenario => { + it(scenario.name, () => { + if (scenario.skipUsersTable !== null) { + scenario.event.destination.Config.skipUsersTable = scenario.skipUsersTable + } + if (scenario.skipTracksTable !== null) { + scenario.event.destination.Config.skipTracksTable = scenario.skipTracksTable + } + + transformers.forEach((transformer, index) => { + const received = transformer.process(scenario.event); + expect(received).toHaveLength(scenario.expected.length); + for (const i in received) { + const evt = received[i]; + expect(evt.data.id ? evt.data.id : evt.data.ID).toEqual(scenario.expected[i].id); + expect(evt.metadata.table.toLowerCase()).toEqual(scenario.expected[i].table); + } + }); + }); + }); + }); describe("track", () => { it("should generate two events for every track call", () => { const i = opInput("track"); diff --git a/test/apitests/service.api.test.ts b/test/apitests/service.api.test.ts index 266619b6ac..e46357f824 100644 --- a/test/apitests/service.api.test.ts +++ b/test/apitests/service.api.test.ts @@ -1,13 +1,13 @@ import fs from 'fs'; -import path from 'path'; -import request from 'supertest'; import { createHttpTerminator } from 'http-terminator'; import Koa from 'koa'; import bodyParser from 'koa-bodyparser'; +import path from 'path'; import setValue from 'set-value'; -import { applicationRoutes } from '../../src/routes'; -import { FetchHandler } from '../../src/helpers/fetchHandlers'; +import request from 'supertest'; import networkHandlerFactory from '../../src/adapters/networkHandlerFactory'; +import { FetchHandler } from '../../src/helpers/fetchHandlers'; +import { applicationRoutes } from '../../src/routes'; let server: any; const OLD_ENV = process.env; diff --git a/test/integrations/common/network.ts b/test/integrations/common/network.ts index 8b0ed16c72..a6ab202a4e 100644 --- a/test/integrations/common/network.ts +++ b/test/integrations/common/network.ts @@ -81,4 +81,15 @@ export const networkCallsData = [ status: 429, }, }, + { + description: 'Mock response depicting DNS lookup failure error', + httpReq: { + method: 'post', + url: 'https://random_test_url/dns_lookup_failure', + }, + httpRes: { + data: {}, + status: 400, + }, + }, ]; diff --git a/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts b/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts new file mode 100644 index 0000000000..76e07690cf --- /dev/null +++ b/test/integrations/destinations/adobe_analytics/dataDelivery/business.ts @@ -0,0 +1,181 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'ADOBE_ANALYTICS', + destinationId: 'dummyDestinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; +const headers = { + 'Content-Type': 'application/xml', +}; + +export const reqMetadataArray = [proxyMetdata]; + +const failureRequestParameters = { + XML: { + payload: + '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', + }, + params: {}, +}; + +const successRequestParameters = { + XML: { + payload: + '127.0.1.0www.google.co.inGoogleid1110011prodViewGames;Monopoly;1;14.00,Games;UNO;2;6.90successreport', + }, + params: {}, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'adobe_analytics_v1_scenario_1', + name: 'adobe_analytics', + description: '[Proxy v1 API] :: Test for Failure response from Adobe Analytics with reason', + successCriteria: 'Should return a 400 status code with reason', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...failureRequestParameters, + headers, + endpoint: 'https://adobe.failure.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics : NO pagename OR pageurl', + response: [ + { + error: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics : NO pagename OR pageurl', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'adobe_analytics_v1_scenario_2', + name: 'adobe_analytics', + description: + '[Proxy v1 API] :: Test for Failure response from Adobe Analytics without reason (Generic error)', + successCriteria: 'Should return a 400 status code with a general error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...failureRequestParameters, + headers, + endpoint: 'https://adobe.failure2.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics with a general error', + response: [ + { + error: + '[ADOBE_ANALYTICS Response Handler] Request failed for destination adobe_analytics with a general error', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'adobe_analytics_v1_scenario_3', + name: 'adobe_analytics', + description: '[Proxy v1 API] :: Test for Success response from Adobe Analytics', + successCriteria: 'Should return a 200 status code with status SUCCESS', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...successRequestParameters, + headers, + endpoint: 'https://adobe.success.omtrdc.net/b/ss//6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ADOBE_ANALYTICS] - Request Processed Successfully', + response: [ + { + error: '"SUCCESS"', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts b/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts index 182969da73..2535a0639e 100644 --- a/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts +++ b/test/integrations/destinations/adobe_analytics/dataDelivery/data.ts @@ -1,4 +1,6 @@ -export const data = [ +import { testScenariosForV1API } from './business'; + +const legacyTests = [ { name: 'adobe_analytics', description: 'Test 0: Failure response from Adobe Analytics with reason', @@ -72,7 +74,7 @@ export const data = [ JSON_ARRAY: {}, XML: { payload: - '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReportgeneric', + '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', }, FORM: {}, }, @@ -140,3 +142,5 @@ export const data = [ }, }, ]; + +export const data = [...testScenariosForV1API, ...legacyTests]; diff --git a/test/integrations/destinations/adobe_analytics/network.ts b/test/integrations/destinations/adobe_analytics/network.ts index 2fe4f0204e..7e32c5f10b 100644 --- a/test/integrations/destinations/adobe_analytics/network.ts +++ b/test/integrations/destinations/adobe_analytics/network.ts @@ -17,7 +17,7 @@ export const networkCallsData = [ { httpReq: { url: 'https://adobe.failure2.omtrdc.net/b/ss//6', - data: '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReportgeneric', + data: '17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerprodViewGames;;11;148.39failureReport', params: {}, headers: { 'Content-Type': 'application/xml', diff --git a/test/integrations/destinations/af/processor/data.ts b/test/integrations/destinations/af/processor/data.ts index d0fd29b089..9812f7f902 100644 --- a/test/integrations/destinations/af/processor/data.ts +++ b/test/integrations/destinations/af/processor/data.ts @@ -1,4 +1,56 @@ -export const data = [ +import { newConfigValidationTests } from './validation'; + +const commonPropertiesWithProduct = { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'ZAR', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + products: [ + { + sku: '45790-32', + url: 'https://www.example.com/product/path', + name: 'Monopoly: 3rd Edition', + price: 19, + category: 'Games', + quantity: 1, + image_url: 'https:///www.example.com/product/path.jpg', + product_id: '507f1f77bcf86cd799439011', + }, + { + sku: '46493-32', + name: 'Uno Card Game', + price: 3, + category: 'Games', + quantity: 2, + product_id: '505bd76785ebb509fc183733', + }, + ], + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', +}; + +const commonContextWithExternalId = { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + source: 'test', + app: { namespace: 'com.rudderlabs.javascript' }, + os: { name: 'android' }, + traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, + library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, +}; + +const commonHeader = { 'Content-Type': 'application/json', authentication: 'abcde' }; + +const commonV2EndPoint = 'https://api3.appsflyer.com/inappevent/com.rudderlabs.javascript'; +const commonV1EndPoint = 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript'; + +export const existingTestCases = [ { name: 'af', description: 'Test 0', @@ -43,7 +95,7 @@ export const data = [ integrations: { AF: { af_uid: 'afUid' } }, }, destination: { - Config: { devKey: 'ef1d42390426e3f7c90ac78272e74344', androidAppId: 'appId' }, + Config: { devKey: 'abcde', androidAppId: 'appId' }, Enabled: true, addPropertiesAtRoot: false, }, @@ -117,7 +169,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', addPropertiesAtRoot: false, }, @@ -136,11 +188,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -208,7 +257,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -227,11 +276,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -305,7 +351,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', addPropertiesAtRoot: false, }, @@ -397,7 +443,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -474,7 +520,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -493,11 +539,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -568,7 +611,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -586,11 +629,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -661,7 +701,7 @@ export const data = [ integrations: { AF: { af_uid: 'afUid' } }, }, destination: { - Config: { devKey: 'ef1d42390426e3f7c90ac78272e74344', appleAppId: '123456789' }, + Config: { devKey: 'abcde', appleAppId: '123456789' }, Enabled: true, }, }, @@ -678,10 +718,7 @@ export const data = [ version: '1', type: 'REST', endpoint: 'https://api2.appsflyer.com/inappevent/id123456789', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -722,51 +759,10 @@ export const data = [ type: 'track', event: 'Order Completed', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', - properties: { - tax: 2, - total: 27.5, - coupon: 'hasbros', - revenue: 48, - price: 25, - quantity: 2, - currency: 'ZAR', - discount: 2.5, - order_id: '50314b8e9bcf000000000000', - products: [ - { - sku: '45790-32', - url: 'https://www.example.com/product/path', - name: 'Monopoly: 3rd Edition', - price: 19, - category: 'Games', - quantity: 1, - image_url: 'https:///www.example.com/product/path.jpg', - product_id: '507f1f77bcf86cd799439011', - }, - { - sku: '46493-32', - name: 'Uno Card Game', - price: 3, - category: 'Games', - quantity: 2, - product_id: '505bd76785ebb509fc183733', - }, - ], - shipping: 3, - subtotal: 22.5, - affiliation: 'Google Store', - checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', - }, + properties: commonPropertiesWithProduct, anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', integrations: { AF: { af_uid: 'afUid' } }, }, @@ -794,8 +790,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -834,14 +830,7 @@ export const data = [ type: 'track', event: 'Order Completed', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', properties: { @@ -886,8 +875,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -926,14 +915,7 @@ export const data = [ type: 'track', event: 'Order Completed', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', @@ -963,8 +945,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1001,51 +983,10 @@ export const data = [ type: 'track', event: 'normal track event', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', - properties: { - tax: 2, - total: 27.5, - coupon: 'hasbros', - revenue: 48, - price: 25, - quantity: 2, - currency: 'ZAR', - discount: 2.5, - order_id: '50314b8e9bcf000000000000', - products: [ - { - sku: '45790-32', - url: 'https://www.example.com/product/path', - name: 'Monopoly: 3rd Edition', - price: 19, - category: 'Games', - quantity: 1, - image_url: 'https:///www.example.com/product/path.jpg', - product_id: '507f1f77bcf86cd799439011', - }, - { - sku: '46493-32', - name: 'Uno Card Game', - price: 3, - category: 'Games', - quantity: 2, - product_id: '505bd76785ebb509fc183733', - }, - ], - shipping: 3, - subtotal: 22.5, - affiliation: 'Google Store', - checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', - }, + properties: commonPropertiesWithProduct, anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', integrations: { AF: { af_uid: 'afUid' } }, }, @@ -1057,6 +998,7 @@ export const data = [ groupValueTrait: 'age', trackProductsOnce: false, trackRevenuePerProduct: false, + afCurrencyAtRoot: true, }, }, }, @@ -1073,13 +1015,13 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { eventValue: - '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_quantity":2,"af_price":25}', + '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_quantity":2,"af_price":25,"af_currency":"ZAR"}', eventName: 'normal track event', eventTime: '2020-08-14T05:30:30.118Z', eventCurrency: 'ZAR', @@ -1113,14 +1055,7 @@ export const data = [ type: 'track', event: 'normal track event', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', @@ -1150,8 +1085,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1218,7 +1153,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -1237,11 +1172,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1312,7 +1244,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', sharingFilter: ['hello'], }, @@ -1331,11 +1263,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -1408,7 +1337,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', sharingFilter: 'all', }, @@ -1427,11 +1356,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -1504,7 +1430,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', }, Enabled: true, @@ -1580,7 +1506,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', sharingFilter: 'all', addPropertiesAtRoot: true, @@ -1600,11 +1526,8 @@ export const data = [ output: { version: '1', type: 'REST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, method: 'POST', params: {}, body: { @@ -1673,7 +1596,7 @@ export const data = [ }, destination: { Config: { - devKey: 'ef1d42390426e3f7c90ac78272e74344', + devKey: 'abcde', androidAppId: 'com.rudderlabs.javascript', addPropertiesAtRoot: true, }, @@ -1693,11 +1616,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { - 'Content-Type': 'application/json', - authentication: 'ef1d42390426e3f7c90ac78272e74344', - }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1738,51 +1658,10 @@ export const data = [ type: 'track', event: 'Order Completed', sentAt: '2020-08-14T05:30:30.118Z', - context: { - externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], - source: 'test', - app: { namespace: 'com.rudderlabs.javascript' }, - os: { name: 'android' }, - traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, - library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, - }, + context: commonContextWithExternalId, messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', timestamp: '2020-08-14T05:30:30.118Z', - properties: { - tax: 2, - total: 27.5, - coupon: 'hasbros', - revenue: 48, - price: 25, - quantity: 2, - currency: 'ZAR', - discount: 2.5, - order_id: '50314b8e9bcf000000000000', - products: [ - { - sku: '45790-32', - url: 'https://www.example.com/product/path', - name: 'Monopoly: 3rd Edition', - price: 19, - category: 'Games', - quantity: 1, - image_url: 'https:///www.example.com/product/path.jpg', - product_id: '507f1f77bcf86cd799439011', - }, - { - sku: '46493-32', - name: 'Uno Card Game', - price: 3, - category: 'Games', - quantity: 2, - product_id: '505bd76785ebb509fc183733', - }, - ], - shipping: 3, - subtotal: 22.5, - affiliation: 'Google Store', - checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', - }, + properties: commonPropertiesWithProduct, anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', integrations: { AF: { af_uid: 'afUid' } }, }, @@ -1811,8 +1690,8 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript', - headers: { 'Content-Type': 'application/json', authentication: 'abcde' }, + endpoint: commonV1EndPoint, + headers: commonHeader, params: {}, body: { JSON: { @@ -1838,3 +1717,5 @@ export const data = [ }, }, ]; + +export const data = [...existingTestCases, ...newConfigValidationTests]; diff --git a/test/integrations/destinations/af/processor/validation.ts b/test/integrations/destinations/af/processor/validation.ts new file mode 100644 index 0000000000..8042570c7d --- /dev/null +++ b/test/integrations/destinations/af/processor/validation.ts @@ -0,0 +1,2048 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + generateSimplifiedTrackPayload, + overrideDestination, + transformResultBuilder, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'AF', + DestinationDefinition: { + ID: '123', + Name: 'AF', + DisplayName: 'Appsflyer', + Config: {}, + }, + Config: { + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + addPropertiesAtRoot: true, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const commonProperties = { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, +}; + +const commonPropertiesWithProduct = { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'ZAR', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + products: [ + { + sku: '45790-32', + url: 'https://www.example.com/product/path', + name: 'Monopoly: 3rd Edition', + price: 19, + category: 'Games', + quantity: 1, + image_url: 'https:///www.example.com/product/path.jpg', + product_id: '507f1f77bcf86cd799439011', + }, + { + sku: '46493-32', + name: 'Uno Card Game', + price: 3, + category: 'Games', + quantity: 2, + product_id: '505bd76785ebb509fc183733', + }, + ], + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', +}; + +const commonContextWithExternalId = { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + source: 'test', + app: { namespace: 'com.rudderlabs.javascript' }, + os: { name: 'android' }, + traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1' }, + library: { name: 'rudder-sdk-ruby-sync', version: '1.0.6' }, +}; + +const commonHeader = { 'Content-Type': 'application/json', authentication: 'abcde' }; + +const commonV2EndPoint = 'https://api3.appsflyer.com/inappevent/com.rudderlabs.javascript'; +const commonV1EndPoint = 'https://api2.appsflyer.com/inappevent/com.rudderlabs.javascript'; + +export const existingTestCases = [ + { + name: 'af', + description: 'Test 0', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + page: { path: '', referrer: '', search: '', title: '', url: '' }, + screen: { density: 2 }, + }, + type: 'identify', + messageId: '50360b9c-ea8d-409c-b672-c9230f91cce5', + originalTimestamp: '2019-10-15T09:35:31.288Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + userProperties: { test_key: 'test value' }, + sentAt: '2019-10-14T09:03:22.563Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { devKey: 'abcde', androidAppId: 'appId' }, + Enabled: true, + addPropertiesAtRoot: false, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'message type not supported', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 1', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + addPropertiesAtRoot: false, + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 2', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { email: 'testhubspot2@email.com', name: 'Test Hubspot' }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'track', + messageId: '08829772-d991-427c-b976-b4c4f4430b4e', + originalTimestamp: '2019-10-15T09:35:31.291Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + event: 'test track event HS', + properties: { user_actual_role: 'system_admin, system_user', user_actual_id: 12345 }, + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + eventValue: + '{"properties":{"user_actual_role":"system_admin, system_user","user_actual_id":12345}}', + eventName: 'test track event HS', + customer_user_id: '12345', + ip: '0.0.0.0', + os: '', + appsflyer_id: 'afUid', + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 3', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '5094f5704b9cf2b3', + channel: 'mobile', + context: { + app: { + build: '1', + name: 'LeanPlumIntegrationAndroid', + namespace: 'com.android.SampleLeanPlum', + version: '1.0', + }, + device: { + id: '5094f5704b9cf2b3', + manufacturer: 'Google', + model: 'Android SDK built for x86', + name: 'generic_x86', + type: 'android', + }, + library: { name: 'com.rudderstack.android.sdk.core', version: '1.0.1-beta.1' }, + locale: 'en-US', + network: { carrier: 'Android', bluetooth: false, cellular: true, wifi: true }, + os: { name: 'Android', version: '8.1.0' }, + screen: { density: 420, height: 1794, width: 1080 }, + timezone: 'Asia/Kolkata', + traits: { anonymousId: '5094f5704b9cf2b3' }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 8.1.0; Android SDK built for x86 Build/OSM1.180201.007)', + }, + event: 'MainActivity', + integrations: { All: true }, + messageId: 'id1', + properties: { name: 'MainActivity', automatic: true }, + originalTimestamp: '2020-03-12T09:05:03.421Z', + type: 'screen', + sentAt: '2020-03-12T09:05:13.042Z', + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + addPropertiesAtRoot: false, + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Appsflyer id is not set. Rejecting the event', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 4', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { email: 'test@rudderstack.com' }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'track', + messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', + originalTimestamp: '2019-10-14T11:15:18.300Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + event: 'checkout started', + properties: { + currency: 'CAD', + products: [ + { + product_id: 'pr1', + quantity: 1, + price: 24.75, + name: 'my product', + sku: 'p-298', + }, + { + product_id: 'pr2', + quantity: 1, + price: 24.75, + name: 'my product 2', + sku: 'p-299', + }, + ], + step: 1, + paymentMethod: 'Visa', + testDimension: true, + testMetric: true, + }, + integrations: { All: true }, + sentAt: '2019-10-14T11:15:53.296Z', + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Appsflyer id is not set. Rejecting the event', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 5', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [ + { id: 'some_other2345_sample_external_id', type: 'appsflyerExternalId' }, + ], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'some_other2345_sample_external_id', + os: '', + ip: '0.0.0.0', + app_version_name: '1.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 6', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 7', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'ios', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { devKey: 'abcde', appleAppId: '123456789' }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: 'https://api2.appsflyer.com/inappevent/id123456789', + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 8', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_price":[19,3],"af_quantity":[1,2],"af_order_id":"50314b8e9bcf000000000000","af_content_id":["507f1f77bcf86cd799439011","505bd76785ebb509fc183733"]}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 9', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'ZAR', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_price":25,"af_quantity":2,"af_order_id":"50314b8e9bcf000000000000"}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 10', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: '', + eventName: 'Order Completed', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 11', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'normal track event', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + afCurrencyAtRoot: true, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + eventValue: + '{"properties":{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f"},"af_revenue":48,"af_quantity":2,"af_price":25,"af_currency":"ZAR"}', + eventName: 'normal track event', + eventTime: '2020-08-14T05:30:30.118Z', + eventCurrency: 'ZAR', + appsflyer_id: 'afUid', + bundleIdentifier: 'com.rudderlabs.javascript', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 12', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'normal track event', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: '', + eventName: 'normal track event', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 13', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 14', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + sharingFilter: ['hello'], + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + sharing_filter: ['hello'], + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 15', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + sharingFilter: 'all', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + sharing_filter: 'all', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Test 16', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: '', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'os name is required along with the respective appId eg. (os->android & Android App Id is required) or (os->ios & Apple App Id is required)', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Place Properties at root level Page Call', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testhubspot2@email.com', + name: 'Test Hubspot', + anonymousId: '12345', + }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'page', + messageId: 'e8585d9a-7137-4223-b295-68ab1b17dad7', + originalTimestamp: '2019-10-15T09:35:31.289Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { path: '', referrer: '', search: '', title: '', url: '' }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + sharingFilter: 'all', + addPropertiesAtRoot: true, + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + endpoint: commonV1EndPoint, + headers: commonHeader, + method: 'POST', + params: {}, + body: { + JSON: { + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + customer_user_id: '12345', + eventValue: '{"path":"","referrer":"","search":"","title":"","url":""}', + eventName: 'page', + appsflyer_id: 'afUid', + os: '', + ip: '0.0.0.0', + sharing_filter: 'all', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Place properties at root level track call', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + externalId: [{ type: 'appsflyerExternalId', id: 'afUid' }], + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { email: 'testhubspot2@email.com', name: 'Test Hubspot' }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-GB', + ip: '0.0.0.0', + os: { name: 'android', version: '' }, + screen: { density: 2 }, + }, + type: 'track', + messageId: '08829772-d991-427c-b976-b4c4f4430b4e', + originalTimestamp: '2019-10-15T09:35:31.291Z', + anonymousId: '00000000000000000000000000', + userId: '12345', + event: 'test track event HS', + properties: { user_actual_role: 'system_admin, system_user', user_actual_id: 12345 }, + sentAt: '2019-10-14T11:15:53.296Z', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + addPropertiesAtRoot: true, + }, + Enabled: true, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + eventValue: + '{"user_actual_role":"system_admin, system_user","user_actual_id":12345}', + eventName: 'test track event HS', + customer_user_id: '12345', + ip: '0.0.0.0', + os: '', + appsflyer_id: 'afUid', + app_version_name: '1.0.0', + bundleIdentifier: 'com.rudderlabs.javascript', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'af', + description: 'Place properties at root track call with af data', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + destination: { + Config: { + devKey: 'abcde', + androidAppId: 'com.rudderlabs.javascript', + groupTypeTrait: 'email', + groupValueTrait: 'age', + trackProductsOnce: false, + trackRevenuePerProduct: false, + addPropertiesAtRoot: true, + }, + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + params: {}, + body: { + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","af_revenue":48,"af_price":[19,3],"af_quantity":[1,2],"af_order_id":"50314b8e9bcf000000000000","af_content_id":["507f1f77bcf86cd799439011","505bd76785ebb509fc183733"]}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + eventTime: '2020-08-14T05:30:30.118Z', + appsflyer_id: 'afUid', + }, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, +]; + +export const newConfigValidationTests: ProcessorTestData[] = [ + { + id: 'af-config-validation-test-1', + name: 'af', + description: 'No authentication Key is present', + scenario: 'Framework', + successCriteria: + 'The request should fail as atleast one authentication key is must to propagate request', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: {}, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonProperties, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'No authentication key is present. Aborting.', + statTags: { + destinationId: 'default-destinationId', + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'af-config-validation-test-2', + name: 'af', + description: 'No dev key present even if v1 authorization is chosen', + scenario: 'Framework', + successCriteria: + 'The request should fail when authorization is of v1 type and there is no dev key', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { + authVersion: 'v1', + s2sKey: 'dummy', + }), + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: {}, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonProperties, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'dev key is mandatory for v1 authorization. Aborting.', + statTags: { + destType: 'AF', + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'af-config-validation-test-3', + name: 'af', + description: 'No dev key present even if v1 authorization is chosen', + scenario: 'Framework', + successCriteria: + 'The request should fail when authorization is of v1 type and there is no dev key', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { + authVersion: 'v2', + devKey: 'abcde', + }), + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: {}, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonProperties, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 's2s key is mandatory for v2 authorization. Aborting.', + statTags: { + destType: 'AF', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'af-config-validation-test-4', + name: 'af', + description: 'New Config and old way of authentication', + scenario: 'Business', + successCriteria: 'Response should contain only event payload and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { + authVersion: 'v1', + devKey: 'abcde', + }), + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: commonV1EndPoint, + headers: commonHeader, + JSON: { + customer_user_id: 'default-user-id', + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","af_revenue":48,"af_price":[19,3],"af_quantity":[1,2],"af_order_id":"50314b8e9bcf000000000000","af_content_id":["507f1f77bcf86cd799439011","505bd76785ebb509fc183733"]}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + appsflyer_id: 'afUid', + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'af-config-validation-test-5', + name: 'af', + description: 'New Config and new way of authentication', + scenario: 'Business', + successCriteria: 'Response should contain only event payload and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { + authVersion: 'v2', + s2sKey: 'dummy', + }), + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Order Completed', + sentAt: '2020-08-14T05:30:30.118Z', + context: commonContextWithExternalId, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: commonPropertiesWithProduct, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { AF: { af_uid: 'afUid' } }, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: commonV2EndPoint, + headers: { ...commonHeader, authentication: 'dummy' }, + JSON: { + bundleIdentifier: 'com.rudderlabs.javascript', + eventValue: + '{"tax":2,"total":27.5,"coupon":"hasbros","revenue":48,"price":25,"quantity":2,"currency":"ZAR","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"sku":"45790-32","url":"https://www.example.com/product/path","name":"Monopoly: 3rd Edition","price":19,"category":"Games","quantity":1,"image_url":"https:///www.example.com/product/path.jpg","product_id":"507f1f77bcf86cd799439011"},{"sku":"46493-32","name":"Uno Card Game","price":3,"category":"Games","quantity":2,"product_id":"505bd76785ebb509fc183733"}],"shipping":3,"subtotal":22.5,"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","af_revenue":48,"af_price":[19,3],"af_quantity":[1,2],"af_order_id":"50314b8e9bcf000000000000","af_content_id":["507f1f77bcf86cd799439011","505bd76785ebb509fc183733"]}', + eventName: 'Order Completed', + eventCurrency: 'ZAR', + customer_user_id: 'default-user-id', + appsflyer_id: 'afUid', + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/algolia/dataDelivery/business.ts b/test/integrations/destinations/algolia/dataDelivery/business.ts new file mode 100644 index 0000000000..8ba964e2dd --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/business.ts @@ -0,0 +1,331 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV0Payload, generateProxyV1Payload } from '../../../testUtils'; +import { abortStatTags, commonRequestProperties, metadataArray, retryStatTags } from './constant'; +const proxyMetdata3 = { + jobId: 3, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; +export const testScenariosForV0API = [ + { + id: 'algolia_v0_bussiness_scenario_1', + name: 'algolia', + description: '[Proxy v0 API] :: algolia all valid events', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Generic Response Handler] Request for destination: algolia Processed Successfully', + destinationResponse: { + response: { + message: 'OK', + status: 200, + }, + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_bussiness_scenario_2', + name: 'algolia', + description: '[Proxy v0 API] :: algolia with invalid event', + successCriteria: 'Error Response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.singleInValidEvent, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 422, + body: { + output: { + status: 422, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 422', + destinationResponse: { + response: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + statTags: abortStatTags, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_bussiness_scenario_3', + name: 'algolia', + description: '[Proxy v0 API] :: algolia with invalid events in batch', + successCriteria: 'Error Response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.combinedValidInvalidEvents, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 422, + body: { + output: { + status: 422, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 422', + destinationResponse: { + response: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + statTags: abortStatTags, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'algolia_v1_bussiness_scenario_1', + name: 'algolia', + description: '[Proxy v1 API] :: algolia all valid events in batch', + successCriteria: 'Success response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.multipleValidEvent, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[ALGOLIA Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + message: 'OK', + status: 200, + }, + status: 200, + }, + response: [ + { + error: 'success', + metadata: metadataArray[0], + statusCode: 200, + }, + { + error: 'success', + metadata: metadataArray[1], + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'algolia_v1_bussiness_scenario_2', + name: 'algolia', + description: '[Proxy v1 API] :: algolia all invalid events in batch', + successCriteria: 'Send response with dontBatch as true', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.singleInValidEvent, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + response: [ + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + statusCode: 500, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_bussiness_scenario_3', + name: 'algolia', + description: '[Proxy v1 API] :: algolia combination of valid and invalid events in batch', + successCriteria: 'Should use dontBatch true and proper response returned', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestProperties.commonHeaders, + endpoint: 'https://insights.algolia.io/1/events', + JSON: commonRequestProperties.combinedValidInvalidEvents, + }, + [...metadataArray, proxyMetdata3], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + response: [ + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 2, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + { + error: + '{"status":422,"message":"EventType must be one of \\"click\\", \\"conversion\\" or \\"view\\""}', + metadata: { + jobId: 3, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: true, + }, + statusCode: 500, + }, + ], + statTags: retryStatTags, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/algolia/dataDelivery/constant.ts b/test/integrations/destinations/algolia/dataDelivery/constant.ts new file mode 100644 index 0000000000..e8d0817a7f --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/constant.ts @@ -0,0 +1,118 @@ +const proxyMetdata1 = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +const proxyMetdata2 = { + jobId: 2, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +export const metadataArray = [proxyMetdata1, proxyMetdata2]; + +export const abortStatTags = { + errorCategory: 'network', + errorType: 'aborted', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const commonRequestProperties = { + commonHeaders: { + 'X-Algolia-API-Key': 'dummyApiKey', + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'User-Agent': 'RudderLabs', + }, + singleValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + singleInValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + multipleValidEvent: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + combinedValidInvalidEvents: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, +}; + +export const retryStatTags = { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', +}; diff --git a/test/integrations/destinations/algolia/dataDelivery/data.ts b/test/integrations/destinations/algolia/dataDelivery/data.ts new file mode 100644 index 0000000000..feb4eb46c5 --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/data.ts @@ -0,0 +1,9 @@ +import { testScenariosForV0API, testScenariosForV1API } from './business'; +import { otherScenariosV0, otherScenariosV1 } from './other'; + +export const data = [ + ...testScenariosForV0API, + ...testScenariosForV1API, + ...otherScenariosV0, + ...otherScenariosV1, +]; diff --git a/test/integrations/destinations/algolia/dataDelivery/other.ts b/test/integrations/destinations/algolia/dataDelivery/other.ts new file mode 100644 index 0000000000..f5ccc70337 --- /dev/null +++ b/test/integrations/destinations/algolia/dataDelivery/other.ts @@ -0,0 +1,524 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV0Payload, generateProxyV1Payload } from '../../../testUtils'; + +export const otherScenariosV0 = [ + { + id: 'algolia_v0_other_scenario_1', + name: 'algolia', + description: + '[Proxy v0 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 503, + body: { + output: { + status: 503, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 503', + destinationResponse: { + response: { + error: { + message: 'Service Unavailable', + description: + 'The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later.', + }, + }, + status: 503, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_2', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: 'Internal Server Error', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_3', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 504, + body: { + output: { + status: 504, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 504', + destinationResponse: { + response: 'Gateway Timeout', + status: 504, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_4', + name: 'algolia', + description: '[Proxy v0 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: '', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, + { + id: 'algolia_v0_other_scenario_5', + name: 'algolia', + description: + '[Proxy v0 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Generic Response Handler] Request failed for destination algolia with status: 500', + destinationResponse: { + response: '', + status: 500, + }, + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + }, + }, + }, + }, + }, +]; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'algolia_v1_other_scenario_1', + name: 'algolia', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 503, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_2', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_3', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 504, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_4', + name: 'algolia', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, + { + id: 'algolia_v1_other_scenario_5', + name: 'algolia', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: true, + }, + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'ALGOLIA', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: 'ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/algolia/network.ts b/test/integrations/destinations/algolia/network.ts new file mode 100644 index 0000000000..84e932bbdb --- /dev/null +++ b/test/integrations/destinations/algolia/network.ts @@ -0,0 +1,117 @@ +export const networkCallsData = [ + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + data: { + events: [ + { + eventName: 'product clicked', + eventType: 'click', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'view', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + { + eventName: 'product clicked', + eventType: 'abc', + filters: ['field1:hello', 'val1:val2'], + index: 'products', + userToken: 'testuserId1', + }, + ], + }, + params: {}, + headers: { 'User-Agent': 'RudderLabs' }, + method: 'POST', + }, + httpRes: { + data: { + status: 422, + message: 'EventType must be one of "click", "conversion" or "view"', + }, + status: 422, + }, + }, + { + httpReq: { + url: 'https://insights.algolia.io/1/events', + method: 'POST', + headers: { + 'User-Agent': 'RudderLabs', + }, + }, + httpRes: { + data: { + status: 200, + message: 'OK', + }, + status: 200, + }, + }, +]; diff --git a/test/integrations/destinations/algolia/processor/data.ts b/test/integrations/destinations/algolia/processor/data.ts index 7c37c9642a..d239c8de70 100644 --- a/test/integrations/destinations/algolia/processor/data.ts +++ b/test/integrations/destinations/algolia/processor/data.ts @@ -1627,4 +1627,806 @@ export const data = [ }, }, }, + { + name: 'algolia', + description: + 'For conversion event including product array and subtype addToCart, object data is sent', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + eventSubtype: 'addToCart', + currency: 'USD', + products: [ + { + objectId: 'ecommerce-sample-data-919', + position: 7, + quantity: '2', + price: 10, + queryID: '123', + discount: '10', + }, + { + objectId: '9780439784542', + position: 8, + quantity: '3', + price: 30, + queryID: '123', + discount: '10', + }, + ], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + events: [ + { + index: 'products', + currency: 'USD', + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + objectIDs: ['ecommerce-sample-data-919', '9780439784542'], + userToken: 'testuserId1', + eventName: 'product list viewed', + eventSubtype: 'addToCart', + eventType: 'conversion', + objectData: [ + { + quantity: 2, + price: '10', + queryID: '123', + discount: '10', + }, + { + quantity: 3, + price: '30', + queryID: '123', + discount: '10', + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'algolia', + description: + 'For conversion event including product array and subtype purchase, object data is sent', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + currency: 'USD', + eventSubtype: 'purchase', + products: [ + { + objectId: 'ecommerce-sample-data-919', + position: 7, + quantity: '2', + price: 10, + queryID: '123', + discount: '10', + }, + { + objectId: '9780439784542', + position: 8, + quantity: '3', + price: 30, + queryID: '123', + discount: '10', + }, + ], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + events: [ + { + index: 'products', + currency: 'USD', + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + objectIDs: ['ecommerce-sample-data-919', '9780439784542'], + userToken: 'testuserId1', + eventName: 'product list viewed', + eventSubtype: 'purchase', + eventType: 'conversion', + objectData: [ + { + quantity: 2, + queryID: '123', + discount: '10', + price: '10', + }, + { + quantity: 3, + queryID: '123', + discount: '10', + price: '30', + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'algolia', + description: + 'For conversion event including product array and subtype wrong, object data is sent but subType is omitted', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + eventSubtype: 'random', + value: 10, + currency: 'USD', + products: [ + { + objectId: 'ecommerce-sample-data-919', + position: 7, + quantity: '2', + queryID: '123', + discount: '10', + price: 10, + }, + { + objectId: '9780439784542', + position: 8, + quantity: '3', + queryID: '123', + discount: '10', + price: 10, + }, + ], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + events: [ + { + index: 'products', + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + objectIDs: ['ecommerce-sample-data-919', '9780439784542'], + userToken: 'testuserId1', + eventName: 'product list viewed', + eventType: 'conversion', + value: 10, + currency: 'USD', + objectData: [ + { + price: '10', + quantity: 2, + queryID: '123', + discount: '10', + }, + { + price: '10', + quantity: 3, + queryID: '123', + discount: '10', + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'algolia', + description: + 'For conversion event without including product array and subtype purchase, object data is not sent but subType is sent', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + eventSubtype: 'purchase', + filters: ['field1:hello', 'val1:val2'], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + events: [ + { + index: 'products', + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + filters: ['field1:hello', 'val1:val2'], + userToken: 'testuserId1', + eventName: 'product list viewed', + eventType: 'conversion', + eventSubtype: 'purchase', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'algolia', + description: 'When price information is present in objectData, currency is mandatory', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + page: { + path: '/destinations/ometria', + referrer: '', + search: '', + title: '', + url: 'https://docs.rudderstack.com/destinations/ometria', + category: 'destination', + initial_referrer: 'https://docs.rudderstack.com', + initial_referring_domain: 'docs.rudderstack.com', + }, + }, + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + properties: { + index: 'products', + products: [ + { + objectId: 'ecommerce-sample-data-919', + position: 7, + quantity: '2', + price: 10, + }, + { + objectId: '9780439784542', + position: 8, + quantity: '3', + price: 30, + }, + ], + queryId: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'product list viewed', + to: 'conversion', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Currency missing when objectData fields has price informations.: Workflow: procWorkflow, Step: populateProductsData, ChildStep: populateForClickEvent, OriginalError: Currency missing when objectData fields has price informations.', + statTags: { + destType: 'ALGOLIA', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 400, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/algolia/router/data.ts b/test/integrations/destinations/algolia/router/data.ts index 65c74342dc..dca899693e 100644 --- a/test/integrations/destinations/algolia/router/data.ts +++ b/test/integrations/destinations/algolia/router/data.ts @@ -2220,4 +2220,2406 @@ export const data = [ }, }, }, + { + name: 'algolia', + description: 'dontBatch true for all', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, + { + name: 'algolia', + description: 'dontBatch Partial true for events in a batch', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: true, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, + { + name: 'algolia', + description: 'dontBatch false for all events, all events are batched in 1', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'track', + anonymousId: '345345', + event: 'product clicked', + userId: 'test', + properties: { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + }, + integrations: { + All: true, + }, + }, + metadata: { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + }, + ], + destType: 'algolia', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + JSON: { + events: [ + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + { + index: 'products', + filters: ['field1:hello', 'val1:val2'], + userToken: 'test', + eventName: 'product clicked', + eventType: 'click', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://insights.algolia.io/1/events', + headers: { + 'X-Algolia-Application-Id': 'O2YARRI15I', + 'X-Algolia-API-Key': 'dummyApiKey', + }, + params: {}, + files: {}, + }, + metadata: [ + { + jobId: 1, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 3, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 4, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + { + jobId: 5, + attemptNum: 0, + userId: '', + sourceId: '2bAiFXtSLvENPDRAxVRxf0Udfaz', + destinationId: '2dH3xYIQnTNqfgPCqfVs88gWQdQ', + workspaceId: '2Csl0lSTbuM3qyHdaOQB2GcDH8o', + secret: null, + dontBatch: false, + }, + ], + batched: true, + statusCode: 200, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + cdkV2Enabled: true, + apiKey: 'dummyApiKey', + applicationId: 'O2YARRI15I', + eventTypeSettings: [ + { + from: 'Product List Filtered', + to: 'click', + }, + { + from: 'Product List Viewed', + to: 'view', + }, + { + from: 'Order Completed', + to: 'view', + }, + { + from: 'Product Added', + to: 'conversion', + }, + { + from: 'Product Viewed', + to: 'view', + }, + { + from: 'Product Clicked', + to: 'click', + }, + ], + pixelId: '123456789', + advertiserId: '429047995', + eventId: '429047995', + enhancedMatch: true, + enableDeduplication: true, + deduplicationKey: 'messageId', + sendingUnHashedData: true, + customProperties: [ + { + properties: 'presentclass', + }, + { + properties: 'presentgrade', + }, + ], + eventsMapping: [ + { + from: 'ABC Searched', + to: 'WatchVideo', + }, + ], + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ALGOLIA', + Enabled: true, + cdkV2Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/am/dataDelivery/data.ts b/test/integrations/destinations/am/dataDelivery/data.ts index a4faa7e60c..19baca02c3 100644 --- a/test/integrations/destinations/am/dataDelivery/data.ts +++ b/test/integrations/destinations/am/dataDelivery/data.ts @@ -68,6 +68,15 @@ export const data = [ message: '[Generic Response Handler] Request for destination: am Processed Successfully', destinationResponse: { + headers: { + 'access-control-allow-methods': 'GET, POST', + 'access-control-allow-origin': '*', + connection: 'keep-alive', + 'content-length': '93', + 'content-type': 'application/json', + date: 'Sat, 11 Dec 2021 15:08:22 GMT', + 'strict-transport-security': 'max-age=15768000', + }, response: { code: 200, server_upload_time: 1639235302252, @@ -145,6 +154,15 @@ export const data = [ message: '[Generic Response Handler] Request failed for destination am with status: 400', destinationResponse: { + headers: { + 'access-control-allow-methods': 'GET, POST', + 'access-control-allow-origin': '*', + connection: 'keep-alive', + 'content-length': '93', + 'content-type': 'application/json', + date: 'Sat, 11 Dec 2021 15:08:22 GMT', + 'strict-transport-security': 'max-age=15768000', + }, response: { code: 400, server_upload_time: 1639235302252, diff --git a/test/integrations/destinations/am/processor/data.ts b/test/integrations/destinations/am/processor/data.ts index b645fb5ac7..01f9feb44a 100644 --- a/test/integrations/destinations/am/processor/data.ts +++ b/test/integrations/destinations/am/processor/data.ts @@ -10739,6 +10739,7 @@ export const data = [ integrations: { All: true, Amplitude: { + skipUserPropertiesSync: false, event_id: 2, }, }, @@ -10894,6 +10895,7 @@ export const data = [ integrations: { All: true, Amplitude: { + skipUserPropertiesSync: true, event_id: 2, }, }, @@ -10949,6 +10951,7 @@ export const data = [ insert_id: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', ip: '1.1.1.1', event_id: 2, + $skip_user_properties_sync: true, user_properties: { initial_referrer: 'https://docs.rudderstack.com', initial_referring_domain: 'docs.rudderstack.com', diff --git a/test/integrations/destinations/awin/data.ts b/test/integrations/destinations/awin/data.ts index 64c8fbc2a1..821b55637e 100644 --- a/test/integrations/destinations/awin/data.ts +++ b/test/integrations/destinations/awin/data.ts @@ -828,16 +828,646 @@ export const data = [ status: 200, body: [ { - error: "Event is not present in 'Events to Track' list. Aborting message.", + error: "Event is not present in 'Events to Track' list. Dropping the event.", statTags: { destType: 'AWIN', - errorCategory: 'dataValidation', - errorType: 'instrumentation', feature: 'processor', implementation: 'native', module: 'destination', }, - statusCode: 400, + statusCode: 298, + }, + ], + }, + }, + }, + { + name: 'awin', + description: 'Track call- with product array', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + advertiserId: '1234', + eventsToTrack: [ + { + eventName: 'abc', + }, + { + eventName: 'prop2', + }, + { + eventName: 'prop3', + }, + ], + }, + }, + message: { + type: 'track', + event: 'prop2', + sentAt: '2022-01-20T13:39:21.033Z', + userId: 'user123456001', + channel: 'web', + properties: { + currency: 'INR', + voucherCode: '1bcu1', + amount: 500, + commissionGroup: 'sales', + cks: 'new', + testMode: '1', + order_id: 'QW123', + products: [ + { + product_id: '123', + name: 'Product 1', + price: 10, + quantity: 1, + sku: undefined, + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: undefined, + }, + ], + }, + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.2.20', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + path: '/Testing/App_for_LaunchDarkly/ourSdk.html', + title: 'Document', + search: '', + tab_url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + referrer: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/', + initial_referrer: '$direct', + referring_domain: '127.0.0.1:7307', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 1440, + height: 900, + density: 2, + innerWidth: 536, + innerHeight: 689, + }, + traits: { + city: 'Pune', + name: 'First User', + email: 'firstUser@testmail.com', + title: 'VP', + gender: 'female', + avatar: 'https://i.pravatar.cc/300', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.2.20', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', + }, + rudderId: '553b5522-c575-40a7-8072-9741c5f9a647', + messageId: '831f1fa5-de84-4f22-880a-4c3f23fc3f04', + anonymousId: 'bf412108-0357-4330-b119-7305e767823c', + integrations: { + All: true, + }, + originalTimestamp: '2022-01-20T13:39:21.032Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.awin1.com/sread.php', + headers: {}, + params: { + amount: 500, + ch: 'aw', + parts: 'sales:500', + cr: 'INR', + tt: 'ss', + tv: '2', + vc: '1bcu1', + cks: 'new', + merchant: '1234', + testmode: '1', + ref: 'QW123', + 'bd[0]': 'AW:P|1234|QW123|123|Product%201|10|1||sales%3A500|Category%201', + 'bd[1]': 'AW:P|1234|QW123|456|Product%202|20|2|SKU456|sales%3A500|', + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'awin', + description: 'Track call- with product array where important keys might be missing.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + advertiserId: '1234', + eventsToTrack: [ + { + eventName: 'abc', + }, + { + eventName: 'prop2', + }, + { + eventName: 'prop3', + }, + ], + }, + }, + message: { + type: 'track', + event: 'prop2', + sentAt: '2022-01-20T13:39:21.033Z', + userId: 'user123456001', + channel: 'web', + properties: { + currency: 'INR', + voucherCode: '1bcu1', + amount: 500, + commissionGroup: 'sales', + cks: 'new', + testMode: '1', + order_id: 'QW123', + products: [ + { + price: 10, + quantity: 1, + sku: undefined, + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: undefined, + }, + ], + }, + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.2.20', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + path: '/Testing/App_for_LaunchDarkly/ourSdk.html', + title: 'Document', + search: '', + tab_url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + referrer: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/', + initial_referrer: '$direct', + referring_domain: '127.0.0.1:7307', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 1440, + height: 900, + density: 2, + innerWidth: 536, + innerHeight: 689, + }, + traits: { + city: 'Pune', + name: 'First User', + email: 'firstUser@testmail.com', + title: 'VP', + gender: 'female', + avatar: 'https://i.pravatar.cc/300', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.2.20', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', + }, + rudderId: '553b5522-c575-40a7-8072-9741c5f9a647', + messageId: '831f1fa5-de84-4f22-880a-4c3f23fc3f04', + anonymousId: 'bf412108-0357-4330-b119-7305e767823c', + integrations: { + All: true, + }, + originalTimestamp: '2022-01-20T13:39:21.032Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.awin1.com/sread.php', + headers: {}, + params: { + amount: 500, + ch: 'aw', + parts: 'sales:500', + cr: 'INR', + tt: 'ss', + tv: '2', + vc: '1bcu1', + cks: 'new', + merchant: '1234', + testmode: '1', + ref: 'QW123', + 'bd[0]': 'AW:P|1234|QW123|456|Product%202|20|2|SKU456|sales%3A500|', + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'awin', + description: 'Track call: with custom transaction property', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + advertiserId: '1234', + eventsToTrack: [ + { + eventName: 'abc', + }, + { + eventName: 'prop2', + }, + { + eventName: 'prop3', + }, + ], + customFieldMap: [ + { + from: 'customTransactionValue1', + to: 'p1', + }, + { + from: 'customTransactionValue2', + to: 'p2', + }, + ], + }, + }, + message: { + type: 'track', + event: 'abc', + sentAt: '2022-01-20T13:39:21.033Z', + userId: 'user123456001', + channel: 'web', + properties: { + currency: 'INR', + voucherCode: '1bcu1', + amount: 125, + customTransactionValue1: 'val1', + customTransactionValue2: 'val2', + }, + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.2.20', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + path: '/Testing/App_for_LaunchDarkly/ourSdk.html', + title: 'Document', + search: '', + tab_url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + referrer: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/', + initial_referrer: '$direct', + referring_domain: '127.0.0.1:7307', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 1440, + height: 900, + density: 2, + innerWidth: 536, + innerHeight: 689, + }, + traits: { + city: 'Pune', + name: 'First User', + email: 'firstUser@testmail.com', + title: 'VP', + gender: 'female', + avatar: 'https://i.pravatar.cc/300', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.2.20', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', + }, + rudderId: '553b5522-c575-40a7-8072-9741c5f9a647', + messageId: '831f1fa5-de84-4f22-880a-4c3f23fc3f04', + anonymousId: 'bf412108-0357-4330-b119-7305e767823c', + integrations: { + All: true, + }, + originalTimestamp: '2022-01-20T13:39:21.032Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.awin1.com/sread.php', + headers: {}, + params: { + amount: 125, + ch: 'aw', + cr: 'INR', + tt: 'ss', + tv: '2', + vc: '1bcu1', + merchant: '1234', + parts: 'DEFAULT:125', + testmode: '0', + p1: 'val1', + p2: 'val2', + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'awin', + description: 'Track call- with product array, along with transactional custom property', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + advertiserId: '1234', + eventsToTrack: [ + { + eventName: 'abc', + }, + { + eventName: 'prop2', + }, + { + eventName: 'prop3', + }, + ], + customFieldMap: [ + { + from: 'customTransactionValue1', + to: 'p1', + }, + { + from: 'customTransactionValue2', + to: 'p2', + }, + ], + }, + }, + message: { + type: 'track', + event: 'prop2', + sentAt: '2022-01-20T13:39:21.033Z', + userId: 'user123456001', + channel: 'web', + properties: { + currency: 'INR', + voucherCode: '1bcu1', + amount: 500, + commissionGroup: 'sales', + cks: 'new', + testMode: '1', + order_id: 'QW123', + customTransactionValue1: 'val1', + customTransactionValue2: 'val2', + products: [ + { + price: 10, + quantity: 1, + sku: undefined, + category: 'Category 1', + }, + { + product_id: '456', + name: 'Product 2', + price: 20, + quantity: 2, + sku: 'SKU456', + category: undefined, + }, + ], + }, + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.2.20', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + path: '/Testing/App_for_LaunchDarkly/ourSdk.html', + title: 'Document', + search: '', + tab_url: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/ourSdk.html', + referrer: 'http://127.0.0.1:7307/Testing/App_for_LaunchDarkly/', + initial_referrer: '$direct', + referring_domain: '127.0.0.1:7307', + initial_referring_domain: '', + }, + locale: 'en-US', + screen: { + width: 1440, + height: 900, + density: 2, + innerWidth: 536, + innerHeight: 689, + }, + traits: { + city: 'Pune', + name: 'First User', + email: 'firstUser@testmail.com', + title: 'VP', + gender: 'female', + avatar: 'https://i.pravatar.cc/300', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.2.20', + }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', + }, + rudderId: '553b5522-c575-40a7-8072-9741c5f9a647', + messageId: '831f1fa5-de84-4f22-880a-4c3f23fc3f04', + anonymousId: 'bf412108-0357-4330-b119-7305e767823c', + integrations: { + All: true, + }, + originalTimestamp: '2022-01-20T13:39:21.032Z', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.awin1.com/sread.php', + headers: {}, + params: { + amount: 500, + ch: 'aw', + parts: 'sales:500', + cr: 'INR', + tt: 'ss', + tv: '2', + vc: '1bcu1', + cks: 'new', + merchant: '1234', + testmode: '1', + ref: 'QW123', + 'bd[0]': 'AW:P|1234|QW123|456|Product%202|20|2|SKU456|sales%3A500|', + p1: 'val1', + p2: 'val2', + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, }, ], }, diff --git a/test/integrations/destinations/bloomreach/common.ts b/test/integrations/destinations/bloomreach/common.ts new file mode 100644 index 0000000000..798e744cbc --- /dev/null +++ b/test/integrations/destinations/bloomreach/common.ts @@ -0,0 +1,99 @@ +import { Destination } from '../../../../src/types'; + +const destType = 'bloomreach'; +const destTypeInUpperCase = 'BLOOMREACH'; +const displayName = 'bloomreach'; +const channel = 'web'; +const destination: Destination = { + Config: { + apiBaseUrl: 'https://demoapp-api.bloomreach.com', + apiKey: 'test-api-key', + apiSecret: 'test-api-secret', + projectToken: 'test-project-token', + hardID: 'registered', + softID: 'cookie', + }, + DestinationDefinition: { + DisplayName: displayName, + ID: '123', + Name: destTypeInUpperCase, + Config: { cdkV2Enabled: true }, + }, + Enabled: true, + ID: '123', + Name: destTypeInUpperCase, + Transformations: [], + WorkspaceID: 'test-workspace-id', +}; + +const traits = { + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + phone: '1234567890', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, +}; + +const properties = { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + category: 'Games', + name: 'Cones of Dunshire', + brand: 'Wyatt Games', + variant: 'expansion pack', + price: 49.99, + quantity: 5, + coupon: 'PREORDER15', + currency: 'USD', + position: 1, + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.webp', + key1: 'value1', +}; +const endpoint = 'https://demoapp-api.bloomreach.com/track/v2/projects/test-project-token/batch'; + +const processorInstrumentationErrorStatTags = { + destType: destTypeInUpperCase, + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +const RouterInstrumentationErrorStatTags = { + ...processorInstrumentationErrorStatTags, + feature: 'router', +}; + +const proxyV1RetryableErrorStatTags = { + ...RouterInstrumentationErrorStatTags, + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', +}; + +const headers = { + 'Content-Type': 'application/json', + Authorization: 'Basic dGVzdC1hcGkta2V5OnRlc3QtYXBpLXNlY3JldA==', +}; + +export { + destType, + channel, + destination, + processorInstrumentationErrorStatTags, + RouterInstrumentationErrorStatTags, + traits, + headers, + properties, + endpoint, + proxyV1RetryableErrorStatTags, +}; diff --git a/test/integrations/destinations/bloomreach/dataDelivery/business.ts b/test/integrations/destinations/bloomreach/dataDelivery/business.ts new file mode 100644 index 0000000000..9e71b7a2fd --- /dev/null +++ b/test/integrations/destinations/bloomreach/dataDelivery/business.ts @@ -0,0 +1,195 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { destType, headers, properties, endpoint } from '../common'; + +const customerProperties = { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, +}; + +const metadataArray = [generateMetadata(1), generateMetadata(2)]; + +// https://documentation.bloomreach.com/engagement/reference/tips-and-best-practices +export const businessProxyV1: ProxyV1TestData[] = [ + { + id: 'bloomreach_v1_business_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Test for a valid request - where the destination responds with 200 with error for request 2 in a batch', + successCriteria: 'Should return 200 with partial failures within the response payload', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params: {}, + JSON: { + commands: [ + { + name: 'customers', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + update_timestamp: 1709405952, + properties: customerProperties, + }, + }, + { + name: 'customers', + data: { + customer_ids: {}, + }, + }, + ], + }, + endpoint, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[BLOOMREACH Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + results: [ + { + success: true, + }, + { + success: false, + errors: ['At least one id should be specified.'], + }, + ], + start_time: 1710771351.9885373, + end_time: 1710771351.9891083, + success: true, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: 'At least one id should be specified.', + }, + ], + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_business_scenario_2', + name: destType, + description: + '[Proxy v1 API] :: Test for a valid request - where the destination responds with 200 without any error', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params: {}, + JSON: { + commands: [ + { + name: 'customers/events', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + timestamp: 1709566376, + properties, + event_type: 'test_event', + }, + }, + { + name: 'customers', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + update_timestamp: 1709405952, + properties: customerProperties, + }, + }, + ], + }, + endpoint, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[BLOOMREACH Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + results: [ + { + success: true, + }, + { + success: true, + }, + ], + start_time: 1710771351.9885373, + end_time: 1710771351.9891083, + success: true, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 200, + metadata: generateMetadata(2), + error: 'success', + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/dataDelivery/data.ts b/test/integrations/destinations/bloomreach/dataDelivery/data.ts new file mode 100644 index 0000000000..5099eafce7 --- /dev/null +++ b/test/integrations/destinations/bloomreach/dataDelivery/data.ts @@ -0,0 +1,3 @@ +import { businessProxyV1 } from './business'; +import { otherProxyV1 } from './other'; +export const data = [...businessProxyV1, ...otherProxyV1]; diff --git a/test/integrations/destinations/bloomreach/dataDelivery/other.ts b/test/integrations/destinations/bloomreach/dataDelivery/other.ts new file mode 100644 index 0000000000..f0dd9cc09a --- /dev/null +++ b/test/integrations/destinations/bloomreach/dataDelivery/other.ts @@ -0,0 +1,212 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { destType, proxyV1RetryableErrorStatTags } from '../common'; + +const metadataArray = [generateMetadata(1)]; + +// https://documentation.bloomreach.com/engagement/reference/tips-and-best-practices +export const otherProxyV1: ProxyV1TestData[] = [ + { + id: 'bloomreach_v1_other_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_service_not_available', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 503, + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_other_scenario_2', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_internal_server_error', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 500, + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_other_scenario_3', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 504, + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_other_scenario_4', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_response', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 500, + }, + }, + }, + }, + }, + { + id: 'bloomreach_v1_other_scenario_5', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'BLOOMREACH: Error encountered in transformer proxy V1', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/mocks.ts b/test/integrations/destinations/bloomreach/mocks.ts new file mode 100644 index 0000000000..ba3b22b52a --- /dev/null +++ b/test/integrations/destinations/bloomreach/mocks.ts @@ -0,0 +1,5 @@ +import * as config from '../../../../src/cdk/v2/destinations/bloomreach/config'; + +export const defaultMockFns = () => { + jest.replaceProperty(config, 'MAX_BATCH_SIZE', 3 as typeof config.MAX_BATCH_SIZE); +}; diff --git a/test/integrations/destinations/bloomreach/network.ts b/test/integrations/destinations/bloomreach/network.ts new file mode 100644 index 0000000000..b20ff881b8 --- /dev/null +++ b/test/integrations/destinations/bloomreach/network.ts @@ -0,0 +1,124 @@ +import { destType, headers, properties, endpoint } from './common'; + +export const networkCallsData = [ + { + httpReq: { + url: endpoint, + data: { + commands: [ + { + name: 'customers', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + update_timestamp: 1709405952, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + }, + }, + { + name: 'customers', + data: { + customer_ids: {}, + }, + }, + ], + }, + params: { destination: destType }, + headers, + method: 'POST', + }, + httpRes: { + data: { + results: [ + { + success: true, + }, + { + success: false, + errors: ['At least one id should be specified.'], + }, + ], + start_time: 1710771351.9885373, + end_time: 1710771351.9891083, + success: true, + }, + status: 200, + statusText: 'Ok', + }, + }, + { + httpReq: { + url: endpoint, + data: { + commands: [ + { + name: 'customers/events', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + timestamp: 1709566376, + properties, + event_type: 'test_event', + }, + }, + { + name: 'customers', + data: { + customer_ids: { + cookie: '97c46c81-3140-456d-b2a9-690d70aaca35', + }, + update_timestamp: 1709405952, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + }, + }, + ], + }, + params: { destination: destType }, + headers, + method: 'POST', + }, + httpRes: { + data: { + results: [ + { + success: true, + }, + { + success: true, + }, + ], + start_time: 1710771351.9885373, + end_time: 1710771351.9891083, + success: true, + }, + status: 200, + statusText: 'Ok', + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/processor/data.ts b/test/integrations/destinations/bloomreach/processor/data.ts new file mode 100644 index 0000000000..a3633ad0dd --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/data.ts @@ -0,0 +1,5 @@ +import { validation } from './validation'; +import { identify } from './identify'; +import { track } from './track'; +import { page } from './page'; +export const data = [...identify, ...track, ...page, ...validation]; diff --git a/test/integrations/destinations/bloomreach/processor/identify.ts b/test/integrations/destinations/bloomreach/processor/identify.ts new file mode 100644 index 0000000000..2a79cb57e3 --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/identify.ts @@ -0,0 +1,156 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, destination, traits, headers, endpoint } from '../common'; + +export const identify: ProcessorTestData[] = [ + { + id: 'bloomreach-identify-test-1', + name: destType, + description: 'Identify call to create/update customer properties', + scenario: 'Framework+Business', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: 'userId123', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { registered: 'userId123', cookie: 'anonId123' }, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + update_timestamp: 1709566376, + }, + name: 'customers', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'bloomreach-identify-test-2', + name: destType, + description: 'Identify call with multiple hard and soft identifiers using integration object', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain multiple hard and soft identifiers and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: 'userId123', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + bloomreach: { + hardID: { + hardID1: 'value1', + }, + softID: { + google_analytics: 'gaId123', + softID2: 'value2', + }, + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { + registered: 'userId123', + cookie: 'anonId123', + hardID1: 'value1', + google_analytics: 'gaId123', + softID2: 'value2', + }, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + update_timestamp: 1709566376, + }, + name: 'customers', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/processor/page.ts b/test/integrations/destinations/bloomreach/processor/page.ts new file mode 100644 index 0000000000..3081feeb26 --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/page.ts @@ -0,0 +1,129 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, destination, headers, endpoint } from '../common'; + +const properties = { + category: 'Docs', + path: '', + referrer: '', + search: '', + title: '', + url: '', +}; + +export const page: ProcessorTestData[] = [ + { + id: 'bloomreach-page-test-1', + name: destType, + description: 'Page call with category from properties and root-level name', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain event_name = "Viewed {{ category }} {{ name }} Page" and properties and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'page', + anonymousId: 'anonId123', + name: 'Integration', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { cookie: 'anonId123' }, + properties, + timestamp: 1709566376, + event_type: 'Viewed Docs Integration Page', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'bloomreach-page-test-2', + name: destType, + description: 'Page call with category, name from properties', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain event_name = "Viewed {{ category }} {{ name }} Page" and properties and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'page', + anonymousId: 'anonId123', + name: '', + properties: { ...properties, name: 'Integration' }, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { cookie: 'anonId123' }, + properties: { ...properties, name: 'Integration' }, + timestamp: 1709566376, + event_type: 'Viewed Docs Integration Page', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/processor/track.ts b/test/integrations/destinations/bloomreach/processor/track.ts new file mode 100644 index 0000000000..a369f508b2 --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/track.ts @@ -0,0 +1,173 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, destination, headers, properties, endpoint } from '../common'; + +export const track: ProcessorTestData[] = [ + { + id: 'bloomreach-track-test-1', + name: destType, + description: 'Track call with anonymous user', + scenario: 'Framework+Business', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + anonymousId: 'anonId123', + event: 'Product Viewed', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { cookie: 'anonId123' }, + properties, + timestamp: 1709566376, + event_type: 'Product Viewed', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'bloomreach-track-test-2', + name: destType, + description: 'Track call with known user', + scenario: 'Framework+Business', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Product Added', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { registered: 'userId123', cookie: 'anonId123' }, + properties, + timestamp: 1709566376, + event_type: 'Product Added', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'bloomreach-track-test-3', + name: destType, + description: 'Track call with no properties', + scenario: 'Framework+Business', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + anonymousId: 'anonId123', + event: 'test_event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { cookie: 'anonId123' }, + timestamp: 1709566376, + event_type: 'test_event', + }, + name: 'customers/events', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/processor/validation.ts b/test/integrations/destinations/bloomreach/processor/validation.ts new file mode 100644 index 0000000000..1a6199abb0 --- /dev/null +++ b/test/integrations/destinations/bloomreach/processor/validation.ts @@ -0,0 +1,205 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { + destType, + destination, + processorInstrumentationErrorStatTags, + traits, + headers, + endpoint, +} from '../common'; + +export const validation: ProcessorTestData[] = [ + { + id: 'bloomreach-validation-test-1', + name: destType, + description: 'Missing userId and anonymousId', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either one of userId or anonymousId is required. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Either one of userId or anonymousId is required. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'bloomreach-validation-test-2', + name: destType, + description: 'Unsupported message type -> group', + scenario: 'Framework', + successCriteria: 'Instrumentation Error for Unsupported message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'group', + userId: 'userId123', + channel: 'mobile', + anonymousId: 'anon_123', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type group is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type group is not supported', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'bloomreach-validation-test-3', + name: destType, + description: 'Missing required field -> timestamp', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Timestamp is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Timestamp is not present. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'bloomreach-validation-test-4', + name: destType, + description: 'Empty userId and non empty anonymousId', + scenario: 'Framework', + successCriteria: 'Response should contain all the mapping and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: '', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + JSON: { + data: { + customer_ids: { registered: '', cookie: 'anonId123' }, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + update_timestamp: 1709566376, + }, + name: 'customers', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/bloomreach/router/data.ts b/test/integrations/destinations/bloomreach/router/data.ts new file mode 100644 index 0000000000..e99d0cc8cd --- /dev/null +++ b/test/integrations/destinations/bloomreach/router/data.ts @@ -0,0 +1,220 @@ +import { generateMetadata } from '../../../testUtils'; +import { defaultMockFns } from '../mocks'; +import { + destType, + destination, + traits, + properties, + headers, + endpoint, + RouterInstrumentationErrorStatTags, +} from '../common'; + +const routerRequest = { + input: [ + { + message: { + type: 'track', + anonymousId: 'anonId1', + event: 'test_event_1A', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + destination, + }, + { + message: { + type: 'identify', + anonymousId: 'anonId1', + userId: 'userId1', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + destination, + }, + { + message: { + type: 'track', + anonymousId: 'anonId2', + event: 'test_event_2A', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(3), + destination, + }, + { + message: { + type: 'track', + anonymousId: 'anonId1', + userId: 'userId1', + event: 'test_event_1B', + properties, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(4), + destination, + }, + { + message: { + type: 'identify', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(5), + destination, + }, + ], + destType, +}; +export const data = [ + { + id: 'bloomreach-router-test-1', + name: destType, + description: 'Basic Router Test to test multiple payloads', + scenario: 'Framework', + successCriteria: 'All events should be transformed successfully and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + JSON: { + commands: [ + { + data: { + customer_ids: { cookie: 'anonId1' }, + properties, + timestamp: 1709566376, + event_type: 'test_event_1A', + }, + name: 'customers/events', + }, + { + data: { + customer_ids: { + registered: 'userId1', + cookie: 'anonId1', + }, + properties: { + email: 'test@example.com', + first_name: 'John', + last_name: 'Doe', + phone: '1234567890', + city: 'New York', + country: 'USA', + address: { + city: 'New York', + country: 'USA', + pinCode: '123456', + }, + }, + update_timestamp: 1709566376, + }, + name: 'customers', + }, + { + data: { + customer_ids: { cookie: 'anonId2' }, + properties, + timestamp: 1709566376, + event_type: 'test_event_2A', + }, + name: 'customers/events', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(1), generateMetadata(2), generateMetadata(3)], + batched: true, + statusCode: 200, + destination, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + JSON: { + commands: [ + { + data: { + customer_ids: { registered: 'userId1', cookie: 'anonId1' }, + properties, + timestamp: 1709566376, + event_type: 'test_event_1B', + }, + name: 'customers/events', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(4)], + batched: true, + statusCode: 200, + destination, + }, + { + metadata: [generateMetadata(5)], + batched: false, + statusCode: 400, + error: 'Either one of userId or anonymousId is required. Aborting', + statTags: RouterInstrumentationErrorStatTags, + destination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/bluecore/ecommTestData.ts b/test/integrations/destinations/bluecore/ecommTestData.ts index de7584df78..19b63e7bda 100644 --- a/test/integrations/destinations/bluecore/ecommTestData.ts +++ b/test/integrations/destinations/bluecore/ecommTestData.ts @@ -73,7 +73,7 @@ const commonOutputHeaders = { 'Content-Type': 'application/json', }; -const eventEndPoint = 'https://api.bluecore.com/api/track/mobile/v1'; +const eventEndPoint = 'https://api.bluecore.app/api/track/mobile/v1'; export const ecomTestData = [ { @@ -296,7 +296,11 @@ export const ecomTestData = [ customer: { age: '22', email: 'test@rudderstack.com', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', + phone: '9112340375', }, + product_id: '123', products: [ { id: '123', @@ -304,9 +308,11 @@ export const ecomTestData = [ property2: 'value2', }, ], + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', }, event: 'viewed_product', - token: 'dummy_sandbox', }, userId: '', }), @@ -379,8 +385,11 @@ export const ecomTestData = [ JSON: { properties: { distinct_id: 'user@1', + product_id: '123', customer: { age: '22', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', }, products: [ { @@ -389,9 +398,11 @@ export const ecomTestData = [ property2: 'value2', }, ], + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', }, event: 'wishlist', - token: 'dummy_sandbox', }, userId: '', }), @@ -406,8 +417,11 @@ export const ecomTestData = [ JSON: { properties: { distinct_id: 'user@1', + product_id: '123', customer: { age: '22', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', }, products: [ { @@ -416,9 +430,11 @@ export const ecomTestData = [ property2: 'value2', }, ], + token: 'dummy_sandbox', + property1: 'value1', + property2: 'value2', }, event: 'add_to_cart', - token: 'dummy_sandbox', }, userId: '', }), diff --git a/test/integrations/destinations/bluecore/identifyTestData.ts b/test/integrations/destinations/bluecore/identifyTestData.ts index 660e335bc6..fee27ccf0f 100644 --- a/test/integrations/destinations/bluecore/identifyTestData.ts +++ b/test/integrations/destinations/bluecore/identifyTestData.ts @@ -55,6 +55,10 @@ const commonOutputCustomerProperties = { first_name: 'Test', last_name: 'Rudderlabs', sex: 'non-binary', + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + db: '19950715', + gender: 'non-binary', + phone: '+1234589947', address: { city: 'Kolkata', state: 'WB', @@ -71,7 +75,7 @@ const anonymousId = '97c46c81-3140-456d-b2a9-690d70aaca35'; const userId = 'user@1'; const sentAt = '2021-01-03T17:02:53.195Z'; const originalTimestamp = '2021-01-03T17:02:53.193Z'; -const commonEndpoint = 'https://api.bluecore.com/api/track/mobile/v1'; +const commonEndpoint = 'https://api.bluecore.app/api/track/mobile/v1'; export const identifyData = [ { @@ -118,8 +122,8 @@ export const identifyData = [ properties: { distinct_id: 'abc@gmail.com', customer: { ...commonOutputCustomerProperties, email: 'abc@gmail.com' }, + token: 'dummy_sandbox', }, - token: 'dummy_sandbox', event: 'customer_patch', }, }), @@ -302,8 +306,9 @@ export const identifyData = [ properties: { distinct_id: 'user@1', customer: { ...commonOutputCustomerProperties, email: 'abc@gmail.com' }, + token: 'dummy_sandbox', }, - token: 'dummy_sandbox', + event: 'identify', }, }), @@ -361,8 +366,8 @@ export const identifyData = [ properties: { distinct_id: '54321', customer: { ...commonOutputCustomerProperties, email: 'abc@gmail.com' }, + token: 'dummy_sandbox', }, - token: 'dummy_sandbox', event: 'customer_patch', }, }), diff --git a/test/integrations/destinations/bluecore/trackTestData.ts b/test/integrations/destinations/bluecore/trackTestData.ts index 72d48bf93d..7474127558 100644 --- a/test/integrations/destinations/bluecore/trackTestData.ts +++ b/test/integrations/destinations/bluecore/trackTestData.ts @@ -86,7 +86,7 @@ const commonOutputHeaders = { 'Content-Type': 'application/json', }; -const eventEndPoint = 'https://api.bluecore.com/api/track/mobile/v1'; +const eventEndPoint = 'https://api.bluecore.app/api/track/mobile/v1'; export const trackTestData = [ { @@ -140,6 +140,9 @@ export const trackTestData = [ customer: { age: '22', email: 'test@rudderstack.com', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', + phone: '9112340375', }, products: [ { @@ -155,9 +158,11 @@ export const trackTestData = [ quantity: 3, }, ], + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', }, event: 'TestEven001', - token: 'dummy_sandbox', }, userId: '', }), @@ -216,13 +221,19 @@ export const trackTestData = [ JSON: { properties: { distinct_id: 'test@rudderstack.com', + product_id: '123', + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', customer: { age: '22', email: 'test@rudderstack.com', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', + phone: '9112340375', }, }, event: 'TestEven001', - token: 'dummy_sandbox', }, userId: '', }), @@ -283,11 +294,17 @@ export const trackTestData = [ distinct_id: 'test@rudderstack.com', customer: { age: '22', + anonymousId: '9c6bd77ea9da3e68', email: 'test@rudderstack.com', + id: 'user@1', + phone: '9112340375', }, + product_id: '123', + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', }, event: 'optin', - token: 'dummy_sandbox', }, userId: '', }), @@ -346,13 +363,19 @@ export const trackTestData = [ JSON: { properties: { distinct_id: 'test@rudderstack.com', + product_id: '123', + property1: 'value1', + property2: 'value2', + token: 'dummy_sandbox', customer: { age: '22', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', email: 'test@rudderstack.com', + phone: '9112340375', }, }, event: 'unsubscribe', - token: 'dummy_sandbox', }, userId: '', }), @@ -405,9 +428,12 @@ export const trackTestData = [ JSON: { properties: { distinct_id: '54321', + token: 'dummy_sandbox', customer: { age: '22', email: 'abc@gmail.com', + anonymousId: '9c6bd77ea9da3e68', + id: 'user@1', }, products: [ { @@ -423,9 +449,10 @@ export const trackTestData = [ quantity: 3, }, ], + property1: 'value1', + property2: 'value2', }, event: 'TestEven001', - token: 'dummy_sandbox', }, userId: '', }), diff --git a/test/integrations/destinations/blueshift/processor/data.ts b/test/integrations/destinations/blueshift/processor/data.ts index d489a38bd6..be23521621 100644 --- a/test/integrations/destinations/blueshift/processor/data.ts +++ b/test/integrations/destinations/blueshift/processor/data.ts @@ -739,7 +739,7 @@ export const data = [ body: [ { statusCode: 400, - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', diff --git a/test/integrations/destinations/braze/processor/data.ts b/test/integrations/destinations/braze/processor/data.ts index 58c54bb232..644bebace1 100644 --- a/test/integrations/destinations/braze/processor/data.ts +++ b/test/integrations/destinations/braze/processor/data.ts @@ -3863,4 +3863,466 @@ export const data = [ }, }, }, + { + name: 'braze', + description: '[Test 28] Test for alias_id overriding feature for track event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + restApiKey: 'dummyApiKey', + prefixProperties: true, + useNativeSDK: false, + }, + DestinationDefinition: { + DisplayName: 'Braze', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'BRAZE', + }, + Enabled: true, + ID: '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Braze', + Transformations: [], + }, + message: { + anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + channel: 'web', + context: { + traits: { + city: 'Disney', + country: 'USA', + email: 'micky@example.com', + firstname: 'Mickey', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + }, + event: 'Test Event', + integrations: { + All: true, + Braze: { + alias: { + alias_label: 'email', + alias_name: 'micky@example.com', + }, + }, + }, + messageId: 'aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a', + originalTimestamp: '2020-01-24T06:29:02.367Z', + properties: { + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + coupon: 'hasbros', + currency: 'USD', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + revenue: 25, + shipping: 3, + subtotal: 22.5, + tax: 2, + total: 27.5, + }, + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53712', + sentAt: '2020-01-24T06:29:02.368Z', + timestamp: '2020-01-24T11:59:02.402+05:30', + type: 'track', + userId: '', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://rest.fra-01.braze.eu/users/track', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: 'Bearer dummyApiKey', + }, + params: {}, + body: { + JSON: { + partner: 'RudderStack', + attributes: [ + { + email: 'micky@example.com', + city: 'Disney', + country: 'USA', + firstname: 'Mickey', + _update_existing_only: false, + user_alias: { + alias_name: 'micky@example.com', + alias_label: 'email', + }, + }, + ], + events: [ + { + name: 'Test Event', + time: '2020-01-24T11:59:02.402+05:30', + properties: { + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + coupon: 'hasbros', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + revenue: 25, + shipping: 3, + subtotal: 22.5, + tax: 2, + total: 27.5, + }, + _update_existing_only: false, + user_alias: { + alias_name: 'micky@example.com', + alias_label: 'email', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'braze', + description: + '[Test 29] Test for alias_id overriding feature for identify event (with intermediate alias call)', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + restApiKey: 'dummyApiKey', + prefixProperties: true, + useNativeSDK: false, + dataCenter: 'us-01', + }, + DestinationDefinition: { + DisplayName: 'Braze', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'BRAZE', + }, + Enabled: true, + ID: '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Braze', + Transformations: [], + }, + message: { + anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + channel: 'web', + context: { + traits: { + city: 'Disney', + country: 'USA', + email: 'micky@example.com', + firstname: 'Mickey', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + }, + integrations: { + All: true, + Braze: { + alias: { + alias_label: 'email', + alias_name: 'micky@example.com', + }, + }, + }, + messageId: 'aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a', + originalTimestamp: '2020-01-24T06:29:02.367Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53712', + sentAt: '2020-01-24T06:29:02.368Z', + timestamp: '2020-01-24T11:59:02.402+05:30', + type: 'identify', + userId: 'mickeyUser', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://rest.iad-01.braze.com/users/track', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: 'Bearer dummyApiKey', + }, + params: {}, + body: { + JSON: { + attributes: [ + { + email: 'micky@example.com', + city: 'Disney', + country: 'USA', + firstname: 'Mickey', + external_id: 'mickeyUser', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: 'mickeyUser', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'braze', + description: 'Test 30', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-09-14T12:09:37.491Z', + userId: 'Randomuser2222', + channel: 'web', + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.3', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'file:///Users/manashi/Desktop/rudder-all-sdk-application-testing/Fullstory%20test%20By%20JS%20SDK/braze.html', + path: '/Users/manashi/Desktop/rudder-all-sdk-application-testing/Fullstory%20test%20By%20JS%20SDK/braze.html', + title: 'Fullstory Test', + search: '', + referrer: '', + }, + locale: 'en-GB', + screen: { + density: 2, + }, + traits: { + email: 'manashi@gmaiol.com', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.3', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36', + }, + messageId: '24ecc509-ce3e-473c-8483-ba1ea2c195cb', + properties: { + products: [ + null, + { + sku: '45790-32', + url: 'https://www.example.com/product/path', + key1: { + key11: 'value1', + key22: 'value2', + }, + name: 'Monopoly: 3rd Edition', + price: 19, + category: 'Games', + quantity: 1, + image_url: 'https:///www.example.com/product/path.jpg', + currency78: 'USD', + product_id: '507f1f77bcf86cd799439011', + }, + ], + }, + anonymousId: 'c6ff1462-b692-43d6-8f6a-659efedc99ea', + integrations: { + All: true, + }, + originalTimestamp: '2020-09-14T12:09:37.491Z', + }, + destination: { + Config: { + restApiKey: 'dummyApiKey', + prefixProperties: true, + useNativeSDK: false, + }, + DestinationDefinition: { + DisplayName: 'Braze', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'BRAZE', + }, + Enabled: true, + ID: '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Braze', + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Invalid Order Completed event: Message properties and product at index: 0 is missing currency', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'BRAZE', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, + { + name: 'braze', + description: 'Test 31', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2020-09-14T12:09:37.491Z', + userId: 'Randomuser2222', + channel: 'web', + context: { + os: { + name: '', + version: '', + }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.3', + namespace: 'com.rudderlabs.javascript', + }, + page: { + url: 'file:///Users/manashi/Desktop/rudder-all-sdk-application-testing/Fullstory%20test%20By%20JS%20SDK/braze.html', + path: '/Users/manashi/Desktop/rudder-all-sdk-application-testing/Fullstory%20test%20By%20JS%20SDK/braze.html', + title: 'Fullstory Test', + search: '', + referrer: '', + }, + locale: 'en-GB', + screen: { + density: 2, + }, + traits: { + email: 'manashi@gmaiol.com', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.3', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36', + }, + messageId: '24ecc509-ce3e-473c-8483-ba1ea2c195cb', + properties: { + products: [undefined], + }, + anonymousId: 'c6ff1462-b692-43d6-8f6a-659efedc99ea', + integrations: { + All: true, + }, + originalTimestamp: '2020-09-14T12:09:37.491Z', + }, + destination: { + Config: { + restApiKey: 'dummyApiKey', + prefixProperties: true, + useNativeSDK: false, + }, + DestinationDefinition: { + DisplayName: 'Braze', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'BRAZE', + }, + Enabled: true, + ID: '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Braze', + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Invalid Order Completed event: Products array is empty', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'BRAZE', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/braze/router/data.ts b/test/integrations/destinations/braze/router/data.ts index 76201bda41..6803742e86 100644 --- a/test/integrations/destinations/braze/router/data.ts +++ b/test/integrations/destinations/braze/router/data.ts @@ -640,6 +640,59 @@ export const data = [ userId: 'user@50', }, }, + { + destination: { + ID: '2N9UakqKF0D35wfzSeofIxPdL8X', + Name: 'Braze-Test', + Config: { + appKey: '0e5440c3-226b-45d0-91b5-c64da56cde16', + blacklistedEvents: [], + dataCenter: 'US-03', + enableNestedArrayOperations: false, + enableSubscriptionGroupInGroupCall: false, + eventFilteringOption: 'disable', + oneTrustCookieCategories: [], + restApiKey: 'dummyApiKey', + supportDedup: true, + trackAnonymousUser: true, + whitelistedEvents: [], + }, + Enabled: true, + WorkspaceID: '27O0bhB6p5ehfOWeeZlOSsSDTLg', + Transformations: [], + IsProcessorEnabled: true, + RevisionID: '2N9Uaf2tWq2QRmatBWQm03Rz6qX', + }, + metadata: { jobId: 5, userId: 'u1' }, + message: { + anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.5', + }, + ip: '0.0.0.0', + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.5' }, + locale: 'en-GB', + os: { name: '', version: '' }, + screen: { density: 2 }, + traits: { + city: 'Disney', + email: 'mickey@disney.com', + firstName: 'Mickey', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + }, + integrations: { All: true }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + type: 'identify', + userId: 'user@50', + }, + }, ], destType: 'braze', }, @@ -688,6 +741,10 @@ export const data = [ external_id: 'user@50', first_name: 'Mickey', }, + { + country: 'USA', + external_id: 'user@50', + }, ], events: [ { @@ -733,6 +790,7 @@ export const data = [ { jobId: 1, userId: 'u1' }, { jobId: 2, userId: 'u1' }, { jobId: 3, userId: 'u1' }, + { jobId: 4, userId: 'u1' }, ], batched: true, statusCode: 200, @@ -771,7 +829,7 @@ export const data = [ }, statusCode: 400, batched: false, - metadata: [{ jobId: 4, userId: 'u1' }], + metadata: [{ jobId: 5, userId: 'u1' }], destination: { ID: '2N9UakqKF0D35wfzSeofIxPdL8X', Name: 'Braze-Test', diff --git a/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts b/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts index 929af485d8..288a06bfe6 100644 --- a/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts +++ b/test/integrations/destinations/campaign_manager/dataDelivery/oauth.ts @@ -326,14 +326,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 401, body: { output: { response: [ { error: '{"error":{"code":401,"message":"Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","errors":[{"message":"Login Required.","domain":"global","reason":"required","location":"Authorization","locationType":"header"}],"status":"UNAUTHENTICATED","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"CREDENTIALS_MISSING","domain":"googleapis.com","metadata":{"method":"google.ads.xfa.op.v4.DfareportingConversions.Batchinsert","service":"googleapis.com"}}]}}', - statusCode: 500, + statusCode: 401, metadata: { jobId: 1, attemptNum: 1, @@ -361,7 +361,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'REFRESH_TOKEN', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 401, }, }, }, @@ -389,14 +389,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 403, body: { output: { response: [ { error: '{"error":{"code":403,"message":"Request had insufficient authentication scopes.","errors":[{"message":"Insufficient Permission","domain":"global","reason":"insufficientPermissions"}],"status":"PERMISSION_DENIED","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"ACCESS_TOKEN_SCOPE_INSUFFICIENT","domain":"googleapis.com","metadata":{"service":"gmail.googleapis.com","method":"caribou.api.proto.MailboxService.GetProfile"}}]}}', - statusCode: 500, + statusCode: 403, metadata: { jobId: 1, attemptNum: 1, @@ -424,7 +424,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'AUTH_STATUS_INACTIVE', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 403, }, }, }, @@ -452,14 +452,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 403, body: { output: { response: [ { error: '{"error":{"code":403,"message":"invalid_grant","error_description":"Bad accesss"}}', - statusCode: 500, + statusCode: 403, metadata: { jobId: 1, attemptNum: 1, @@ -487,7 +487,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'AUTH_STATUS_INACTIVE', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 403, }, }, }, @@ -514,14 +514,14 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ }, output: { response: { - status: 500, + status: 401, body: { output: { response: [ { error: '{"error":"unauthorized","error_description":"Access token expired: 2020-10-20T12:00:00.000Z"}', - statusCode: 500, + statusCode: 401, metadata: { jobId: 1, attemptNum: 1, @@ -549,7 +549,7 @@ export const v1oauthScenarios: ProxyV1TestData[] = [ authErrorCategory: 'REFRESH_TOKEN', message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 401, }, }, }, diff --git a/test/integrations/destinations/campaign_manager/dataDelivery/other.ts b/test/integrations/destinations/campaign_manager/dataDelivery/other.ts index 709f55a4c0..1c0c45728c 100644 --- a/test/integrations/destinations/campaign_manager/dataDelivery/other.ts +++ b/test/integrations/destinations/campaign_manager/dataDelivery/other.ts @@ -260,7 +260,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ { error: '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', - statusCode: 500, + statusCode: 503, metadata: { jobId: 1, attemptNum: 1, @@ -287,7 +287,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ }, message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 503, }, }, }, @@ -376,7 +376,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ response: [ { error: '"Gateway Timeout"', - statusCode: 500, + statusCode: 504, metadata: { jobId: 1, attemptNum: 1, @@ -403,7 +403,7 @@ export const otherScenariosV1: ProxyV1TestData[] = [ }, message: 'Campaign Manager: Error transformer proxy v1 during CAMPAIGN_MANAGER response transformation', - status: 500, + status: 504, }, }, }, diff --git a/test/integrations/destinations/campaign_manager/processor/data.ts b/test/integrations/destinations/campaign_manager/processor/data.ts index beff44c928..9aa41691c6 100644 --- a/test/integrations/destinations/campaign_manager/processor/data.ts +++ b/test/integrations/destinations/campaign_manager/processor/data.ts @@ -827,4 +827,517 @@ export const data = [ }, }, }, + { + name: 'campaign_manager', + description: 'Test 6: Enhanced Conversions with un-hashed data in request payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + id: '0572f78fa49c648e', + name: 'generic_x86_arm', + type: 'Android', + model: 'AOSP on IA Emulator', + manufacturer: 'Google', + adTrackingEnabled: true, + advertisingId: '44c97318-9040-4361-8bc7-4eb30f665ca8', + }, + traits: { + email: 'alex@example.com', + phone: '+1-202-555-0146', + firstName: 'John', + lastName: 'Gomes', + city: 'London', + state: 'England', + country: 'GB', + postalCode: 'EC3M', + street: '71 Cherry Court SOUTHAMPTON SO53 5PD UK', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + originalTimestamp: '2022-11-17T00:22:02.903+05:30', + properties: { + profileId: '34245', + floodlightConfigurationId: '213123123', + ordinal: 'string', + floodlightActivityId: '456543345245', + value: '756', + encryptedUserIdCandidates: ['dfghjbnm'], + quantity: '455678', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + encryptionEntityType: 'DCM_ACCOUNT', + requestType: 'batchupdate', + }, + type: 'track', + event: 'event test', + anonymousId: 'randomId', + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + profileId: '5343234', + treatmentForUnderage: false, + limitAdTracking: false, + childDirectedTreatment: false, + nonPersonalizedAd: false, + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + enableEnhancedConversions: true, + isHashingRequired: true, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://dfareporting.googleapis.com/dfareporting/v4/userprofiles/34245/conversions/batchupdate', + headers: { + Authorization: 'Bearer dummyApiToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + kind: 'dfareporting#conversionsBatchUpdateRequest', + encryptionInfo: { + encryptionEntityType: 'DCM_ACCOUNT', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + kind: 'dfareporting#encryptionInfo', + }, + conversions: [ + { + floodlightConfigurationId: '213123123', + ordinal: 'string', + timestampMicros: '1668624722903000', + floodlightActivityId: '456543345245', + quantity: '455678', + value: 756, + encryptedUserIdCandidates: ['dfghjbnm'], + nonPersonalizedAd: false, + treatmentForUnderage: false, + userIdentifiers: [ + { + hashedEmail: + '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + }, + { + hashedPhoneNumber: + 'ec7e6b85f24fa6b796f1017236463f1b7160fbdc5e663e39ab363b6d6fe30b9f', + }, + { + addressInfo: { + hashedFirstName: + '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + hashedLastName: + '12918b23d69d698324a78a8ab8f5060fdb25537ea9620f956d39adca151c3ef9', + hashedStreetAddress: + '5c100d86e9f40bb62a85ca821ff93d96aff6b0dc4c792794c4a4d51ec9246eff', + city: 'London', + state: 'England', + postalCode: 'EC3M', + countryCode: 'GB', + }, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'campaign_manager', + description: 'Test 7: Enhanced Conversions with hashed data in request payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + id: '0572f78fa49c648e', + name: 'generic_x86_arm', + type: 'Android', + model: 'AOSP on IA Emulator', + manufacturer: 'Google', + adTrackingEnabled: true, + advertisingId: '44c97318-9040-4361-8bc7-4eb30f665ca8', + }, + traits: { + email: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + phone: '', + firstName: '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + lastName: '12918b23d69d698324a78a8ab8f5060fdb25537ea9620f956d39adca151c3ef9', + city: 'London', + state: 'England', + country: 'GB', + postalCode: 'EC3M', + street: '5c100d86e9f40bb62a85ca821ff93d96aff6b0dc4c792794c4a4d51ec9246eff', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + originalTimestamp: '2022-11-17T00:22:02.903+05:30', + properties: { + profileId: '34245', + floodlightConfigurationId: '213123123', + ordinal: 'string', + floodlightActivityId: '456543345245', + value: '756', + encryptedUserIdCandidates: ['dfghjbnm'], + quantity: '455678', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + encryptionEntityType: 'DCM_ACCOUNT', + requestType: 'batchupdate', + }, + type: 'track', + event: 'event test', + anonymousId: 'randomId', + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + profileId: '5343234', + treatmentForUnderage: false, + limitAdTracking: false, + childDirectedTreatment: false, + nonPersonalizedAd: false, + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + enableEnhancedConversions: true, + isHashingRequired: false, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://dfareporting.googleapis.com/dfareporting/v4/userprofiles/34245/conversions/batchupdate', + headers: { + Authorization: 'Bearer dummyApiToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + kind: 'dfareporting#conversionsBatchUpdateRequest', + encryptionInfo: { + encryptionEntityType: 'DCM_ACCOUNT', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + kind: 'dfareporting#encryptionInfo', + }, + conversions: [ + { + floodlightConfigurationId: '213123123', + ordinal: 'string', + timestampMicros: '1668624722903000', + floodlightActivityId: '456543345245', + quantity: '455678', + value: 756, + encryptedUserIdCandidates: ['dfghjbnm'], + nonPersonalizedAd: false, + treatmentForUnderage: false, + userIdentifiers: [ + { + hashedEmail: + '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + }, + { + addressInfo: { + hashedFirstName: + '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + hashedLastName: + '12918b23d69d698324a78a8ab8f5060fdb25537ea9620f956d39adca151c3ef9', + hashedStreetAddress: + '5c100d86e9f40bb62a85ca821ff93d96aff6b0dc4c792794c4a4d51ec9246eff', + city: 'London', + state: 'England', + postalCode: 'EC3M', + countryCode: 'GB', + }, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'campaign_manager', + description: 'Test 8: Enhanced Conversions with no traits in request payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + id: '0572f78fa49c648e', + name: 'generic_x86_arm', + type: 'Android', + model: 'AOSP on IA Emulator', + manufacturer: 'Google', + adTrackingEnabled: true, + advertisingId: '44c97318-9040-4361-8bc7-4eb30f665ca8', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + originalTimestamp: '2022-11-17T00:22:02.903+05:30', + properties: { + profileId: '34245', + floodlightConfigurationId: '213123123', + ordinal: 'string', + floodlightActivityId: '456543345245', + value: '756', + encryptedUserIdCandidates: ['dfghjbnm'], + quantity: '455678', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + encryptionEntityType: 'DCM_ACCOUNT', + requestType: 'batchupdate', + }, + type: 'track', + event: 'event test', + anonymousId: 'randomId', + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + profileId: '5343234', + treatmentForUnderage: false, + limitAdTracking: false, + childDirectedTreatment: false, + nonPersonalizedAd: false, + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + enableEnhancedConversions: true, + isHashingRequired: true, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://dfareporting.googleapis.com/dfareporting/v4/userprofiles/34245/conversions/batchupdate', + headers: { + Authorization: 'Bearer dummyApiToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + kind: 'dfareporting#conversionsBatchUpdateRequest', + encryptionInfo: { + encryptionEntityType: 'DCM_ACCOUNT', + encryptionSource: 'AD_SERVING', + encryptionEntityId: '3564523', + kind: 'dfareporting#encryptionInfo', + }, + conversions: [ + { + floodlightConfigurationId: '213123123', + ordinal: 'string', + timestampMicros: '1668624722903000', + floodlightActivityId: '456543345245', + quantity: '455678', + value: 756, + encryptedUserIdCandidates: ['dfghjbnm'], + nonPersonalizedAd: false, + treatmentForUnderage: false, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'dummyApiToken', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/clevertap/dataDelivery/business.ts b/test/integrations/destinations/clevertap/dataDelivery/business.ts new file mode 100644 index 0000000000..d9f83f52f3 --- /dev/null +++ b/test/integrations/destinations/clevertap/dataDelivery/business.ts @@ -0,0 +1,284 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; + +const params = { + destination: 'clevertap', +}; +const headers = { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': + 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', + 'Content-Type': 'application/json', +}; + +const statTags = { + destType: 'CLEVERTAP', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const V1BusinessTestScenarion: ProxyV1TestData[] = [ + { + id: 'clevertap_business_0', + scenario: 'business', + successCriteria: 'should return 200 status code with success message', + name: 'clevertap', + description: '[business]:: create an user through identify call', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers, + JSON: { + d: [ + { + type: 'profile', + profileData: { + Email: 'jamesDoe@gmail.com', + Name: 'James Doe', + Phone: '92374162212', + Gender: 'M', + Employed: true, + DOB: '1614775793', + Education: 'Science', + Married: 'Y', + 'Customer Type': 'Prime', + graduate: true, + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_tags: '["Test_User","Interested_User","DIY_Hobby"]', + custom_mappings: '{"Office":"Trastkiv","Country":"Russia"}', + address: + '{"city":"kolkata","country":"India","postalCode":789223,"state":"WB","street":""}', + }, + identity: 'anon_id', + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test1', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"success","processed":1,"unprocessed":[]}', + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'clevertap_business_1', + scenario: 'business', + successCriteria: 'should return 401 status code with error message', + name: 'clevertap', + description: '[business]:: event failed due to invalid credentials', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers: { + 'X-CleverTap-Account-Id': 'fakeId123', + 'X-CleverTap-Passcode': 'fakePasscode123', + 'Content-Type': 'application/json', + }, + JSON: { + d: [ + { + identity: 'anon-id-new', + type: 'event', + evtName: 'Web Page Viewed: Rudder', + evtData: { + title: 'Home', + path: '/', + }, + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test2', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 401, + message: 'Request failed with status: 401', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"fail","error":"Invalid Credentials","code":401}', + statusCode: 401, + }, + ], + statTags, + }, + }, + }, + }, + }, + { + id: 'clevertap_business_2', + scenario: 'business', + successCriteria: 'should return 401 status code with error message', + name: 'clevertap', + description: '[business]:: event failed due to invalid credentials', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers: { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': + 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', + 'Content-Type': 'application/json', + }, + JSON: { + d: [ + { + identity: 'anon-id-new', + type: 'event', + evtData: { + title: 'Home', + path: '/', + }, + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test3', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + message: 'Request failed with status: 200', + response: [ + { + metadata: generateMetadata(123), + error: '{"status":"fail","processed":0,"unprocessed":[]}', + statusCode: 400, + }, + ], + statTags, + }, + }, + }, + }, + }, + { + id: 'clevertap_business_3', + scenario: 'business', + successCriteria: 'should return 200 status code with success message', + name: 'clevertap', + description: '[business]:: create an user through identify call', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + params, + headers, + JSON: { + d: [ + { + identity: 'testUser1', + type: 'profile', + profileData: { + Name: 'Test User1', + Email: 'test1@testMail.com', + }, + }, + { + evtData: { + name: 1234, + revenue: 4.99, + }, + type: 'event', + identity: 'user123', + }, + { + identity: 'testUser2', + type: 'profile', + profileData: { + Name: 'Test User2', + Email: 'test2@testMail.com', + }, + }, + ], + }, + endpoint: 'https://api.clevertap.com/1/upload/test4', + }, + [generateMetadata(123)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + statTags, + status: 400, + message: 'Request failed with status: 200', + response: [ + { + metadata: generateMetadata(123), + error: + '{"status":"partial","processed":2,"unprocessed":[{"status":"fail","code":509,"error":"Event Name is incorrect. ErrorCode: 509 - Event name is mandatory. Skipped record number : 2","record":{"evtData":{"name":1234,"revenue":4.99},"type":"event","identity":"user123"}}]}', + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/clevertap/dataDelivery/data.ts b/test/integrations/destinations/clevertap/dataDelivery/data.ts index 8032dd50c8..57e0d0ceea 100644 --- a/test/integrations/destinations/clevertap/dataDelivery/data.ts +++ b/test/integrations/destinations/clevertap/dataDelivery/data.ts @@ -1,4 +1,5 @@ -export const data = [ +import { V1BusinessTestScenarion } from './business'; +const oldV0TestCases = [ { name: 'clevertap', description: 'Test 0', @@ -228,3 +229,5 @@ export const data = [ }, }, ]; + +export const data = [...oldV0TestCases, ...V1BusinessTestScenarion]; diff --git a/test/integrations/destinations/clevertap/network.ts b/test/integrations/destinations/clevertap/network.ts index 57a647e684..9122ba1129 100644 --- a/test/integrations/destinations/clevertap/network.ts +++ b/test/integrations/destinations/clevertap/network.ts @@ -87,6 +87,35 @@ const dataDeliveryMocksData = [ }, httpRes: { data: { status: 'fail', processed: 0, unprocessed: [] }, status: 200 }, }, + { + httpReq: { + url: 'https://api.clevertap.com/1/upload/test4', + method: 'POST', + }, + httpRes: { + data: { + status: 'partial', + processed: 2, + unprocessed: [ + { + status: 'fail', + code: 509, + error: + 'Event Name is incorrect. ErrorCode: 509 - Event name is mandatory. Skipped record number : 2', + record: { + evtData: { + name: 1234, + revenue: 4.99, + }, + type: 'event', + identity: 'user123', + }, + }, + ], + }, + status: 200, + }, + }, ]; const deleteNwData = [ { diff --git a/test/integrations/destinations/clevertap/processor/data.ts b/test/integrations/destinations/clevertap/processor/data.ts index 6309c5ec8a..1d7bdd7e78 100644 --- a/test/integrations/destinations/clevertap/processor/data.ts +++ b/test/integrations/destinations/clevertap/processor/data.ts @@ -122,6 +122,130 @@ export const data = [ }, }, }, + { + name: 'clevertap', + description: 'Should not load email from externalId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + passcode: 'sample_passcode', + accountId: '476550467', + trackAnonymous: true, + enableObjectIdMapping: false, + }, + }, + message: { + channel: 'web', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: 'anon_id', + type: 'identify', + traits: { + anonymousId: 'anon_id', + name: 'James Doe', + phone: '92374162212', + gender: 'M', + employed: true, + birthday: '1614775793', + education: 'Science', + graduate: true, + married: true, + customerType: 'Prime', + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_tags: ['Test_User', 'Interested_User', 'DIY_Hobby'], + custom_mappings: { + Office: 'Trastkiv', + Country: 'Russia', + }, + address: { + city: 'kolkata', + country: 'India', + postalCode: 789223, + state: 'WB', + street: '', + }, + 'category-unsubscribe': { email: ['Marketing', 'Transactional'] }, + }, + context: { + externalId: [{ type: 'someId', id: 'someID' }], + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.clevertap.com/1/upload', + headers: { + 'X-CleverTap-Account-Id': '476550467', + 'X-CleverTap-Passcode': 'sample_passcode', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + d: [ + { + type: 'profile', + profileData: { + Name: 'James Doe', + Phone: '92374162212', + Gender: 'M', + Employed: true, + DOB: '1614775793', + Education: 'Science', + Married: true, + 'Customer Type': 'Prime', + graduate: true, + msg_push: true, + msgSms: true, + msgemail: true, + msgwhatsapp: false, + custom_mappings: '{"Office":"Trastkiv","Country":"Russia"}', + custom_tags: '["Test_User","Interested_User","DIY_Hobby"]', + address: + '{"city":"kolkata","country":"India","postalCode":789223,"state":"WB","street":""}', + 'category-unsubscribe': { email: ['Marketing', 'Transactional'] }, + }, + identity: 'anon_id', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, { name: 'clevertap', description: 'Test 1', diff --git a/test/integrations/destinations/delighted/network.ts b/test/integrations/destinations/delighted/network.ts index d9896a25e8..1ccc785ea3 100644 --- a/test/integrations/destinations/delighted/network.ts +++ b/test/integrations/destinations/delighted/network.ts @@ -27,4 +27,18 @@ export const networkCallsData = [ status: 200, }, }, + { + httpReq: { + url: 'https://api.delighted.com/v1/people.json', + method: 'GET', + headers: { Authorization: `Basic ZHVtbXlBcGlLZXlmb3JmYWlsdXJl` }, + params: { + email: 'test@rudderlabs.com', + }, + }, + httpRes: { + status: 429, + data: {}, + }, + }, ]; diff --git a/test/integrations/destinations/delighted/processor/data.ts b/test/integrations/destinations/delighted/processor/data.ts index 7a5ad7de9d..f35c2d8ecb 100644 --- a/test/integrations/destinations/delighted/processor/data.ts +++ b/test/integrations/destinations/delighted/processor/data.ts @@ -944,4 +944,93 @@ export const data = [ }, }, }, + { + name: 'delighted', + description: 'Too many request test', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + apiKey: 'dummyApiKeyforfailure', + channel: 'email', + delay: 0, + eventNamesSettings: [ + { + event: 'Product Reviewed', + }, + ], + }, + }, + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + type: 'track', + userId: 'test@rudderlabs.com', + event: 'Product Reviewed', + properties: { + review_id: '12345', + product_id: '123', + rating: 3, + review_body: 'Average product, expected much more.', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"Error occurred while checking user: {}","destinationResponse":{"response":{},"status":429}}', + statTags: { + destType: 'DELIGHTED', + errorCategory: 'network', + errorType: 'throttled', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 429, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/emarsys/dataDelivery/data.ts b/test/integrations/destinations/emarsys/dataDelivery/data.ts new file mode 100644 index 0000000000..ac3ec780f7 --- /dev/null +++ b/test/integrations/destinations/emarsys/dataDelivery/data.ts @@ -0,0 +1,560 @@ +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +export const headerBlockWithCorrectAccessToken = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', +}; + +export const headerBlockWithWrongAccessToken = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy2", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', +}; + +export const correctContactCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + }, + { + '2': true, + '3': 'abcde', + '10569': 'efgh', + '10519': 1234, + '31': 2, + '39': 'abc', + }, +]; + +export const wrongContactCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + }, + { + '2': true, + '3': 'person0@example.com', + '10569': 1234, + '10519': 'efgh', + '31': 2, + '39': 'abc', + }, +]; + +export const contactPayload = { + key_id: 10569, + contacts: correctContactCreateUpdateData, + contact_list_id: 'dummy', +}; + +export const correctGroupCallPayload = { + key_id: 'right_id', + external_ids: ['personABC@example.com'], +}; + +export const groupPayloadWithWrongKeyId = { + key_id: 'wrong_id', + external_ids: ['efghi', 'jklmn'], +}; + +export const groupPayloadWithWrongExternalId = { + key_id: 'right_id', + external_ids: ['efghi', 'jklmn', 'unknown', 'person4@example.com'], +}; + +export const correctContactWithWrongKeyIdCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + '100': 'abc', + }, + { + '2': true, + '3': 'abcde', + '10569': 'efgh', + '10519': 1234, + '31': 2, + '39': 'abc', + '100': 'abc', + }, +]; + +export const statTags = { + destType: 'EMARSYS', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const metadata = [ + { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, + }, + { + jobId: 2, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, + }, +]; + +const commonIdentifyRequestParametersWithWrongData = { + method: 'PUT', + headers: headerBlockWithCorrectAccessToken, + JSON: { ...contactPayload, contacts: wrongContactCreateUpdateData }, +}; + +const commonIdentifyRequestParameters = { + method: 'PUT', + headers: headerBlockWithCorrectAccessToken, + JSON: { ...contactPayload }, +}; + +const commonIdentifyRequestParametersWithWrongKeyId = { + method: 'PUT', + headers: headerBlockWithCorrectAccessToken, + JSON: { + ...contactPayload, + contacts: correctContactWithWrongKeyIdCreateUpdateData, + key_id: 100, + }, +}; + +const commonGroupRequestParametersWithWrongData = { + method: 'POST', + headers: headerBlockWithCorrectAccessToken, + JSON: groupPayloadWithWrongExternalId, +}; + +const commonGroupRequestParameters = { + method: 'POST', + headers: headerBlockWithCorrectAccessToken, + JSON: correctGroupCallPayload, +}; + +const commonGroupRequestParametersWithWrongKeyId = { + method: 'POST', + headers: headerBlockWithCorrectAccessToken, + JSON: groupPayloadWithWrongKeyId, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Identify Event fails due to wrong key_id', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + ...commonIdentifyRequestParametersWithWrongKeyId, + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + status: 200, + data: { + ids: [], + errors: { + '': { + '2004': 'Invalid key field id: 100', + }, + }, + }, + }, + status: 200, + }, + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: '{"2004":"Invalid key field id: 100"}', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: '{"2004":"Invalid key field id: 100"}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'correct Identify event passes with 200 status code', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1`, + ...commonIdentifyRequestParameters, + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + replyCode: 0, + replyText: 'OK', + data: { ids: ['138621551', 968984932] }, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 200, + metadata: generateMetadata(2), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Identify Event fails due to wrong data', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1`, + ...commonIdentifyRequestParametersWithWrongData, + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + status: 200, + data: { + ids: ['138621551'], + errors: { + '1234': { + '2010': 'Contacts with the external id already exist: 3', + }, + }, + }, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: '{"2010":"Contacts with the external id already exist: 3"}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'correct Group event passes with 200 status code', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contactlist/900337462/add`, + ...commonGroupRequestParameters, + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + replyCode: 0, + replyText: 'OK', + data: { errors: [], inserted_contacts: 1 }, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Group Event fails due to wrong key_id', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contactlist/900337462/add`, + ...commonGroupRequestParametersWithWrongKeyId, + }, + metadata, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags, + message: + 'EMARSYS: Error transformer proxy v1 during EMARSYS response transformation. Invalid key field id: wrong_id', + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: '{"replyCode":2004,"replyText":"Invalid key field id: wrong_id","data":""}', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: '{"replyCode":2004,"replyText":"Invalid key field id: wrong_id","data":""}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Group Event fails due to wrong data', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: `https://api.emarsys.net/api/v2/contactlist/900337462/add`, + ...commonGroupRequestParametersWithWrongData, + }, + [generateMetadata(1), generateMetadata(2), generateMetadata(3), generateMetadata(4)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[EMARSYS Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + replyCode: 0, + replyText: 'OK', + data: { + inserted_contacts: 2, + errors: { + jklmn: { + '2008': 'No contact found with the external id: 3', + }, + unknown: { + '2008': 'No contact found with the external id: 3', + }, + }, + }, + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + { + statusCode: 400, + metadata: generateMetadata(2), + error: '{"2008":"No contact found with the external id: 3"}', + }, + { + statusCode: 400, + metadata: generateMetadata(3), + error: '{"2008":"No contact found with the external id: 3"}', + }, + { + statusCode: 200, + metadata: generateMetadata(4), + error: 'success', + }, + ], + }, + }, + }, + }, + }, + { + id: 'emarsys_v1_scenario_1', + name: 'emarsys', + description: 'Group Event fails due to wrong contact list id', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://api.emarsys.net/api/v2/contactlist/wrong-id/add', + ...commonGroupRequestParameters, + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags, + message: + 'EMARSYS: Error transformer proxy v1 during EMARSYS response transformation. Action Wrong-id is invalid.', + response: [ + { + statusCode: 400, + metadata: generateMetadata(1), + error: '{"replyCode":1,"replyText":"Action Wrong-id is invalid.","data":""}', + }, + ], + }, + }, + }, + }, + }, +]; + +export const data = [...testScenariosForV1API]; diff --git a/test/integrations/destinations/emarsys/deleteUsers/data.ts b/test/integrations/destinations/emarsys/deleteUsers/data.ts new file mode 100644 index 0000000000..2bafe58a4c --- /dev/null +++ b/test/integrations/destinations/emarsys/deleteUsers/data.ts @@ -0,0 +1,235 @@ +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-14')); + jest.mock('crypto', () => ({ + ...jest.requireActual('crypto'), + randomBytes: jest.fn().mockReturnValue(Buffer.from('5398e214ae99c2e50afb709a3bc423f9', 'hex')), + })); +}; + +const commonEventMap = [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, +]; + +const commonFieldMap = [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, +]; + +export const data = [ + { + name: 'emarsys', + description: 'Missing emersysUsername key', + feature: 'userDeletion', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destType: 'EMARSYS', + userAttributes: [ + { + userId: '1234', + phone: '1234567890', + email: 'abc@xyc.com', + }, + ], + config: { + discardEmptyProperties: true, + emersysUsername: undefined, + emersysUserSecret: 'dummySecret', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: commonEventMap, + fieldMapping: commonFieldMap, + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + ], + }, + }, + output: { + response: { + status: 400, + body: [ + { + statusCode: 400, + error: 'Either Emarsys user name or user secret is missing. Aborting', + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Default contact list is not configured', + feature: 'userDeletion', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destType: 'EMARSYS', + userAttributes: [ + { + userId: '1234', + phone: '1234567890', + email: 'abc@xyc.com', + lastName: 'doe', + }, + ], + config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '2', + defaultContactList: undefined, + eventsMapping: commonEventMap, + fieldMapping: commonFieldMap, + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + ], + }, + }, + output: { + response: { + status: 400, + body: [ + { + statusCode: 400, + error: 'No audience list is configured. Aborting', + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'custom identifier is not present in user attribute', + feature: 'userDeletion', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destType: 'EMARSYS', + userAttributes: [ + { + userId: '1234', + phone: '1234567890', + lastName: 'doe', + }, + ], + config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: commonEventMap, + fieldMapping: commonFieldMap, + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 200, + status: 'successful', + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'user not present for deletion', + feature: 'userDeletion', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destType: 'EMARSYS', + userAttributes: [ + { + userId: '1234', + email: 'abc@gmail.com', + phone: '1234567890', + lastName: 'doe', + }, + ], + config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: commonEventMap, + fieldMapping: commonFieldMap, + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 200, + status: 'successful', + }, + ], + }, + }, + }, +].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/emarsys/network.ts b/test/integrations/destinations/emarsys/network.ts new file mode 100644 index 0000000000..c4954afd91 --- /dev/null +++ b/test/integrations/destinations/emarsys/network.ts @@ -0,0 +1,298 @@ +export const headerBlockWithCorrectAccessToken = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', +}; + +export const headerBlockWithWrongAccessToken = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy2", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', +}; + +export const correctContactCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + }, + { + '2': true, + '3': 'abcde', + '10569': 'efgh', + '10519': 1234, + '31': 2, + '39': 'abc', + }, +]; + +export const correctContactWithWrongKeyIdCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + '100': 'abc', + }, + { + '2': true, + '3': 'abcde', + '10569': 'efgh', + '10519': 1234, + '31': 2, + '39': 'abc', + '100': 'abc', + }, +]; + +export const wrongContactCreateUpdateData = [ + { + '2': 'Person0', + '3': 'person0@example.com', + '10569': 'efghi', + '10519': 'efghi', + '31': 1, + '39': 'abc', + }, + { + '2': true, + '3': 'person0@example.com', + '10569': 1234, + '10519': 'efgh', + '31': 2, + '39': 'abc', + }, +]; + +export const contactPayload = { + key_id: 10569, + contacts: correctContactCreateUpdateData, + contact_list_id: 'dummy', +}; + +export const correctGroupCallPayload = { + key_id: 'right_id', + external_ids: ['personABC@example.com'], +}; + +export const groupPayloadWithWrongKeyId = { + key_id: 'wrong_id', + external_ids: ['efghi', 'jklmn'], +}; + +export const groupPayloadWithWrongExternalId = { + key_id: 'right_id', + external_ids: ['efghi', 'jklmn', 'unknown', 'person4@example.com'], +}; + +export const comonHeader = { + Accept: 'application/json', + 'Content-Type': 'application/json', + + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="MjEzMDY5ZmI3NjMwNzE1N2M1ZTI5MWMzMzI3ODQxNDU2YWM4NTI3YQ==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2023-10-14T00:00:00.000Z"', +}; + +// MOCK DATA +const businessMockData = [ + { + description: 'Mock response from destination depicting request with a correct contact payload', + httpReq: { + method: 'PUT', + url: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: headerBlockWithCorrectAccessToken, + data: contactPayload, + }, + httpRes: { + data: { + replyCode: 0, + replyText: 'OK', + data: { ids: ['138621551', 968984932] }, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a partially wrong contact payload', + httpReq: { + method: 'PUT', + url: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: headerBlockWithCorrectAccessToken, + data: { ...contactPayload, contacts: wrongContactCreateUpdateData }, + }, + httpRes: { + data: { + data: { + ids: ['138621551'], + errors: { '1234': { '2010': 'Contacts with the external id already exist: 3' } }, + }, + status: 200, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting request with a wrong key_id in payload', + httpReq: { + method: 'PUT', + url: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: headerBlockWithCorrectAccessToken, + data: { + ...contactPayload, + contacts: correctContactWithWrongKeyIdCreateUpdateData, + key_id: 100, + }, + }, + httpRes: { + data: { + data: { ids: [], errors: { '': { '2004': 'Invalid key field id: 100' } } }, + status: 200, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination for correct group call ', + httpReq: { + method: 'POST', + url: 'https://api.emarsys.net/api/v2/contactlist/900337462/add', + headers: headerBlockWithCorrectAccessToken, + data: correctGroupCallPayload, + }, + httpRes: { + data: { replyCode: 0, replyText: 'OK', data: { inserted_contacts: 1, errors: [] } }, + status: 200, + }, + }, + { + description: 'Mock response from destination for group call with wrong key_id ', + httpReq: { + method: 'POST', + url: 'https://api.emarsys.net/api/v2/contactlist/900337462/add', + headers: headerBlockWithCorrectAccessToken, + data: groupPayloadWithWrongKeyId, + }, + httpRes: { + data: { replyCode: 2004, replyText: 'Invalid key field id: wrong_id', data: '' }, + status: 400, + }, + }, + { + description: 'Mock response from destination for group call with wrong data ', + httpReq: { + method: 'POST', + url: 'https://api.emarsys.net/api/v2/contactlist/900337462/add', + headers: headerBlockWithCorrectAccessToken, + data: groupPayloadWithWrongExternalId, + }, + httpRes: { + data: { + replyCode: 0, + replyText: 'OK', + data: { + inserted_contacts: 2, + errors: { + jklmn: { '2008': 'No contact found with the external id: 3' }, + unknown: { '2008': 'No contact found with the external id: 3' }, + }, + }, + }, + status: 200, + }, + }, + { + description: 'Mock response from destination for correct group call, with wrong contact list ', + httpReq: { + method: 'POST', + url: 'https://api.emarsys.net/api/v2/contactlist/wrong-id/add', + headers: headerBlockWithCorrectAccessToken, + data: correctGroupCallPayload, + }, + httpRes: { + data: { replyCode: 1, replyText: 'Action Wrong-id is invalid.', data: '' }, + status: 400, + }, + }, +]; + +const deleteNwData = [ + { + httpReq: { + method: 'post', + url: 'https://api.emarsys.net/api/v2/contact/delete', + data: { + key_id: 3, + contact_list_id: 'dummy', + 3: ['abc@gmail.com'], + }, + headers: comonHeader, + }, + httpRes: { + data: { + replyCode: 2008, + replyText: 'No contact found with the external id: 3 - abc@gmail.com', + data: '', + }, + status: 200, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.emarsys.net/api/v2/contact/delete', + data: { + userIds: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'], + }, + headers: comonHeader, + }, + httpRes: { + data: 'Your application has made too many requests in too short a time.', + status: 429, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.emarsys.net/api/v2/contact/delete', + data: { + userIds: ['9'], + }, + headers: comonHeader, + }, + httpRes: { + data: { + error: 'User deletion request failed', + }, + status: 400, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.emarsys.net/api/v2/contact/delete', + data: { + userIds: ['1', '2', '3'], + }, + headers: comonHeader, + }, + httpRes: { + data: { + requestId: 'request_1', + }, + status: 200, + }, + }, +]; + +export const networkCallsData = [...businessMockData, ...deleteNwData]; diff --git a/test/integrations/destinations/emarsys/processor/data.ts b/test/integrations/destinations/emarsys/processor/data.ts new file mode 100644 index 0000000000..cfa53fd4f6 --- /dev/null +++ b/test/integrations/destinations/emarsys/processor/data.ts @@ -0,0 +1,1497 @@ +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-14')); + jest.mock('crypto', () => ({ + ...jest.requireActual('crypto'), + randomBytes: jest.fn().mockReturnValue(Buffer.from('5398e214ae99c2e50afb709a3bc423f9', 'hex')), + })); +}; + +const comonHeader = { + Accept: 'application/json', + 'Content-Type': 'application/json', + + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="MjEzMDY5ZmI3NjMwNzE1N2M1ZTI5MWMzMzI3ODQxNDU2YWM4NTI3YQ==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2023-10-14T00:00:00.000Z"', +}; + +export const data = [ + { + name: 'emarsys', + description: 'Test 1 : Track call custom identifier mapped from destination config ', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/event/purchase/trigger', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'track', + destinationPayload: { + payload: { + key_id: '3', + external_id: 'abc@gmail.com', + trigger_id: 'EVENT_TRIGGER_ID', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + event_time: '2023-07-06T11:59:02.402+05:30', + }, + eventId: 'purchase', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: + 'Test 2 : Track call custom identifier mapped from destination config with custom field', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + custom_field: 'value', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + customIdentifierId: 'custom_id', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'custom_field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/event/purchase/trigger', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'track', + destinationPayload: { + payload: { + key_id: 'custom_id', + external_id: 'value', + trigger_id: 'EVENT_TRIGGER_ID', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + event_time: '2023-07-06T11:59:02.402+05:30', + }, + eventId: 'purchase', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 3: Track call with trigger id mapped from integrations object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + custom_field: 'value', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'custom_field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/event/purchase/trigger', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'track', + destinationPayload: { + payload: { + key_id: 3, + external_id: 'abc@gmail.com', + trigger_id: 'EVENT_TRIGGER_ID', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + event_time: '2023-07-06T11:59:02.402+05:30', + }, + eventId: 'purchase', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 4 : group call with default external id email ', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'custom_field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contactlist/dummy/add', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'group', + destinationPayload: { + payload: { + key_id: 3, + external_ids: ['abc@gmail.com'], + }, + contactListId: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 5 : group call, custom identifier id mapped from integration object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + custom_field: 'value', + }, + }, + integrations: { + All: true, + EMARSYS: { + customIdentifierId: 'custom_id', + }, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'custom_field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contactlist/dummy/add', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'group', + destinationPayload: { + payload: { + key_id: 'custom_id', + external_ids: ['value'], + }, + contactListId: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 6 : custom identifier mapped from destination config', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + custom_field: 'value', + }, + }, + integrations: { + All: true, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '2', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contactlist/dummy/add', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'group', + destinationPayload: { + payload: { + key_id: '2', + external_ids: ['Doe'], + }, + contactListId: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 7 : Identify call with contact list id mapped from integrations objects', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + 'custom-field': 'value', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + EMARSYS: { + contactListId: 123, + }, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '2', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'identify', + destinationPayload: { + key_id: '2', + contacts: [ + { + '2': 'one', + '3': 'testone@gmail.com', + custom_id: 'value', + }, + ], + contact_list_id: 123, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 8 : identify call customIdentifierId mapped from integration object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + 'custom-field': 'value', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + userId: 'testuserId1', + integrations: { + All: true, + EMARSYS: { + customIdentifierId: '1', + }, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '2', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'identify', + destinationPayload: { + key_id: '1', + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testone@gmail.com', + custom_id: 'value', + }, + ], + contact_list_id: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 9 : custom identifier mapped from default email value', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + 'custom-field': 'value', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: comonHeader, + params: {}, + body: { + JSON: { + eventType: 'identify', + destinationPayload: { + key_id: 3, + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testone@gmail.com', + custom_id: 'value', + }, + ], + contact_list_id: 'dummy', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: {}, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 10 : identify call error for not finding custom identifier', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + channel: 'web', + context: { + traits: { + firstName: 'test', + lastName: 'one', + 'custom-field': 'value', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Order Completed', + to: 'purchase', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'custom-field', + emersysProperty: 'custom_id', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either configured custom contact identifier value or default identifier email value is missing: Workflow: procWorkflow, Step: preparePayloadForIdentify, ChildStep: undefined, OriginalError: Either configured custom contact identifier value or default identifier email value is missing', + metadata: {}, + statTags: { + destType: 'EMARSYS', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'emarsys', + description: 'Test 11 : Track call with no event mapping field should fail', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: {}, + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + EMARSYS: { + trigger_id: 'EVENT_TRIGGER_ID', + }, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Order Completed is not mapped to any Emersys external event. Aborting: Workflow: procWorkflow, Step: preparePayloadForTrack, ChildStep: undefined, OriginalError: Order Completed is not mapped to any Emersys external event. Aborting', + metadata: {}, + statTags: { + destType: 'EMARSYS', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/emarsys/router/data.ts b/test/integrations/destinations/emarsys/router/data.ts new file mode 100644 index 0000000000..8f449bd351 --- /dev/null +++ b/test/integrations/destinations/emarsys/router/data.ts @@ -0,0 +1,646 @@ +import crypto from 'crypto'; +const config = { + discardEmptyProperties: true, + emersysUsername: 'dummy', + emersysUserSecret: 'dummy', + emersysCustomIdentifier: '3', + defaultContactList: 'dummy', + eventsMapping: [ + { + from: 'Order Completed', + to: 'purchase', + }, + { + from: 'Product Added', + to: 'addToCart', + }, + ], + fieldMapping: [ + { + rudderProperty: 'email', + emersysProperty: '3', + }, + { + rudderProperty: 'firstName', + emersysProperty: '1', + }, + { + rudderProperty: 'lastName', + emersysProperty: '2', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], +}; + +const commonDestination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'emarsys', + DisplayName: 'Emarsys', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: config, + Enabled: true, +}; + +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2019-10-14')); + jest.mock('crypto', () => ({ + ...jest.requireActual('crypto'), + randomBytes: jest.fn().mockReturnValue(Buffer.from('5398e214ae99c2e50afb709a3bc423f9', 'hex')), + })); +}; + +export const data = [ + { + id: 'emarsys-track-test-1', + name: 'emarsys', + description: 'combined batch', + scenario: 'Business', + successCriteria: + 'Identify, group events should be batched based on audience list and key_id criteria and track should not be batched ', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + channel: 'web', + context: { + traits: { + email: 'testone@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 1, + }, + destination: commonDestination, + }, + { + message: { + channel: 'web', + context: { + traits: { + email: 'testtwo@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 2, + }, + destination: commonDestination, + }, + { + message: { + channel: 'web', + context: { + traits: { + email: 'testtwo@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + EMARSYS: { + contactListId: 'dummy2', + customIdentifierId: '1', + }, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 3, + }, + destination: commonDestination, + }, + { + message: { + channel: 'web', + context: { + traits: { + email: 'testtwo@gmail.com', + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + EMARSYS: { + contactListId: 'dummy2', + customIdentifierId: '2', + }, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 4, + }, + destination: commonDestination, + }, + { + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 5, + }, + destination: commonDestination, + }, + { + message: { + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc2@gmail.com', + lastName: 'Doe2', + firstName: 'John2', + }, + }, + integrations: { + All: true, + }, + traits: { + company: 'testComp', + }, + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'group', + userId: 'userId06', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 6, + }, + destination: commonDestination, + }, + { + message: { + channel: 'web', + context: { + traits: { + firstName: 'test', + lastName: 'one', + }, + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + anonymousId: '123456', + event: 'product list viewed', + userId: 'testuserId1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 7, + }, + destination: commonDestination, + }, + { + message: { + event: 'Order Completed', + anonymousId: 'anonId06', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + traits: { + email: 'abc@gmail.com', + lastName: 'Doe', + firstName: 'John', + }, + }, + integrations: { + All: true, + }, + properties: { + company: 'testComp', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53709', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2023-07-06T11:59:02.402+05:30', + type: 'track', + userId: 'userId06', + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 8, + }, + destination: commonDestination, + }, + ], + destType: 'emarsys', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 7, + }, + ], + batched: false, + statusCode: 400, + error: + 'Either configured custom contact identifier value or default identifier email value is missing', + statTags: { + destType: 'EMARSYS', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'router', + implementation: 'cdkV2', + module: 'destination', + }, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '3', + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testone@gmail.com', + }, + { + '1': 'test', + '2': 'one', + '3': 'testtwo@gmail.com', + }, + ], + contact_list_id: 'dummy', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'PUT', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 1, + }, + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 2, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '1', + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testtwo@gmail.com', + }, + ], + contact_list_id: 'dummy2', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'PUT', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 3, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '2', + contacts: [ + { + '1': 'test', + '2': 'one', + '3': 'testtwo@gmail.com', + }, + ], + contact_list_id: 'dummy2', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'PUT', + endpoint: 'https://api.emarsys.net/api/v2/contact/?create_if_not_exists=1', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 4, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '3', + external_ids: ['abc@gmail.com', 'abc2@gmail.com'], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/contactlist/dummy/add', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 5, + }, + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 6, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + { + batchedRequest: { + body: { + JSON: { + key_id: '3', + external_id: 'abc@gmail.com', + data: { + section_group1: [ + { + section_variable1: 'some_value', + section_variable2: 'another_value', + }, + { + section_variable1: 'yet_another_value', + section_variable2: 'one_more_value', + }, + ], + global: { + global_variable1: 'global_value', + global_variable2: 'another_global_value', + }, + }, + attachment: [ + { + filename: 'example.pdf', + data: 'ZXhhbXBsZQo=', + }, + ], + event_time: '2023-07-06T11:59:02.402+05:30', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.emarsys.net/api/v2/event/purchase/trigger', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-WSSE': + 'UsernameToken Username="dummy", PasswordDigest="NDc5MjNlODIyMGE4ODhiMTQyNTA0OGMzZTFjZTM1MmMzMmU0NmNiNw==", Nonce="5398e214ae99c2e50afb709a3bc423f9", Created="2019-10-14T00:00:00.000Z"', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 8, + }, + ], + batched: false, + statusCode: 200, + destination: commonDestination, + }, + ], + }, + }, + }, + }, +].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/facebook_conversions/processor/data.ts b/test/integrations/destinations/facebook_conversions/processor/data.ts index 6eb90942a7..3224a15771 100644 --- a/test/integrations/destinations/facebook_conversions/processor/data.ts +++ b/test/integrations/destinations/facebook_conversions/processor/data.ts @@ -94,7 +94,7 @@ export const data = [ body: [ { error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', + 'Events must be sent within 7 days of their occurrence or up to one minute in the future.', statusCode: 400, statTags: { destType: 'FACEBOOK_CONVERSIONS', @@ -1534,4 +1534,125 @@ export const data = [ }, mockFns: defaultMockFns, }, + { + name: 'facebook_conversions', + description: 'Track event with standard event product added with content_ids', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + channel: 'web', + context: { + device: { + id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + manufacturer: 'Xiaomi', + model: 'Redmi 6', + name: 'xiaomi', + }, + network: { + carrier: 'Banglalink', + }, + os: { + name: 'android', + version: '8.1.0', + }, + screen: { + height: '100', + density: 50, + }, + traits: { + email: ' aBc@gmail.com ', + address: { + zip: 1234, + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'product added', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + revenue: 400, + additional_bet_index: 0, + id: '452345234', + quantity: 5, + }, + timestamp: '2023-11-12T15:46:51.693229+05:30', + type: 'track', + }, + destination: { + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + datasetId: 'dummyID', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + removeExternalId: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + actionSource: 'website', + }, + Enabled: true, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyID/events?access_token=09876', + headers: {}, + params: {}, + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: { + data: [ + '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"AddToCart","event_time":1699784211,"action_source":"website","custom_data":{"revenue":400,"additional_bet_index":0,"id":"452345234","quantity":5,"content_ids":"452345234","contents":[{"id":"452345234","quantity":5}],"content_type":"product","currency":"USD","value":400}}', + ], + }, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, ]; diff --git a/test/integrations/destinations/facebook_offline_conversions/processor/data.ts b/test/integrations/destinations/facebook_offline_conversions/processor/data.ts index 26d9a5e2f9..90709b67a2 100644 --- a/test/integrations/destinations/facebook_offline_conversions/processor/data.ts +++ b/test/integrations/destinations/facebook_offline_conversions/processor/data.ts @@ -1436,7 +1436,7 @@ export const data = [ traits: { email: 'test@rudderstack.com', birthday: '2005-01-01T23:28:56.782Z', - firstName: 'test', + firstName: ' test', name: 'test rudderlabs', address: { city: 'kalkata', diff --git a/test/integrations/destinations/facebook_pixel/dataDelivery/business.ts b/test/integrations/destinations/facebook_pixel/dataDelivery/business.ts index 9ac709978d..1b425ac5fa 100644 --- a/test/integrations/destinations/facebook_pixel/dataDelivery/business.ts +++ b/test/integrations/destinations/facebook_pixel/dataDelivery/business.ts @@ -241,11 +241,11 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ body: { output: { status: 400, - message: 'Invalid Parameter', + message: 'Unsupported post request. some problem with sent parameters', statTags, response: [ { - error: 'Invalid Parameter', + error: 'Unsupported post request. some problem with sent parameters', statusCode: 400, metadata: generateMetadata(1), }, diff --git a/test/integrations/destinations/facebook_pixel/dataDelivery/data.ts b/test/integrations/destinations/facebook_pixel/dataDelivery/data.ts index dcc633e1a8..e66e98bbe3 100644 --- a/test/integrations/destinations/facebook_pixel/dataDelivery/data.ts +++ b/test/integrations/destinations/facebook_pixel/dataDelivery/data.ts @@ -362,7 +362,7 @@ export const v0TestData = [ body: { output: { status: 400, - message: 'Invalid Parameter', + message: 'Unsupported post request. some problem with sent parameters', destinationResponse: { error: { message: 'Unsupported post request. some problem with sent parameters', diff --git a/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts b/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts new file mode 100644 index 0000000000..5a7beb4174 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/configLevelFeaturesTestData.ts @@ -0,0 +1,766 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { + overrideDestination, + generateTrackPayload, + generateMetadata, + transformResultBuilder, +} from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + limitedDataUSage: true, + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: 'ABC Started', + to: 'InitiateCheckout', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + categoryToContent: [ + { + from: 'clothing', + to: 'newClothing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +// the below object has properties that are used as whitelist and blacklist properties in below test cases +const piiPropertiesForAllowDeny = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', + firstName: 'John', + lastName: 'Doe', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonPropertiesWithoutProductArray = { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonTimestamp = new Date('2023-10-14'); + +export const configLevelFeaturesTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-config-test-1', + name: 'facebook_pixel', + description: + 'config feature : limitedDataUSage switched on. Ref:https://developers.facebook.com/docs/marketing-apis/data-processing-options/#supported-tools-and-apis ', + scenario: 'configuration', + successCriteria: 'Response should contain limitedDataUSage related fields', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + dataProcessingOptions: ['val1', 'val2', 'val3'], + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + data_processing_options: 'val1', + data_processing_options_country: 'val2', + data_processing_options_state: 'val3', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-2', + name: 'facebook_pixel', + scenario: 'configuration', + description: + 'config feature : While categoryToContent mapping is filled up in UI, but category is passed with message.properties as well. message.properties.category should be given priority over categoryToContent mapping', + successCriteria: 'Response should contain category mapped to newClothing', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, category: 'clothing' }, + context: { + traits: commonUserTraits, + dataProcessingOptions: ['val1', 'val2', 'val3'], + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + data_processing_options: 'val1', + data_processing_options_country: 'val2', + data_processing_options_state: 'val3', + custom_data: { + category: 'clothing', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['clothing'], + content_type: 'newClothing', + contents: [ + { + id: 'clothing', + quantity: 1, + }, + ], + content_category: 'clothing', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-3', + name: 'facebook_pixel', + description: + 'config feature : ContentCategoryMapping table is filled up, and category is passed with properties along with contentType via integrations object', + scenario: 'configuration', + successCriteria: 'contentType should be used from integrations object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, category: 'clothing' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + contentType: 'newClothingFromIntegrationObject', + }, + }, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'clothing', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['clothing'], + content_type: 'newClothingFromIntegrationObject', + contents: [ + { + id: 'clothing', + quantity: 1, + }, + ], + content_category: 'clothing', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-4', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties with marked hashed within integrations object, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should not be hashed and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + }, + { + blacklistPiiProperties: 'blacklistProp3', + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-5', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties without marked hashed within integrations object but marked hashed true from UI, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should be hashed and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + blacklistPiiHash: true, + }, + { + blacklistPiiProperties: 'blacklistProp3', + blacklistPiiHash: true, + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: + '528e5290f8ff0eb0325f0472b9c1a9ef4fac0b02ff6094b64d9382af4a10444b', + blacklistProp3: + 'bac8d4414984861d5199b7a97699c728bee36c4084299b2ca905434cf65d8944', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-6', + name: 'facebook_pixel', + description: + 'config feature : Config mapped whiteList and blackListed properties marked hashed within integrations object but marked hashed true from UI, along with default pii property email in the properties', + scenario: 'configuration', + successCriteria: + 'BlackListed properties should not be hashed again and default pii property should be deleted from the properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'whitelistProp1', + }, + ], + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'blacklistProp2', + blacklistPiiHash: true, + }, + { + blacklistPiiProperties: 'blacklistProp3', + blacklistPiiHash: true, + }, + ], + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-config-test-7', + name: 'facebook_pixel', + description: + 'properties.content_type is given priority over populating it from categoryToContent mapping.', + scenario: 'configuration', + successCriteria: 'Response should contain content_type as product_group and not newClothing', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...piiPropertiesForAllowDeny, content_type: 'product_group' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + integrations: { + FacebookPixel: { + hashed: true, + }, + }, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: 'default-user-id', + em: 'abc@gmail.com', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + whitelistProp1: 'val1', + blacklistProp2: 'val2', + blacklistProp3: 'val3', + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_type: 'product_group', + content_ids: ['dummy'], + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/data.ts b/test/integrations/destinations/facebook_pixel/processor/data.ts index f6a5cd1e20..6af7e3cd9b 100644 --- a/test/integrations/destinations/facebook_pixel/processor/data.ts +++ b/test/integrations/destinations/facebook_pixel/processor/data.ts @@ -1,4 +1,9 @@ -import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { validationTestData } from './validationTestData'; +import { pageScreenTestData } from './pageScreenTestData'; +import { ecommTestData } from './ecommTestData'; +import { configLevelFeaturesTestData } from './configLevelFeaturesTestData'; export const mockFns = (_) => { // @ts-ignore @@ -6,6565 +11,10 @@ export const mockFns = (_) => { }; export const data = [ - { - name: 'facebook_pixel', - description: 'Test 0', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 1', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'group', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Message type group not supported', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 2', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Test', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'For identify events, "Advanced Mapping" configuration must be enabled on the RudderStack dashboard', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 3', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Rudder Test', - email: 'abc@gmail.com', - firstname: 'Rudder', - lastname: 'Test', - phone: 9000000000, - gender: 'female', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","ph":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","ge":"252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111","ln":"532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25","fn":"2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"identify","event_time":1697278611,"event_id":"84e26acc-56a5-4835-8233-591137fca468","action_source":"website"}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 4', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - name: 'Rudder Test', - email: 'abc@gmail.com', - phone: 9000000000, - address: { - postalCode: 1234, - }, - gender: 'female', - }, - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - properties: { - plan: 'standard plan', - name: 'rudder test', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","ph":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","ge":"252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36","fn":"2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747","ln":"532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25"},"event_name":"identify","event_time":1697278611,"event_id":"84e26acc-56a5-4835-8233-591137fca468","action_source":"website"}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 5', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 6', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 7', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - phone: 9000000000, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 8', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - email: 'abc@gmail.com', - phone: 9000000000, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"email":"abc@gmail.com","phone":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 9', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - timestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Viewed page ApplicationLoaded","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 10', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Viewed a page","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 11', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - phone: 9000000000, - email: 'abc@gmail.com', - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"phone":"593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579","email":"abc@gmail.com","category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","testDimension":true,"testMetric":true,"content_ids":["cat 1"],"content_type":"product_group","contents":[{"id":"cat 1","quantity":1}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 12', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - phone: 9000000000, - email: 'abc@gmail.com', - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","testDimension":true,"testMetric":true,"content_ids":["cat 1"],"content_type":"product_group","contents":[{"id":"cat 1","quantity":1}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 13', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - contentName: 'nutrition', - value: 18.9, - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","contentName":"nutrition","value":18.9,"filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true,"content_ids":["507f1f77bcf86cd799439011","507f1f77bcf86cdef799439011"],"content_type":"product","contents":[{"id":"507f1f77bcf86cd799439011","quantity":2},{"id":"507f1f77bcf86cdef799439011","quantity":2}],"content_category":"cat 1","content_name":"nutrition","currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 14', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'my product list', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - removeExternalId: false, - eventsToEvents: [ - { - from: 'My product list', - to: 'ViewContent', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'list_id', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true,"content_ids":["507f1f77bcf86cd799439011","507f1f77bcf86cdef799439011"],"content_type":"product","contents":[{"id":"507f1f77bcf86cd799439011","quantity":2},{"id":"507f1f77bcf86cdef799439011","quantity":2}],"content_category":"cat 1","value":0,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 15', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"my product 1","content_category":"clothing","value":24.75,"contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 16', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: 24.75, - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - removeExternalId: false, - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":24.75,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 17', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - contentName: 'all about nutrition', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","contentName":"all about nutrition","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2,"content_name":"all about nutrition"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 18', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - contentName: 'abc', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'contentName', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"InitiateCheckout","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","contentName":"abc","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 19', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - dataProcessingOptions: [['LDU'], 1, 1000], - fbc: 'fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890', - fbp: 'fb.1.1554763741205.234567890', - fb_login_id: 'fb_id', - lead_id: 'lead_id', - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUSage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","fbc":"fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890","fbp":"fb.1.1554763741205.234567890","lead_id":"lead_id","fb_login_id":"fb_id"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","data_processing_options":["LDU"],"data_processing_options_country":1,"data_processing_options_state":1000,"custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 20', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - dataProcessingOptions: [['LDU'], 1, 1000], - fbc: 'fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890', - fbp: 'fb.1.1554763741205.234567890', - fb_login_id: 'fb_id', - lead_id: 'lead_id', - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUSage: false, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","fbc":"fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890","fbp":"fb.1.1554763741205.234567890","lead_id":"lead_id","fb_login_id":"fb_id"},"event_name":"spin_result","event_time":1697278611,"action_source":"other","custom_data":{"additional_bet_index":0,"value":400}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 21', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - product_id: 'p-298', - quantity: 2, - price: 18.9, - category: 'health', - value: 18.9, - query: 'HDMI cable', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"product_id":"p-298","quantity":2,"price":18.9,"category":"health","value":18.9,"query":"HDMI cable","content_ids":["p-298"],"content_category":"health","contents":[{"id":"p-298","quantity":2,"item_price":18.9}],"search_string":"HDMI cable","currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 22', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: 'HDMI cable', - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - testDestination: true, - testEventCode: 'TEST1001', - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"query":"HDMI cable","content_ids":[],"content_category":"","value":0,"contents":[],"search_string":"HDMI cable","currency":"USD"}}', - ], - test_event_code: 'TEST1001', - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 23', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - standardPageCall: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: 'phone', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'url', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: 'email', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_ip_address":"0.0.0.0","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"PageView","event_time":1697278611,"event_source_url":"jkl","event_id":"5e10d13a-bf9a-44bf-b884-43a9e591ea71","action_source":"website","custom_data":{"path":"/abc","referrer":"xyz","search":"def","title":"ghi","url":"jkl"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 24', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'track page', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'track page', - to: 'PageView', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'additional_bet_index', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"PageView","event_time":1697278611,"action_source":"other","custom_data":{"revenue":400,"additional_bet_index":0}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 25', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'my product list', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - filters: [ - { - type: 'department', - value: 'beauty', - }, - { - type: 'price', - value: 'under', - }, - ], - sorts: [ - { - type: 'price', - value: 'desc', - }, - ], - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'My product list', - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: 'list_id', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Schedule","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"quantity":2,"category":"cat 1","list_id":"1234","filters[0].type":"department","filters[0].value":"beauty","filters[1].type":"price","filters[1].value":"under","sorts[0].type":"price","sorts[0].value":"desc","products[0].product_id":"507f1f77bcf86cd799439011","products[0].productDimension":"My Product Dimension","products[0].productMetric":"My Product Metric","products[0].position":10,"products[1].product_id":"507f1f77bcf86cdef799439011","products[1].productDimension":"My Product Dimension1","products[1].productMetric":"My Product Metric1","products[1].position":-10,"testDimension":true,"testMetric":true}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 26', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'event' is required", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 27', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: '35.753', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":35.75,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 28', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: '35.7A3', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"AddToCart","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"value":35.7,"category":"cat 1","id":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"","content_category":"cat 1","contents":[{"id":"p-298","quantity":1}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 29', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product added', - properties: { - currency: 'CAD', - quantity: 1, - value: 'ABC', - category: 'cat 1', - id: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Revenue could not be converted to number', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 30', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 31', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: 100, - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category":100,"order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"100","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 32', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: { - category1: '1', - }, - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.category' must be either be a string or an array", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 33', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2023-10-14T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'spin_result', - to: 'Schedule', - }, - { - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"6dc8118ec743f5f3b758939714193f547f4a674c68757fa80d7c9564dc093b0a","em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"Schedule","event_time":1697278611,"action_source":"other","custom_data":{"revenue":400,"additional_bet_index":0}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 34', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - timestamp: '2019-08-24T15:46:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: 'spin_result', - to: 'Schedule', - }, - { - to: 'Schedule', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 35', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: 'abc@gmail.com', - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - }, - originalTimestamp: '2019-04-16T15:50:51.693229+05:30', - type: 'track', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: 'validToken', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: true, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Events must be sent within seven days of their occurrence or up to one minute in the future.', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 36', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: { - key1: 'HDMI cable', - }, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'query' should be in string format only", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 37', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'products searched', - properties: { - query: 50, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Search","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"query":50,"content_ids":[],"content_category":"","value":0,"contents":[],"search_string":50,"currency":"USD"}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 38', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36","fbc":"fb.1.1697278611693.IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI"},"event_name":"InitiateCheckout","event_time":1697278611,"event_source_url":"https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI","event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 39', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'https://theminimstory.com/collections/summer-of-pearls?utm_source=facebook&utm_medium=paidsocial&utm_campaign=carousel&utm_content=ad1-jul&fbclid=IwAR2SsDcjzd_TLZN-e93kxOeGBYO4pQ3AiyeXSheHW5emDeLw8uTvo6lTMPI', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: { - name: 'checkout started', - }, - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'event name should be string', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 40', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - page: { - url: 'url in wrong format', - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'checkout started', - properties: { - currency: 'CAD', - category: 'clothing', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 1, - price: 24.75, - name: 'my product 2', - sku: 'p-299', - }, - ], - step: 1, - paymentMethod: 'Visa', - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"InitiateCheckout","event_time":1697278611,"event_source_url":"url in wrong format","event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","category":"clothing","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":1,"products[1].price":24.75,"products[1].name":"my product 2","products[1].sku":"p-299","step":1,"paymentMethod":"Visa","testDimension":true,"testMetric":true,"content_category":"clothing","content_ids":["p-298","p-299"],"content_type":"product","value":0,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":1,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 41', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - Facebook_Pixel: { - contentType: 'sending dedicated content type for this particular payload', - }, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"sending dedicated content type for this particular payload","content_name":"my product 1","content_category":"clothing","value":24.75,"contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 42', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product viewed', - properties: { - currency: 'CAD', - quantity: 1, - price: 24.75, - value: 18.9, - name: 'my product 1', - category: 'clothing', - sku: 'p-298', - testDimension: true, - testMetric: true, - position: 4.5, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - removeExternalId: true, - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.value', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"ViewContent","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"currency":"CAD","quantity":1,"price":24.75,"value":18.9,"name":"my product 1","category":"clothing","sku":"p-298","testDimension":true,"testMetric":true,"position":4.5,"content_ids":["p-298"],"content_type":"product","content_name":"my product 1","content_category":"clothing","contents":[{"id":"p-298","quantity":1,"item_price":24.75}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 43', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - contentName: 'all about nutrition', - products: { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.products' is not sent as an Array", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 44', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'product list viewed', - properties: { - email: 'abc@gmail.com', - quantity: 2, - category: 'cat 1', - list_id: '1234', - contentName: 'nutrition', - value: 18.9, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - productDimension: 'My Product Dimension', - productMetric: 'My Product Metric', - position: 10, - }, - [ - { - product_id: '507f1f77bcf86cdef799439011', - productDimension: 'My Product Dimension1', - productMetric: 'My Product Metric1', - position: -10, - }, - ], - ], - testDimension: true, - testMetric: true, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: "'properties.products[1]' is not an object", - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 45', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'custom', - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - pixelId: 'dummyPixelId', - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Access token not found. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 46', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'custom', - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: 'Pixel Id not found. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'FACEBOOK_PIXEL', - module: 'destination', - implementation: 'native', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 47', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - metadata: { - jobId: 12, - }, - destination: { - secretConfig: {}, - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [], - limitedDataUSage: false, - accessToken: 'dummyAccessToken', - testDestination: false, - testEventCode: '', - standardPageCall: false, - blacklistedEvents: [], - whitelistedEvents: [], - eventFilteringOption: 'disable', - removeExternalId: false, - useUpdatedMapping: false, - oneTrustCookieCategories: [], - useNativeSDK: false, - eventDelivery: false, - eventDeliveryTS: 1686748039135, - }, - liveEventsConfig: { - eventDelivery: false, - eventDeliveryTS: 1686748039135, - }, - id: 'destId1', - workspaceId: 'wsp2', - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - name: 'san-fb_pixel', - enabled: true, - deleted: false, - createdAt: '2023-06-06T13:36:08.579Z', - updatedAt: '2023-06-14T13:07:19.136Z', - revisionId: 'revId2', - secretVersion: 3, - }, - message: { - type: 'page', - sentAt: '2023-10-14T15:46:51.000Z', - userId: 'user@19', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - version: 'dev-snapshot', - namespace: 'com.rudderlabs.javascript', - }, - page: { - url: 'http://127.0.0.1:8888/', - path: '/', - title: 'Document', - search: '', - tab_url: 'http://127.0.0.1:8888/', - referrer: 'http://127.0.0.1:8888/', - initial_referrer: '$direct', - referring_domain: '127.0.0.1:8888', - initial_referring_domain: '', - }, - locale: 'en-GB', - screen: { - width: 1728, - height: 1117, - density: 2, - innerWidth: 547, - innerHeight: 915, - }, - traits: { - name: false, - source: 'rudderstack', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: 'dev-snapshot', - }, - campaign: {}, - sessionId: 1687769234506, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', - }, - rudderId: '6bbfd003-c074-4ee9-8674-c132ded9ff04', - timestamp: '2023-10-14T15:46:51.000Z', - properties: { - url: 'http://127.0.0.1:8888/', - path: '/', - title: 'Document', - search: '', - tab_url: 'http://127.0.0.1:8888/', - referrer: 'http://127.0.0.1:8888/', - initial_referrer: '$direct', - referring_domain: '127.0.0.1:8888', - initial_referring_domain: '', - }, - receivedAt: '2023-10-14T15:46:51.000Z', - request_ip: '49.206.54.243', - anonymousId: '700ab220-faad-4cdf-8484-63e4c6bce6fe', - integrations: { - All: true, - }, - originalTimestamp: '2023-10-14T15:46:51.000Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=dummyAccessToken`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"72fd46c9ecb386f6747664a3e1d524294a3d7a2c8ae4aeb22b1e578b75093635","client_ip_address":"49.206.54.243","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"},"event_name":"PageView","event_time":1697298411,"event_source_url":"http://127.0.0.1:8888/","action_source":"website","custom_data":{"url":"http://127.0.0.1:8888/","path":"/","title":"Document","search":"","tab_url":"http://127.0.0.1:8888/","referrer":"http://127.0.0.1:8888/","initial_referrer":"$direct","referring_domain":"127.0.0.1:8888","initial_referring_domain":""}}', - ], - }, - }, - files: {}, - userId: '', - }, - metadata: { - jobId: 12, - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 48', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'test@rudderstack.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - total: 99.99, - revenue: 12.24, - shipping: 13.99, - tax: 20.99, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - delivery_category: 'home_delivery', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - delivery_category: 'home_delivery', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5","em":"1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd","client_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","total":99.99,"revenue":12.24,"shipping":13.99,"tax":20.99,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[0].delivery_category":"home_delivery","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","products[1].delivery_category":"home_delivery","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"content_type":"product","value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75,"delivery_category":"home_delivery"},{"id":"p-299","quantity":3,"item_price":24.75,"delivery_category":"home_delivery"}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 49', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - content_ids: ['prod1', 'prod2'], - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400,"content_ids":["prod1","prod2"]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: 'Test 50', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - channel: 'mobile', - destination_props: { - Fb: { - app_id: 'RudderFbApp', - }, - }, - context: { - device: { - id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', - manufacturer: 'Xiaomi', - model: 'Redmi 6', - name: 'xiaomi', - }, - network: { - carrier: 'Banglalink', - }, - os: { - name: 'android', - version: '8.1.0', - }, - screen: { - height: '100', - density: 50, - }, - traits: { - email: ' aBc@gmail.com ', - address: { - zip: 1234, - }, - anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - }, - }, - event: 'spin_result', - integrations: { - All: true, - }, - message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', - properties: { - revenue: 400, - additional_bet_index: 0, - contents: [ - { - id: 'prod1', - quantity: 5, - item_price: 55, - }, - ], - }, - timestamp: '2023-10-14T00:00:00.693229+05:30', - type: 'track', - }, - destination: { - Config: { - limitedDataUsage: true, - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: false, - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - eventCustomProperties: [ - { - eventCustomProperties: '', - }, - ], - removeExternalId: true, - valueFieldIdentifier: '', - advancedMapping: false, - whitelistPiiProperties: [ - { - whitelistPiiProperties: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08","zp":"03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4"},"event_name":"spin_result","event_time":1697221800,"action_source":"app","custom_data":{"additional_bet_index":0,"value":400,"contents":[{"id":"prod1","quantity":5,"item_price":55}]}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'facebook_pixel', - description: - 'Test 51: properties.content_type is given priority over populating it from categoryToContent mapping.', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2023-10-14T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'order completed', - properties: { - content_type: 'product_group', - category: ['clothing', 'fishing'], - order_id: 'rudderstackorder1', - revenue: 12.24, - currency: 'INR', - products: [ - { - quantity: 1, - price: 24.75, - name: 'my product', - sku: 'p-298', - }, - { - quantity: 3, - price: 24.75, - name: 'other product', - sku: 'p-299', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - blacklistPiiProperties: [ - { - blacklistPiiProperties: '', - blacklistPiiHash: true, - }, - ], - categoryToContent: [ - { - from: 'clothing', - to: 'product', - }, - ], - accessToken: '09876', - pixelId: 'dummyPixelId', - eventsToEvents: [ - { - from: '', - to: '', - }, - ], - valueFieldIdentifier: 'properties.price', - advancedMapping: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, - headers: {}, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - data: [ - '{"user_data":{"external_id":"5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5"},"event_name":"Purchase","event_time":1697278611,"event_id":"ec5481b6-a926-4d2e-b293-0b3a77c4d3be","action_source":"website","custom_data":{"content_type":"product_group","category[0]":"clothing","category[1]":"fishing","order_id":"rudderstackorder1","revenue":12.24,"currency":"INR","products[0].quantity":1,"products[0].price":24.75,"products[0].name":"my product","products[0].sku":"p-298","products[1].quantity":3,"products[1].price":24.75,"products[1].name":"other product","products[1].sku":"p-299","content_category":"clothing,fishing","content_ids":["p-298","p-299"],"value":12.24,"contents":[{"id":"p-298","quantity":1,"item_price":24.75},{"id":"p-299","quantity":3,"item_price":24.75}],"num_items":2}}', - ], - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...validationTestData, + ...pageScreenTestData, + ...ecommTestData, + ...configLevelFeaturesTestData, ].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts b/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts new file mode 100644 index 0000000000..5d429d297d --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/ecommTestData.ts @@ -0,0 +1,1120 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { generateTrackPayload, generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: 'ABC Started', + to: 'InitiateCheckout', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonPropertiesWithoutProductArray = { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', +}; + +const commonPropertiesWithProductArray = { + products: [ + { + product_id: '017c6f5d5cf86a4b22432066', + sku: '8732-98', + name: 'Just Another Game', + price: 22, + position: 2, + category: 'Games and Entertainment', + url: 'https://www.myecommercewebsite.com/product', + image_url: 'https://www.myecommercewebsite.com/product/path.jpg', + }, + { + product_id: '89ac6f5d5cf86a4b64eac145', + sku: '1267-01', + name: 'Wrestling Trump Cards', + price: 4, + position: 21, + category: 'Card Games', + }, + ], + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', +}; +const commonTimestamp = new Date('2023-10-14'); +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const ecommTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-ecomm-test-1', + name: 'facebook_pixel', + description: + 'Track call : product list viewed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + content_ids: ['dummy'], + content_type: 'product_group', + contents: [ + { + id: 'dummy', + quantity: 1, + }, + ], + content_category: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-2', + name: 'facebook_pixel', + description: 'Track call : product list viewed event call with properties with product array', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and from products array and should be sent to the destination', + scenario: 'ecommerce', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + content_category: 'dummy', + value: 0, + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-3', + name: 'facebook_pixel', + description: 'Track call : product viewed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to ViewContent, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product viewed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'ViewContent', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_ids: ['12345'], + content_type: 'product', + content_name: '', + content_category: 'dummy', + currency: 'USD', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-4', + name: 'facebook_pixel', + description: 'Track call : product added event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to AddToCart, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product added', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'AddToCart', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_ids: ['12345'], + content_type: 'product', + content_name: '', + content_category: 'dummy', + currency: 'USD', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-5', + name: 'facebook_pixel', + description: 'Track call : order completed event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to purchase, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'order completed', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Purchase', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_category: 'dummy', + content_ids: [], + content_type: 'product', + currency: 'USD', + contents: [], + num_items: 0, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-6', + name: 'facebook_pixel', + description: 'Track call : order completed event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to purchase, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'order completed', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Purchase', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-7', + name: 'facebook_pixel', + description: 'Track call : products searched event call with properties', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to Search, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'products searched', + properties: { ...commonPropertiesWithoutProductArray, query: 'dummy' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'Search', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 100, + product_id: '12345', + query: 'dummy', + content_ids: ['12345'], + content_category: 'dummy', + contents: [ + { + id: '12345', + quantity: 10, + }, + ], + search_string: 'dummy', + currency: 'USD', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-8', + name: 'facebook_pixel', + description: + 'Track call : products searched event call with properties and unsupported query type', + scenario: 'ecommerce', + successCriteria: + 'Error : It should throw an error as the query is not a string or an array of strings', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'products searched', + properties: { ...commonPropertiesWithoutProductArray, query: ['dummy'] }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: "'query' should be in string format only", + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-9', + name: 'facebook_pixel', + description: 'Track call : checkout started event call with properties without product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'checkout started', + properties: commonPropertiesWithoutProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + category: 'dummy', + quantity: 10, + value: 0, + product_id: '12345', + content_category: 'dummy', + content_ids: [], + content_type: 'product', + currency: 'USD', + contents: [], + num_items: 0, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-10', + name: 'facebook_pixel', + description: 'Track call : checkout started event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'checkout started', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-11', + name: 'facebook_pixel', + description: + 'Track call : custom event ABC Started event call with properties with product array', + scenario: 'ecommerce', + successCriteria: + 'It should be internally mapped to InitiateCheckout, with necessary mapping from message.properties and should be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'ABC Started', + properties: commonPropertiesWithProductArray, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'InitiateCheckout', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + 'products[0].product_id': '017c6f5d5cf86a4b22432066', + 'products[0].sku': '8732-98', + 'products[0].name': 'Just Another Game', + 'products[0].price': 22, + 'products[0].position': 2, + 'products[0].category': 'Games and Entertainment', + 'products[0].url': 'https://www.myecommercewebsite.com/product', + 'products[0].image_url': + 'https://www.myecommercewebsite.com/product/path.jpg', + 'products[1].product_id': '89ac6f5d5cf86a4b64eac145', + 'products[1].sku': '1267-01', + 'products[1].name': 'Wrestling Trump Cards', + 'products[1].price': 4, + 'products[1].position': 21, + 'products[1].category': 'Card Games', + category: 'dummy', + quantity: 10, + revenue: 100, + price: 50, + product_id: '12345', + order_id: '23456', + content_category: 'dummy', + content_ids: ['017c6f5d5cf86a4b22432066', '89ac6f5d5cf86a4b64eac145'], + content_type: 'product', + currency: 'USD', + value: 100, + contents: [ + { + id: '017c6f5d5cf86a4b22432066', + quantity: 10, + item_price: 22, + }, + { + id: '89ac6f5d5cf86a4b64eac145', + quantity: 10, + item_price: 4, + }, + ], + num_items: 2, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-ecomm-test-12', + name: 'facebook_pixel', + description: + 'Track call : product list viewed event call with properties without product array and revenue as string', + scenario: 'ecommerce', + successCriteria: + 'Error : It should throw an error as revenue is not a number and should not be sent to the destination', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'product list viewed', + properties: { ...commonPropertiesWithoutProductArray, value: '$20' }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Revenue could not be converted to number', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts b/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts new file mode 100644 index 0000000000..d315b03cea --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/identifyTestData.ts @@ -0,0 +1,209 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { Destination } from '../../../../../src/types'; +import { generateMetadata, transformResultBuilder, overrideDestination } from '../../../testUtils'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }, + Enabled: true, +}; +const commonMessage = { + channel: 'web', + context: { + traits: { + name: 'Rudder Test', + email: 'abc@gmail.com', + firstname: 'Rudder', + lastname: 'Test', + phone: 9000000000, + gender: 'female', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + properties: { + plan: 'standard plan', + name: 'rudder test', + }, + type: 'identify', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + originalTimestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '123456', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', +}; +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'configuration', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const identifyTestData: ProcessorTestData[] = [ + { + id: 'fbPixel-identify-test-1', + name: 'facebook_pixel', + description: '[Error]: Check if advancedMapping configuration is enabled', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, we are sending identify event with advancedMapping disabled', + module: 'destination', + feature: 'processor', + version: 'v0', + input: { + request: { + body: [ + { + message: commonMessage, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { advancedMapping: false }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'For identify events, "Advanced Mapping" configuration must be enabled on the RudderStack dashboard', + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'fbPixel-identify-test-2', + name: 'facebook_pixel', + description: 'Identify event happy flow : without integrations object hashed true', + scenario: 'Business', + successCriteria: + ' Response should contain status code 200 and body should contain unhashed user traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: commonMessage, + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + ph: '593a6d58f34eb5c3de4f47e38d1faaa7d389fafe332a85400b1e54498391c579', + ge: '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + ln: '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', + fn: '2c2ccf28d806f6f9a34b67aa874d2113b7ac1444f1a4092541b8b75b84771747', + client_ip_address: '0.0.0.0', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + }, + event_name: 'identify', + event_time: 1697278611, + event_id: '84e26acc-56a5-4835-8233-591137fca468', + action_source: 'website', + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts b/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts new file mode 100644 index 0000000000..dee772522a --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/pageScreenTestData.ts @@ -0,0 +1,721 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { + generateSimplifiedPageOrScreenPayload, + overrideDestination, + generateMetadata, + transformResultBuilder, +} from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: 'phone', + blacklistPiiHash: true, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; +const commonMessage = { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + }, + type: 'page', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + timestamp: '2023-10-14T15:46:51.693229+05:30', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', +}; + +const commonPageMessage = { ...commonMessage, type: 'page' }; + +const commonScreenMessage = { ...commonMessage, type: 'screen' }; + +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const pageScreenTestData: ProcessorTestData[] = [ + { + id: 'facebook_pixel-page-test-1', + name: 'facebook_pixel', + description: + 'Page call : Happy flow without standard page switched on and with name and properties', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + name: 'ApplicationLoaded', + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + em: '9eceb13483d7f187ec014fd6d4854d1420cfc634328af85f51d0323ba8622e21', + }, + event_name: 'Viewed page ApplicationLoaded', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-2', + name: 'facebook_pixel', + description: 'Page call : with standard page switched on and no properties and no name', + scenario: 'Page', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending any other properties other than standard page properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: {}, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "After excluding opt_out,event_id,action_source, no fields are present in 'properties' for a standard event", + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-3', + name: 'facebook_pixel', + description: 'Page call : with standard page switched on and properties', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + em: '9eceb13483d7f187ec014fd6d4854d1420cfc634328af85f51d0323ba8622e21', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-page-test-4', + name: 'facebook_pixel', + description: 'Page call : with standard page switched off and with properties but no page name', + scenario: 'Page', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'page', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-1', + name: 'facebook_pixel', + description: + 'Screen call : Happy flow without standard page switched on and with name and properties', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + name: 'ApplicationLoaded', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-2', + name: 'facebook_pixel', + description: 'Screen call : with standard page switched on and no properties and no name', + scenario: 'Screen', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending any other properties other than standard page properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: {}, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "After excluding opt_out,event_id,action_source, no fields are present in 'properties' for a standard event", + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-3', + name: 'facebook_pixel', + description: 'Screen call : with standard page switched on and properties', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { standardPageCall: true }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'facebook_pixel-screen-test-4', + name: 'facebook_pixel', + description: + 'Screen call : with standard page switched off and with properties but no page name', + scenario: 'Screen', + successCriteria: 'Response should contain status code 200 and no error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: {}, + }, + properties: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-10-14T15:32:56.409Z', + }, + 'screen', + ), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '470582f368e5aeec2cf487decd1e125b7d265e8b0b06b74a25e999e93bfb699f', + }, + event_name: 'PageView', + event_time: 1697297576, + event_source_url: 'jkl', + action_source: 'website', + custom_data: { + path: '/abc', + referrer: 'xyz', + search: 'def', + title: 'ghi', + url: 'jkl', + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts b/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts new file mode 100644 index 0000000000..9fd65945c4 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/trackTestData.ts @@ -0,0 +1,208 @@ +import { VERSION } from '../../../../../src/v0/destinations/facebook_pixel/config'; +import { generateMetadata, generateTrackPayload, transformResultBuilder } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [ + { + whitelistPiiProperties: 'email', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + revenue: 400, + additional_bet_index: 0, + email: 'abc@gmail.com', +}; + +const commonTimestamp = new Date('2023-10-14'); + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'fbPixel-track-test-1', + name: 'facebook_pixel', + description: 'Track call : custom event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'spin_result', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + additional_bet_index: 0, + email: 'abc@gmail.com', + value: 400, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'fbPixel-track-test-2', + name: 'facebook_pixel', + description: + 'Track call : other standard type event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping but falls under other standard events, should be considered as a simple track event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'AddToWishlist', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://graph.facebook.com/${VERSION}/dummyPixelId/events?access_token=09876`, + headers: {}, + params: {}, + FORM: { + data: [ + JSON.stringify({ + user_data: { + external_id: + '3ffc8a075f330402d82aa0a86c596b0d2fe70df38b22c5be579f86a18e4aca47', + em: '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + client_user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + event_name: 'AddToWishlist', + event_time: 1697241600, + event_id: '12345', + action_source: 'website', + custom_data: { + additional_bet_index: 0, + email: 'abc@gmail.com', + value: 400, + }, + }), + ], + }, + files: {}, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts b/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts new file mode 100644 index 0000000000..a0f85e45e3 --- /dev/null +++ b/test/integrations/destinations/facebook_pixel/processor/validationTestData.ts @@ -0,0 +1,461 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + generateSimplifiedGroupPayload, + generateSimplifiedTrackPayload, + generateTrackPayload, + overrideDestination, +} from '../../../testUtils'; +const commonTimestamp = new Date('2023-10-12'); +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'facebook_pixel', + DisplayName: 'Facebook Pixel', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [ + { + blacklistPiiProperties: '', + blacklistPiiHash: false, + }, + ], + eventsToEvents: [ + { + from: '', + to: '', + }, + ], + eventCustomProperties: [ + { + eventCustomProperties: '', + }, + ], + removeExternalId: true, + valueFieldIdentifier: '', + advancedMapping: true, + whitelistPiiProperties: [ + { + whitelistPiiProperties: '', + }, + ], + }, + Enabled: true, +}; + +const commonStatTags = { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FACEBOOK_PIXEL', + module: 'destination', + implementation: 'native', + feature: 'processor', +}; + +export const validationTestData = [ + { + id: 'fbPixel-validation-test-1', + name: 'facebook_pixel', + description: '[Error]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by facebook pixel destination and the error message should be Event type random is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(commonDestination, { + accessToken: '09876', + pixelId: 'dummyPixelId', + }), + message: generateSimplifiedGroupPayload({ + userId: 'user123', + groupId: 'XUepkK', + traits: { + subscribe: true, + }, + context: { + traits: { + email: 'test@rudderstack.com', + phone: '+12 345 678 900', + consent: ['email'], + }, + }, + timestamp: '2023-10-14T00:21:34.208Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Message type group not supported', + statTags: commonStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-2', + name: 'facebook_pixel', + description: + 'Track call : error in instrumentation as pixel id is not mentioned in destination object', + scenario: 'Business', + successCriteria: + 'Error: Pixel Id not found. Aborting, as we are sending an event without pixel id and the status code should be 400', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + context: { + traits: { + email: 'abc@gmail.com', + }, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Pixel Id not found. Aborting', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + errorType: 'configuration', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-3', + name: 'facebook_pixel', + description: 'Track call : custom event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + context: { + traits: { + email: 'abc@gmail.com', + }, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Access token not found. Aborting', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + errorType: 'configuration', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-3', + name: 'facebook_pixel', + description: '[Error]: validate event date and time', + scenario: 'Framework + business', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending an event which is older than 7 days and the error message should be Events must be sent within seven days of their occurrence or up to one minute in the future.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + email: 'test@rudderstack.com', + phone: '9112340375', + plan_details: { + plan_type: 'gold', + duration: '3 months', + }, + }, + }, + properties: { + revenue: 400, + additional_bet_index: 0, + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + destination: overrideDestination(commonDestination, { + accessToken: '09876', + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Events must be sent within 7 days of their occurrence or up to one minute in the future.', + statTags: commonStatTags, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-4', + name: 'facebook_pixel', + description: + 'Track call : error in instrumentation as event name is not mentioned in track call', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + properties: { + revenue: 400, + additional_bet_index: 0, + }, + }, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: "'event' is required", + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-4', + name: 'facebook_pixel', + description: 'Track call : error in instrumentation as event name is not a string', + scenario: 'Business', + successCriteria: + 'Error message should be event name should be string and status code should be 400, as we are sending an event which is not a string', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 1234, + properties: { + revenue: 400, + additional_bet_index: 0, + }, + }, + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'event name should be string', + metadata: generateMetadata(1), + statTags: { + ...commonStatTags, + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'fbPixel-validation-test-5', + name: 'facebook_pixel', + description: '[Error]: validate event date and time', + scenario: 'Framework + business', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending an event which is older than 7 days and the error message should be Events must be sent within seven days of their occurrence or up to one minute in the future.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateSimplifiedTrackPayload({ + // Sun Oct 15 2023 + type: 'track', + event: 'TestEven001', + sentAt: '2023-08-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + action_source: 'physical_store', + email: 'test@rudderstack.com', + phone: '9112340375', + event_id: 'x9lk3gfte768o1oy08cyaylx5t2j9q2wwfl2', + plan_details: { + plan_type: 'gold', + duration: '3 months', + }, + }, + }, + properties: { + revenue: 400, + additional_bet_index: 0, + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2023-08-25T15:32:56.409Z', + }), + destination: overrideDestination(commonDestination, { + accessToken: '09876', + pixelId: 'dummyPixelId', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 200, + output: { + body: { + FORM: { + data: [ + JSON.stringify({ + user_data: { + em: '1c5e54849f5c711ce38fa60716fbbe44bff478f9ca250897b39cdfc2438cd1bd', + ph: '820c46baccd33a1664f583b4505a7e39e033197e06e0bd7c87109e33c57c5497', + }, + event_name: 'TestEven001', + event_time: 1692977576, + event_id: 'x9lk3gfte768o1oy08cyaylx5t2j9q2wwfl2', + action_source: 'physical_store', + custom_data: { additional_bet_index: 0, value: 400 }, + }), + ], + }, + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + }, + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + files: {}, + headers: {}, + method: 'POST', + params: {}, + type: 'REST', + userId: '', + version: '1', + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/facebook_pixel/router/data.ts b/test/integrations/destinations/facebook_pixel/router/data.ts index 4bd9914768..1d3b35b42a 100644 --- a/test/integrations/destinations/facebook_pixel/router/data.ts +++ b/test/integrations/destinations/facebook_pixel/router/data.ts @@ -8,7 +8,8 @@ export const mockFns = (_) => { export const data = [ { name: 'facebook_pixel', - description: 'Test 0', + description: + 'Test 0: General batch request with two events, one track and one identify event with advancedMapping set to false and true respectively', feature: 'router', module: 'destination', version: 'v0', @@ -201,4 +202,114 @@ export const data = [ }, }, }, + { + name: 'facebook_pixel', + description: + 'Test 1 : adTrackingEnabled is passed in context, advertiser_tracking_enabled set to true', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + destination_props: { Fb: { app_id: 'RudderFbApp' } }, + context: { + device: { + id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + manufacturer: 'Xiaomi', + model: 'Redmi 6', + name: 'xiaomi', + adTrackingEnabled: true, + }, + network: { carrier: 'Banglalink' }, + os: { name: 'android', version: '8.1.0' }, + screen: { height: '100', density: 50 }, + traits: { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { All: true }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { revenue: 400, additional_bet_index: 0 }, + timestamp: '2023-10-14T15:46:51.693229+05:30', + type: 'track', + }, + metadata: { jobId: 1, userId: 'u1' }, + destination: { + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [{ blacklistPiiProperties: '', blacklistPiiHash: false }], + removeExternalId: true, + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [{ from: '', to: '' }], + eventCustomProperties: [{ eventCustomProperties: '' }], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [{ whitelistPiiProperties: '' }], + }, + Enabled: true, + }, + }, + ], + destType: 'facebook_pixel', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/dummyPixelId/events?access_token=09876', + headers: {}, + params: {}, + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: { + data: [ + '{"user_data":{"em":"48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08"},"event_name":"spin_result","event_time":1697278611,"advertiser_tracking_enabled":true,"action_source":"other","custom_data":{"additional_bet_index":0,"value":400}}', + ], + }, + }, + files: {}, + }, + metadata: [{ jobId: 1, userId: 'u1' }], + batched: false, + statusCode: 200, + destination: { + Config: { + limitedDataUsage: true, + blacklistPiiProperties: [{ blacklistPiiProperties: '', blacklistPiiHash: false }], + removeExternalId: true, + accessToken: '09876', + pixelId: 'dummyPixelId', + eventsToEvents: [{ from: '', to: '' }], + eventCustomProperties: [{ eventCustomProperties: '' }], + valueFieldIdentifier: '', + advancedMapping: false, + whitelistPiiProperties: [{ whitelistPiiProperties: '' }], + }, + Enabled: true, + }, + }, + ], + }, + }, + }, + }, ].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/fb/processor/data.ts b/test/integrations/destinations/fb/processor/data.ts index a437b90855..b0b4ba9ecf 100644 --- a/test/integrations/destinations/fb/processor/data.ts +++ b/test/integrations/destinations/fb/processor/data.ts @@ -497,7 +497,7 @@ export const data = [ traits: { email: 'abc@gmail.com', anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', - firstName: 'test', + firstName: ' test', lastName: 'last', gender: 1234, phone: '+91-9831311135', diff --git a/test/integrations/destinations/fb_custom_audience/dataDelivery/business.ts b/test/integrations/destinations/fb_custom_audience/dataDelivery/business.ts index c48ad227ab..6334094b3a 100644 --- a/test/integrations/destinations/fb_custom_audience/dataDelivery/business.ts +++ b/test/integrations/destinations/fb_custom_audience/dataDelivery/business.ts @@ -424,4 +424,50 @@ export const testScenariosForV1API: ProxyV1TestData[] = [ }, }, }, + { + id: 'fbca_v1_scenario_9', + name: 'fb_custom_audience', + description: 'user deletion failed differently created custom audience', + successCriteria: 'Fail with status code 400 and sending the actual error message.', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + method: 'DELETE', + endpoint: getEndPoint('aud-value-based'), + headers: { + 'test-dest-response-key': 'validAccessToken', + }, + params: testParams2, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + message: '(#100) Value-Based Custom Audience requires LOOKALIKE_VALUE attribute.', + statTags: { + ...statTags, + errorCategory: 'network', + errorType: 'aborted', + }, + response: [ + { + error: '(#100) Value-Based Custom Audience requires LOOKALIKE_VALUE attribute.', + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/fb_custom_audience/dataDelivery/other.ts b/test/integrations/destinations/fb_custom_audience/dataDelivery/other.ts index 52138604b0..2ad5964934 100644 --- a/test/integrations/destinations/fb_custom_audience/dataDelivery/other.ts +++ b/test/integrations/destinations/fb_custom_audience/dataDelivery/other.ts @@ -1,6 +1,6 @@ -import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; -import { ProxyV1TestData } from '../../../testTypes'; import { getEndPoint } from '../../../../../src/v0/destinations/fb_custom_audience/config'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; import { statTags, testParams2 as testParams } from './business'; export const otherScenariosV1: ProxyV1TestData[] = [ @@ -50,4 +50,47 @@ export const otherScenariosV1: ProxyV1TestData[] = [ }, }, }, + { + id: 'fbca_v1_other_scenario_2', + name: 'fb_custom_audience', + description: 'got invalid response format (not-json) from facebook', + successCriteria: 'should throw retyable error', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + method: 'DELETE', + endpoint: getEndPoint('aud1'), + headers: { + 'test-dest-response-key': 'htmlResponse', + }, + params: testParams, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: 'Invalid response format (HTML) during response transformation', + statTags, + response: [ + { + error: + '"

My First Heading

My first paragraph.

"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/fb_custom_audience/network.ts b/test/integrations/destinations/fb_custom_audience/network.ts index 9b498bc07e..369c27afa9 100644 --- a/test/integrations/destinations/fb_custom_audience/network.ts +++ b/test/integrations/destinations/fb_custom_audience/network.ts @@ -523,4 +523,105 @@ export const networkCallsData = [ status: 400, }, }, + { + httpReq: { + version: '1', + type: 'REST', + method: 'DELETE', + endpoint: getEndPoint('aud1'), + headers: { + 'test-dest-response-key': 'htmlResponse', + }, + params: { + access_token: 'ABC', + payload: { + is_raw: true, + data_source: { + sub_type: 'ANYTHING', + }, + schema: [ + 'EMAIL', + 'DOBM', + 'DOBD', + 'DOBY', + 'PHONE', + 'GEN', + 'FI', + 'MADID', + 'ZIP', + 'ST', + 'COUNTRY', + ], + data: [ + [ + 'shrouti@abc.com', + '2', + '13', + '2013', + '@09432457768', + 'f', + 'Ms.', + 'ABC', + 'ZIP ', + '123abc ', + 'IN', + ], + ], + }, + }, + userId: '', + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + }, + httpRes: { + data: '

My First Heading

My first paragraph.

', + status: 400, + }, + }, + { + httpReq: { + version: '1', + type: 'REST', + method: 'DELETE', + endpoint: getEndPoint('aud-value-based'), + headers: { + 'test-dest-response-key': 'validAccessToken', + }, + params: { + access_token: 'ABC', + payload: { + is_raw: true, + data_source: { + sub_type: 'ANYTHING', + }, + schema: ['DOBY', 'PHONE', 'GEN', 'FI', 'MADID', 'ZIP', 'ST', 'COUNTRY'], + data: [['2013', '@09432457768', 'f', 'Ms.', 'ABC', 'ZIP ', '123abc ', 'IN']], + }, + }, + userId: '', + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + }, + httpRes: { + data: { + error: { + message: '(#100) Value-Based Custom Audience requires LOOKALIKE_VALUE attribute.', + type: 'OAuthException', + code: 100, + fbtrace_id: 'ADB2jAGDMC_CbfM9430kDdQ', + }, + }, + status: 400, + }, + }, ]; diff --git a/test/integrations/destinations/fb_custom_audience/processor/data.ts b/test/integrations/destinations/fb_custom_audience/processor/data.ts index 267b966865..75fa321aca 100644 --- a/test/integrations/destinations/fb_custom_audience/processor/data.ts +++ b/test/integrations/destinations/fb_custom_audience/processor/data.ts @@ -1,5 +1,10 @@ import { getEndPoint } from '../../../../../src/v0/destinations/fb_custom_audience/config'; +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-15')); +}; + export const data = [ { name: 'fb_custom_audience', @@ -1261,6 +1266,23 @@ export const data = [ DOBY: '2013', PHONE: '@09432457768', GEN: 'f', + FN: 'test', + LN: 'user', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + { + EMAIL: 'testuser2@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FN: '1234', + LN: true, FI: 'Ms.', MADID: 'ABC', ZIP: 'ZIP ', @@ -1276,6 +1298,8 @@ export const data = [ DOBY: '2013', PHONE: '@09432457768', GEN: 'f', + FN: 'test', + LN: 'user', FI: 'Ms.', MADID: 'ABC', ZIP: 'ZIP ', @@ -1303,6 +1327,8 @@ export const data = [ 'DOBY', 'PHONE', 'GEN', + 'FN', + 'LN', 'FI', 'MADID', 'ZIP', @@ -1355,6 +1381,8 @@ export const data = [ 'DOBY', 'PHONE', 'GEN', + 'FN', + 'LN', 'FI', 'MADID', 'ZIP', @@ -1369,6 +1397,8 @@ export const data = [ '7931aa2a1bed855457d1ddf6bc06ab4406a9fba0579045a4d6ff78f9c07c440f', '0dcd4be87427e008a16adbdc2b2c15a14accf485dd451314dcecfb902c51c686', '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + '04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb', 'db0683221aebc02cc034b65ebcf7d1bddd1eb199e33fd23a31931947d13a11bc', 'abc', '4a70fe9aa6436e02c2dea340fbd1e352e4ef2d8ce6ca52ad25d4b95471fc8bf2', @@ -1409,6 +1439,8 @@ export const data = [ 'DOBY', 'PHONE', 'GEN', + 'FN', + 'LN', 'FI', 'MADID', 'ZIP', @@ -1423,6 +1455,23 @@ export const data = [ '7931aa2a1bed855457d1ddf6bc06ab4406a9fba0579045a4d6ff78f9c07c440f', '0dcd4be87427e008a16adbdc2b2c15a14accf485dd451314dcecfb902c51c686', '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + '04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb', + 'db0683221aebc02cc034b65ebcf7d1bddd1eb199e33fd23a31931947d13a11bc', + 'abc', + '4a70fe9aa6436e02c2dea340fbd1e352e4ef2d8ce6ca52ad25d4b95471fc8bf2', + 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', + '582967534d0f909d196b97f9e6921342777aea87b46fa52df165389db1fb8ccf', + ], + [ + '0059c0c78dac479648069fc1ed4298cd53bd857857aac57ac597537bedc6e043', + 'a953f09a1b6b6725b81956e9ad0b1eb49e3ad40004c04307ef8af6246a054116', + '3fdba35f04dc8c462986c992bcf875546257113072a909c162f7e470e581e278', + '7931aa2a1bed855457d1ddf6bc06ab4406a9fba0579045a4d6ff78f9c07c440f', + '0dcd4be87427e008a16adbdc2b2c15a14accf485dd451314dcecfb902c51c686', + '252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', + '', + 'b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b', 'db0683221aebc02cc034b65ebcf7d1bddd1eb199e33fd23a31931947d13a11bc', 'abc', '4a70fe9aa6436e02c2dea340fbd1e352e4ef2d8ce6ca52ad25d4b95471fc8bf2', @@ -54419,4 +54468,149 @@ export const data = [ }, }, }, -]; + { + name: 'fb_custom_audience', + description: + 'If App secret is configured in the UI, appsecret_proof and appsecret_time will be added to destination request.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + EMAIL: 'shrouti2@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + }, + }, + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + destination: { + Config: { + accessToken: 'ABC', + appSecret: 'dummySecret', + userSchema: [ + 'EMAIL', + 'DOBM', + 'DOBD', + 'DOBY', + 'PHONE', + 'GEN', + 'FI', + 'MADID', + 'ZIP', + 'ST', + 'COUNTRY', + ], + isHashRequired: false, + disableFormat: false, + audienceId: 'aud1', + isRaw: true, + type: 'NA', + subType: 'ANYTHING', + maxUserCount: '50', + }, + Enabled: true, + Transformations: [], + IsProcessorEnabled: true, + }, + libraries: [], + request: { + query: {}, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: getEndPoint('aud1'), + headers: {}, + params: { + access_token: 'ABC', + appsecret_proof: 'd103874f3b5f01f57c4f84edfb96ac94055da8f83c2b45e6f26dafca9188ff4d', + appsecret_time: 1697328000, + payload: { + is_raw: true, + data_source: { + sub_type: 'ANYTHING', + }, + schema: [ + 'EMAIL', + 'DOBM', + 'DOBD', + 'DOBY', + 'PHONE', + 'GEN', + 'FI', + 'MADID', + 'ZIP', + 'ST', + 'COUNTRY', + ], + data: [ + [ + 'shrouti2@abc.com', + '2', + '13', + '2013', + '@09432457768', + 'f', + 'Ms.', + 'ABC', + 'ZIP ', + '123abc ', + 'IN', + ], + ], + }, + }, + userId: '', + body: { + JSON: {}, + XML: {}, + JSON_ARRAY: {}, + FORM: {}, + }, + files: {}, + }, + statusCode: 200, + }, + ], + }, + }, + }, +].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/fb_custom_audience/router/audienceList.ts b/test/integrations/destinations/fb_custom_audience/router/audienceList.ts new file mode 100644 index 0000000000..c386fbf782 --- /dev/null +++ b/test/integrations/destinations/fb_custom_audience/router/audienceList.ts @@ -0,0 +1,123 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '50', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLAudienceRouterRequest: RouterTransformationRequest = { + input: [ + { + message: { + sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', + userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', + channel: 'sources', + messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', + event: 'Add_Audience', + context: { + destinationFields: 'EMAIL, FN', + externalId: [{ type: 'FB_CUSTOM_AUDIENCE-23848494844100489', identifierType: 'EMAIL' }], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', + rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', + properties: { + listData: { + add: [ + { EMAIL: 'dede@gmail.com', FN: 'vishwa' }, + { EMAIL: 'fchsjjn@gmail.com', FN: 'hskks' }, + { EMAIL: 'fghjnbjk@gmail.com', FN: 'ghfry' }, + { EMAIL: 'gvhjkk@gmail.com', FN: 'hbcwqe' }, + { EMAIL: 'qsdwert@egf.com', FN: 'dsfds' }, + { EMAIL: 'ascscxsaca@com', FN: 'scadscdvcda' }, + { EMAIL: 'abc@gmail.com', FN: 'subscribed' }, + { EMAIL: 'ddwnkl@gmail.com', FN: 'subscribed' }, + { EMAIL: 'subscribed@eewrfrd.com', FN: 'pending' }, + { EMAIL: 'acsdvdf@ddfvf.com', FN: 'pending' }, + ], + }, + }, + type: 'audienceList', + anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', + }, + destination: destination, + metadata: generateMetadata(3), + }, + { + message: { + sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', + userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', + channel: 'sources', + messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', + event: 'Add_Audience', + context: { + externalId: [{ type: 'FB_CUSTOM_AUDIENCE-23848494844100489', identifierType: 'EMAIL' }], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', + rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', + properties: { + listData: { + add: [ + { EMAIL: 'dede@gmail.com', FN: 'vishwa' }, + { EMAIL: 'fchsjjn@gmail.com', FN: 'hskks' }, + { EMAIL: 'fghjnbjk@gmail.com', FN: 'ghfry' }, + { EMAIL: 'gvhjkk@gmail.com', FN: 'hbcwqe' }, + { EMAIL: 'qsdwert@egf.com', FN: 'dsfds' }, + { EMAIL: 'ascscxsaca@com', FN: 'scadscdvcda' }, + { EMAIL: 'abc@gmail.com', FN: 'subscribed' }, + { EMAIL: 'ddwnkl@gmail.com', FN: 'subscribed' }, + { EMAIL: 'subscribed@eewrfrd.com', FN: 'pending' }, + { EMAIL: 'acsdvdf@ddfvf.com', FN: 'pending' }, + ], + }, + }, + type: 'audienceList', + anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', + }, + destination: destination, + metadata: generateMetadata(4), + }, + ], + destType: 'fb_custom_audience', +}; + +module.exports = { + rETLAudienceRouterRequest, +}; diff --git a/test/integrations/destinations/fb_custom_audience/router/batchingRecord.ts b/test/integrations/destinations/fb_custom_audience/router/batchingRecord.ts new file mode 100644 index 0000000000..0ceff5260e --- /dev/null +++ b/test/integrations/destinations/fb_custom_audience/router/batchingRecord.ts @@ -0,0 +1,130 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '2', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLBatchingRouterRequest: RouterTransformationRequest = { + input: [ + { + destination: destination, + message: { + action: 'insert', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(1), + }, + { + destination: destination, + message: { + action: 'insert', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(2), + }, + { + destination: destination, + message: { + action: 'insert', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'fb_custom_audience', +}; + +module.exports = { + rETLBatchingRouterRequest, +}; diff --git a/test/integrations/destinations/fb_custom_audience/router/data.ts b/test/integrations/destinations/fb_custom_audience/router/data.ts index 492b625b2d..72438e74b0 100644 --- a/test/integrations/destinations/fb_custom_audience/router/data.ts +++ b/test/integrations/destinations/fb_custom_audience/router/data.ts @@ -1,167 +1,20 @@ -import { getEndPoint } from '../../../../../src/v0/destinations/fb_custom_audience/config'; +import { eventStreamRouterRequest } from './eventStream'; +import { rETLAudienceRouterRequest } from './audienceList'; +import { rETLBatchingRouterRequest } from './batchingRecord'; +import { rETLRecordRouterRequest } from './record'; export const data = [ { name: 'fb_custom_audience', description: 'eventStream tests', + scenario: 'business', + successCriteria: 'event stream events should be batched correctly', feature: 'router', module: 'destination', version: 'v0', input: { request: { - body: { - input: [ - { - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'f', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - remove: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'f', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - }, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - metadata: { jobId: 1, userId: 'u1' }, - destination: { - Config: { - accessToken: 'ABC', - userSchema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - isHashRequired: false, - disableFormat: false, - audienceId: 'aud1', - isRaw: true, - type: 'UNKNOWN', - subType: 'ANYTHING', - maxUserCount: '50', - }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - }, - { - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'f', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - remove: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'f', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - }, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - metadata: { jobId: 2, userId: 'u1' }, - destination: { - Config: { - accessToken: 'ABC', - userSchema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - isHashRequired: false, - disableFormat: false, - audienceId: 'aud1', - isRaw: true, - type: 'NA', - subType: 'ANYTHING', - maxUserCount: '50', - }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - libraries: [], - request: { query: {} }, - }, - ], - destType: 'fb_custom_audience', - }, + body: eventStreamRouterRequest, }, }, output: { @@ -175,13 +28,15 @@ export const data = [ version: '1', type: 'REST', method: 'DELETE', - endpoint: getEndPoint('aud1'), + endpoint: 'https://graph.facebook.com/v18.0/aud1/users', headers: {}, params: { access_token: 'ABC', payload: { is_raw: true, - data_source: { type: 'UNKNOWN', sub_type: 'ANYTHING' }, + data_source: { + sub_type: 'ANYTHING', + }, schema: [ 'EMAIL', 'DOBM', @@ -212,20 +67,27 @@ export const data = [ ], }, }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, { version: '1', type: 'REST', method: 'POST', - endpoint: getEndPoint('aud1'), + endpoint: 'https://graph.facebook.com/v18.0/aud1/users', headers: {}, params: { access_token: 'ABC', payload: { is_raw: true, - data_source: { type: 'UNKNOWN', sub_type: 'ANYTHING' }, + data_source: { + sub_type: 'ANYTHING', + }, schema: [ 'EMAIL', 'DOBM', @@ -256,11 +118,29 @@ export const data = [ ], }, }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, ], - metadata: [{ jobId: 1, userId: 'u1' }], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 1, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], batched: false, statusCode: 200, destination: { @@ -283,13 +163,22 @@ export const data = [ disableFormat: false, audienceId: 'aud1', isRaw: true, - type: 'UNKNOWN', + type: 'NA', subType: 'ANYTHING', maxUserCount: '50', }, Enabled: true, Transformations: [], IsProcessorEnabled: true, + ID: '123', + Name: 'fb_custom_audience', + DestinationDefinition: { + ID: '123', + Name: 'fb_custom_audience', + DisplayName: 'fb_custom_audience', + Config: {}, + }, + WorkspaceID: '123', }, }, { @@ -298,13 +187,15 @@ export const data = [ version: '1', type: 'REST', method: 'DELETE', - endpoint: getEndPoint('aud1'), + endpoint: 'https://graph.facebook.com/v18.0/aud1/users', headers: {}, params: { access_token: 'ABC', payload: { is_raw: true, - data_source: { sub_type: 'ANYTHING' }, + data_source: { + sub_type: 'ANYTHING', + }, schema: [ 'EMAIL', 'DOBM', @@ -335,20 +226,27 @@ export const data = [ ], }, }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, { version: '1', type: 'REST', method: 'POST', - endpoint: getEndPoint('aud1'), + endpoint: 'https://graph.facebook.com/v18.0/aud1/users', headers: {}, params: { access_token: 'ABC', payload: { is_raw: true, - data_source: { sub_type: 'ANYTHING' }, + data_source: { + sub_type: 'ANYTHING', + }, schema: [ 'EMAIL', 'DOBM', @@ -379,11 +277,29 @@ export const data = [ ], }, }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, ], - metadata: [{ jobId: 2, userId: 'u1' }], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 2, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], batched: false, statusCode: 200, destination: { @@ -413,6 +329,15 @@ export const data = [ Enabled: true, Transformations: [], IsProcessorEnabled: true, + ID: '123', + Name: 'fb_custom_audience', + DestinationDefinition: { + ID: '123', + Name: 'fb_custom_audience', + DisplayName: 'fb_custom_audience', + Config: {}, + }, + WorkspaceID: '123', }, }, ], @@ -423,53 +348,158 @@ export const data = [ { name: 'fb_custom_audience', description: 'rETL tests', + scenario: 'business', + successCriteria: 'it should transform audience event correctly', feature: 'router', module: 'destination', version: 'v0', input: { request: { + body: rETLAudienceRouterRequest, + }, + }, + output: { + response: { + status: 200, body: { - input: [ + output: [ { - message: { - sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', - userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', - channel: 'sources', - messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', - event: 'Add_Audience', - context: { - destinationFields: 'EMAIL, FN', - externalId: [ - { type: 'FB_CUSTOM_AUDIENCE-23848494844100489', identifierType: 'EMAIL' }, - ], - mappedToDestination: 'true', - sources: { - job_run_id: 'cgiiurt8um7k7n5dq480', - task_run_id: 'cgiiurt8um7k7n5dq48g', - job_id: '2MUWghI7u85n91dd1qzGyswpZan', - version: '895/merge', + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FN'], + data: [ + [ + '7625cab24612c37df6d2f724721bb38a25095d0295e29b807238ee188b8aca43', + 'e328a0d90d4b5132b2655cf7079b160040d2c1a83d70d4cad9cf1f69310635b3', + ], + [ + 'b2b4abadd72190af54305c0d3abf1977fec4935016bb13ff28040d5712318dfd', + 'f8147eb72c9bb356c362fdb0796b54971ebc983cb60b3cc3ff29582ce2052bad', + ], + [ + 'c4b007d1c3c9a5d31bd4082237a913e8e0db1767225c2a5ef33be2716df005fa', + 'd8bb13b95eaed7f9b6a8af276aa6122e8015e0c466c1a84e49ff7c69ad6ac911', + ], + [ + '94639be1bd9f17c05820164e9d71ef78558f117a9e8bfab43cf8015e08aa0b27', + 'b1661f97721dede0f876dcbf603289ee339f641b9c310deba53c76940f472698', + ], + [ + '39b456cfb4bb07f9e6bb18698aa173171ca49c731fccc4790e9ecea808d24ae6', + '6c882abd6d0aff713cdd6a4a31ee28c9140612fb2627a611f6f9f539bac44f81', + ], + [ + '769f73387add781a481ca08300008a08fb2f1816aaed196137efc2e05976d711', + '2222cb73346f7a01a1d4d3db28b58fd41045782bb66152b92aade379192544c5', + ], + [ + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + 'abc12f8d666517c35280bf220f5390b1f0ef4bdbbc794ac59c95bba0381bf91b', + ], + [ + 'da2d431121cd10578fd81f8f80344b06db59ea2d05a7b5d27536c8789ddae8f0', + 'abc12f8d666517c35280bf220f5390b1f0ef4bdbbc794ac59c95bba0381bf91b', + ], + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '62a2fed3d6e08c44835fce71f02210b1ddabfb066e39edf1e6c261988f824dd3', + ], + [ + '0c1d1b0ba547a742013366d6fbc8f71dd77f566d94e41ed9f828a74b96928161', + '62a2fed3d6e08c44835fce71f02210b1ddabfb066e39edf1e6c261988f824dd3', + ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: false, + statusCode: 200, + destination: { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '50', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, }, - recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', - rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', - properties: { - listData: { - add: [ - { EMAIL: 'dede@gmail.com', FN: 'vishwa' }, - { EMAIL: 'fchsjjn@gmail.com', FN: 'hskks' }, - { EMAIL: 'fghjnbjk@gmail.com', FN: 'ghfry' }, - { EMAIL: 'gvhjkk@gmail.com', FN: 'hbcwqe' }, - { EMAIL: 'qsdwert@egf.com', FN: 'dsfds' }, - { EMAIL: 'ascscxsaca@com', FN: 'scadscdvcda' }, - { EMAIL: 'abc@gmail.com', FN: 'subscribed' }, - { EMAIL: 'ddwnkl@gmail.com', FN: 'subscribed' }, - { EMAIL: 'subscribed@eewrfrd.com', FN: 'pending' }, - { EMAIL: 'acsdvdf@ddfvf.com', FN: 'pending' }, - ], + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, + }, + }, + { + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 4, + secret: { + accessToken: 'default-accessToken', }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', }, - type: 'audienceList', - anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', + ], + batched: false, + statusCode: 400, + error: + 'context.destinationFields is required property for events mapped to destination ', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'FB_CUSTOM_AUDIENCE', + destinationId: 'default-destinationId', + module: 'destination', + implementation: 'native', + feature: 'router', + workspaceId: 'default-workspaceId', }, destination: { Config: { @@ -484,52365 +514,488 @@ export const data = [ type: 'NA', userSchema: ['EMAIL'], }, - secretConfig: {}, ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, }, - metadata: { jobId: 2, userId: 'u1' }, }, + ], + }, + }, + }, + }, + { + name: 'fb_custom_audience', + description: 'rETL record tests', + scenario: 'business', + successCriteria: 'all record events should be transformed correctly based on their operation', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: rETLRecordRouterRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ { - message: { - sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', - userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', - channel: 'sources', - messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', - event: 'Add_Audience', - context: { - externalId: [ - { type: 'FB_CUSTOM_AUDIENCE-23848494844100489', identifierType: 'EMAIL' }, - ], - mappedToDestination: 'true', - sources: { - job_run_id: 'cgiiurt8um7k7n5dq480', - task_run_id: 'cgiiurt8um7k7n5dq48g', - job_id: '2MUWghI7u85n91dd1qzGyswpZan', - version: '895/merge', + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'DELETE', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, + files: {}, }, - recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', - rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', - properties: { - listData: { - add: [ - { EMAIL: 'dede@gmail.com', FN: 'vishwa' }, - { EMAIL: 'fchsjjn@gmail.com', FN: 'hskks' }, - { EMAIL: 'fghjnbjk@gmail.com', FN: 'ghfry' }, - { EMAIL: 'gvhjkk@gmail.com', FN: 'hbcwqe' }, - { EMAIL: 'qsdwert@egf.com', FN: 'dsfds' }, - { EMAIL: 'ascscxsaca@com', FN: 'scadscdvcda' }, - { EMAIL: 'abc@gmail.com', FN: 'subscribed' }, - { EMAIL: 'ddwnkl@gmail.com', FN: 'subscribed' }, - { EMAIL: 'subscribed@eewrfrd.com', FN: 'pending' }, - { EMAIL: 'acsdvdf@ddfvf.com', FN: 'pending' }, - ], + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 1, + secret: { + accessToken: 'default-accessToken', }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', }, - type: 'audienceList', - anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', - }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 2, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, destination: { Config: { accessToken: 'ABC', disableFormat: false, isHashRequired: true, isRaw: false, - maxUserCount: '50', + maxUserCount: '3', oneTrustCookieCategories: [], skipVerify: false, subType: 'NA', type: 'NA', userSchema: ['EMAIL'], }, - secretConfig: {}, ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, }, - metadata: { jobId: 3, userId: 'u1' }, }, { - message: { - sentAt: '2023-03-30 06:42:55.991938402 +0000 UTC', - userId: '2MUWghI7u85n91dd1qzGyswpZan-2MUWqbQqvctyfMGqU9QCNadpKNy', - channel: 'sources', - messageId: '4d906837-031d-4d34-b97a-62fdf51b4d3a', - event: 'Add_Audience', - context: { - destinationFields: - 'EMAIL, DOBM, DOBD, DOBY, PHONE, GEN, FI, MADID, ZIP, ST, COUNTRY', - externalId: [{ type: 'FB_CUSTOM_AUDIENCE-aud1', identifierType: 'EMAIL' }], - mappedToDestination: 'true', - sources: { - job_run_id: 'cgiiurt8um7k7n5dq480', - task_run_id: 'cgiiurt8um7k7n5dq48g', - job_id: '2MUWghI7u85n91dd1qzGyswpZan', - version: '895/merge', + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ + [ + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', + ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, + files: {}, }, - recordId: '725ad989-6750-4839-b46b-0ddb3b8e5aa2/1/10', - rudderId: '85c49666-c628-4835-937b-8f1d9ee7a724', - properties: { - listData: { - add: [ - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - { - EMAIL: 'shrouti@abc.com', - DOBM: '2', - DOBD: '13', - DOBY: '2013', - PHONE: '@09432457768', - GEN: 'female', - FI: 'Ms.', - MADID: 'ABC', - ZIP: 'ZIP ', - ST: '123abc ', - COUNTRY: 'IN', - }, - ], - }, - }, - type: 'audienceList', - anonymousId: '63228b51-394e-4ca2-97a0-427f6187480b', - }, - destination: { - Config: { - accessToken: 'ABC', - disableFormat: false, - isHashRequired: false, - isRaw: true, - maxUserCount: '1000', - oneTrustCookieCategories: [], - skipVerify: false, - subType: 'NA', - type: 'NA', - userSchema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - }, - secretConfig: {}, - ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', - }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - }, - metadata: { jobId: 4, userId: 'u1' }, - }, - ], - destType: 'fb_custom_audience', - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: [ - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('23848494844100489'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - schema: ['EMAIL', 'FN'], - data: [ - ['7625cab24612c37df6d2f724721bb38a25095d0295e29b807238ee188b8aca43', ''], - ['b2b4abadd72190af54305c0d3abf1977fec4935016bb13ff28040d5712318dfd', ''], - ['c4b007d1c3c9a5d31bd4082237a913e8e0db1767225c2a5ef33be2716df005fa', ''], - ['94639be1bd9f17c05820164e9d71ef78558f117a9e8bfab43cf8015e08aa0b27', ''], - ['39b456cfb4bb07f9e6bb18698aa173171ca49c731fccc4790e9ecea808d24ae6', ''], - ['769f73387add781a481ca08300008a08fb2f1816aaed196137efc2e05976d711', ''], - ['48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', ''], - ['da2d431121cd10578fd81f8f80344b06db59ea2d05a7b5d27536c8789ddae8f0', ''], - ['b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', ''], - ['0c1d1b0ba547a742013366d6fbc8f71dd77f566d94e41ed9f828a74b96928161', ''], - ], - }, - }, - body: { JSON: {}, JSON_ARRAY: {}, XML: {}, FORM: {} }, - files: {}, - }, - ], - metadata: [{ jobId: 2, userId: 'u1' }], - batched: false, - statusCode: 200, - destination: { - Config: { - accessToken: 'ABC', - disableFormat: false, - isHashRequired: true, - isRaw: false, - maxUserCount: '50', - oneTrustCookieCategories: [], - skipVerify: false, - subType: 'NA', - type: 'NA', - userSchema: ['EMAIL'], - }, - secretConfig: {}, - ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', - }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - }, - }, - { - destination: { - Config: { - accessToken: 'ABC', - disableFormat: false, - isHashRequired: true, - isRaw: false, - maxUserCount: '50', - oneTrustCookieCategories: [], - skipVerify: false, - subType: 'NA', - type: 'NA', - userSchema: ['EMAIL'], - }, - secretConfig: {}, - ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', - }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, - }, - error: - 'context.destinationFields is required property for events mapped to destination ', - metadata: [{ jobId: 3, userId: 'u1' }], - batched: false, - statusCode: 400, - statTags: { - destType: 'FB_CUSTOM_AUDIENCE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'router', - implementation: 'native', - module: 'destination', - }, - }, - { - batchedRequest: [ - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('aud1'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - is_raw: true, - schema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - data: [ - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - ], - }, - }, - body: { JSON: {}, XML: {}, JSON_ARRAY: {}, FORM: {} }, - files: {}, - }, - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('aud1'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - is_raw: true, - schema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - data: [ - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - ], - }, - }, - body: { JSON: {}, XML: {}, JSON_ARRAY: {}, FORM: {} }, - files: {}, - }, - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('aud1'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - is_raw: true, - schema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - data: [ - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - ], - }, - }, - body: { JSON: {}, XML: {}, JSON_ARRAY: {}, FORM: {} }, - files: {}, - }, - { - version: '1', - type: 'REST', - method: 'POST', - endpoint: getEndPoint('aud1'), - headers: {}, - params: { - access_token: 'ABC', - payload: { - is_raw: true, - schema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], - data: [ - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], - [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', - ], + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '3', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, + }, + }, + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 4, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 5, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 6, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '3', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, + }, + }, + { + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 7, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: false, + statusCode: 400, + error: 'Invalid action type in record event', + statTags: { + errorCategory: 'dataValidation', + destinationId: 'default-destinationId', + errorType: 'instrumentation', + destType: 'FB_CUSTOM_AUDIENCE', + workspaceId: 'default-workspaceId', + module: 'destination', + implementation: 'native', + feature: 'router', + }, + }, + ], + }, + }, + }, + }, + { + name: 'fb_custom_audience', + description: 'rETL record batching tests', + scenario: 'Framework', + successCriteria: 'All the record events should be batched', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: rETLBatchingRouterRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], + ], + }, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://graph.facebook.com/v18.0/23848494844100489/users', + headers: {}, + params: { + access_token: 'ABC', + payload: { + schema: ['EMAIL', 'FI'], + data: [ [ - 'shrouti@abc.com', - '2', - '13', - '2013', - '@09432457768', - 'female', - 'Ms.', - 'ABC', - 'ZIP ', - '123abc ', - 'IN', + 'b100c2ec0718fe6b4805b623aeec6710719d042ceea55f5c8135b010ec1c7b36', + '1e14a2f476f7611a8b22bc85d14237fdc88aac828737e739416c32c5bce3bd16', ], ], }, }, - body: { JSON: {}, XML: {}, JSON_ARRAY: {}, FORM: {} }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, files: {}, }, ], - metadata: [{ jobId: 4, userId: 'u1' }], - batched: false, + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 1, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 2, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, statusCode: 200, destination: { Config: { accessToken: 'ABC', disableFormat: false, - isHashRequired: false, - isRaw: true, - maxUserCount: '1000', + isHashRequired: true, + isRaw: false, + maxUserCount: '2', oneTrustCookieCategories: [], skipVerify: false, subType: 'NA', type: 'NA', - userSchema: [ - 'EMAIL', - 'DOBM', - 'DOBD', - 'DOBY', - 'PHONE', - 'GEN', - 'FI', - 'MADID', - 'ZIP', - 'ST', - 'COUNTRY', - ], + userSchema: ['EMAIL'], }, - secretConfig: {}, - ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', - name: 'FB_CUSTOM_AUDIENCE', - enabled: true, - workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', - deleted: false, - createdAt: '2020-12-30T08:39:32.005Z', - updatedAt: '2021-02-03T16:22:31.374Z', - destinationDefinition: { - id: '1aIXqM806xAVm92nx07YwKbRrO9', - name: 'FB_CUSTOM_AUDIENCE', - displayName: 'FB_CUSTOM_AUDIENCE', - createdAt: '2020-04-09T09:24:31.794Z', - updatedAt: '2021-01-11T11:03:28.103Z', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + Config: {}, + DisplayName: 'FB_CUSTOM_AUDIENCE', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', }, - transformations: [], - isConnectionEnabled: true, - isProcessorEnabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, }, }, ], diff --git a/test/integrations/destinations/fb_custom_audience/router/eventStream.ts b/test/integrations/destinations/fb_custom_audience/router/eventStream.ts new file mode 100644 index 0000000000..b4dcebf48b --- /dev/null +++ b/test/integrations/destinations/fb_custom_audience/router/eventStream.ts @@ -0,0 +1,143 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + accessToken: 'ABC', + userSchema: [ + 'EMAIL', + 'DOBM', + 'DOBD', + 'DOBY', + 'PHONE', + 'GEN', + 'FI', + 'MADID', + 'ZIP', + 'ST', + 'COUNTRY', + ], + isHashRequired: false, + disableFormat: false, + audienceId: 'aud1', + isRaw: true, + type: 'NA', + subType: 'ANYTHING', + maxUserCount: '50', + }, + Enabled: true, + Transformations: [], + IsProcessorEnabled: true, + ID: '123', + Name: 'fb_custom_audience', + DestinationDefinition: { + ID: '123', + Name: 'fb_custom_audience', + DisplayName: 'fb_custom_audience', + Config: {}, + }, + WorkspaceID: '123', +}; + +export const eventStreamRouterRequest: RouterTransformationRequest = { + input: [ + { + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + EMAIL: 'shrouti@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + remove: [ + { + EMAIL: 'shrouti@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + }, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + metadata: generateMetadata(1), + destination: destination, + }, + { + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + EMAIL: 'shrouti@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + remove: [ + { + EMAIL: 'shrouti@abc.com', + DOBM: '2', + DOBD: '13', + DOBY: '2013', + PHONE: '@09432457768', + GEN: 'f', + FI: 'Ms.', + MADID: 'ABC', + ZIP: 'ZIP ', + ST: '123abc ', + COUNTRY: 'IN', + }, + ], + }, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + metadata: generateMetadata(2), + destination: destination, + request: { query: {} }, + }, + ], + destType: 'fb_custom_audience', +}; + +module.exports = { + eventStreamRouterRequest, +}; diff --git a/test/integrations/destinations/fb_custom_audience/router/record.ts b/test/integrations/destinations/fb_custom_audience/router/record.ts new file mode 100644 index 0000000000..534c1c40c2 --- /dev/null +++ b/test/integrations/destinations/fb_custom_audience/router/record.ts @@ -0,0 +1,250 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + accessToken: 'ABC', + disableFormat: false, + isHashRequired: true, + isRaw: false, + maxUserCount: '3', + oneTrustCookieCategories: [], + skipVerify: false, + subType: 'NA', + type: 'NA', + userSchema: ['EMAIL'], + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'FB_CUSTOM_AUDIENCE', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'FB_CUSTOM_AUDIENCE', + DisplayName: 'FB_CUSTOM_AUDIENCE', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLRecordRouterRequest: RouterTransformationRequest = { + input: [ + { + destination: destination, + message: { + action: 'insert', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(3), + }, + { + destination: destination, + message: { + action: 'update', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(4), + }, + { + destination: destination, + message: { + action: 'delete', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(1), + }, + { + destination: destination, + message: { + action: 'delete', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(2), + }, + { + destination: destination, + message: { + action: 'update', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(5), + }, + { + destination: destination, + message: { + action: 'update', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(6), + }, + { + destination: destination, + message: { + action: 'lol', + context: { + destinationFields: 'EMAIL, FI', + externalId: [ + { + type: 'FB_CUSTOM_AUDIENCE-23848494844100489', + identifierType: 'EMAIL', + }, + ], + mappedToDestination: 'true', + sources: { + job_run_id: 'cgiiurt8um7k7n5dq480', + task_run_id: 'cgiiurt8um7k7n5dq48g', + job_id: '2MUWghI7u85n91dd1qzGyswpZan', + version: '895/merge', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + EMAIL: 'subscribed@eewrfrd.com', + FI: 'ghui', + }, + type: 'record', + }, + metadata: generateMetadata(7), + }, + ], + destType: 'fb_custom_audience', +}; + +module.exports = { + rETLRecordRouterRequest, +}; diff --git a/test/integrations/destinations/freshmarketer/processor/data.ts b/test/integrations/destinations/freshmarketer/processor/data.ts index ed920faef0..07112d762f 100644 --- a/test/integrations/destinations/freshmarketer/processor/data.ts +++ b/test/integrations/destinations/freshmarketer/processor/data.ts @@ -375,7 +375,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'FRESHMARKETER', errorCategory: 'dataValidation', @@ -964,7 +964,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'FRESHMARKETER', errorCategory: 'dataValidation', diff --git a/test/integrations/destinations/freshsales/processor/data.ts b/test/integrations/destinations/freshsales/processor/data.ts index eca3b88d9d..7c0eca0926 100644 --- a/test/integrations/destinations/freshsales/processor/data.ts +++ b/test/integrations/destinations/freshsales/processor/data.ts @@ -505,7 +505,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'FRESHSALES', errorCategory: 'dataValidation', @@ -1094,7 +1094,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'FRESHSALES', errorCategory: 'dataValidation', diff --git a/test/integrations/destinations/fullstory/processor/data.ts b/test/integrations/destinations/fullstory/processor/data.ts index d206b4a84f..9c8d29c7e8 100644 --- a/test/integrations/destinations/fullstory/processor/data.ts +++ b/test/integrations/destinations/fullstory/processor/data.ts @@ -149,7 +149,7 @@ export const data = [ id: 's001', }, user: { - id: 'u001', + uid: 'u001', }, }, JSON_ARRAY: {}, diff --git a/test/integrations/destinations/ga4/dataDelivery/business.ts b/test/integrations/destinations/ga4/dataDelivery/business.ts new file mode 100644 index 0000000000..80271abbdb --- /dev/null +++ b/test/integrations/destinations/ga4/dataDelivery/business.ts @@ -0,0 +1,350 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { JSON_MIME_TYPE } from '../../../../../src/v0/util/constant'; + +const headers = { + HOST: 'www.google-analytics.com', + 'Content-Type': JSON_MIME_TYPE, +}; + +const params = { + api_secret: 'dymmyApiSecret', +}; + +const validRequest = { + events: [ + { + name: 'sign_up', + params: { + method: 'google', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidEventNameRequest = { + events: [ + { + name: 'campaign@details', + params: { + term: 'summer+travel', + medium: 'cpc', + source: 'google', + content: 'logo link', + campaign: 'Summer_fun', + campaign_id: 'google_1234', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidParameterValueRequest = { + events: [ + { + name: 'add_to_cart', + params: { + currency: 'USD', + value: 7.77, + engagement_time_msec: 1, + items: [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: '$19', + quantity: 2, + affiliation: 'Google Merchandise Store', + currency: 'USD', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, + ], + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, +}; + +const invalidParamMessage = + 'Validation of item.price should prevent conversion from unsupported value [string_value: "$19"]'; +const invalidParameterErrorMessage = `Validation Server Response Handler:: Validation Error for ga4 of field path :undefined | INTERNAL_ERROR-${invalidParamMessage}`; +const invalidEventNameErrorMessage = + 'Validation Server Response Handler:: Validation Error for ga4 of field path :events | NAME_INVALID-Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.'; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GA4', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'ga4_v0_scenario_1', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a valid request - where the destination responds with 200 without any error', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: validRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: { + validationMessages: [], + }, + status: 200, + }, + message: '[GA4 Response Handler] - Request Processed Successfully', + status: 200, + }, + }, + }, + }, + }, + { + id: 'ga4_v0_scenario_2', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a invalid event name - where the destination responds with 200 with error for invalid event name', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: invalidEventNameRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: + 'Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.', + message: invalidEventNameErrorMessage, + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ga4_v0_scenario_3', + name: 'ga4', + description: + '[Proxy v0 API] :: Test for a invalid parameter value - where the destination responds with 200 with error for invalid parameter value', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers, + params, + JSON: invalidParameterValueRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: invalidParamMessage, + message: invalidParameterErrorMessage, + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'ga4_v1_scenario_1', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a valid request - where the destination responds with 200 without any error', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: validRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: '[GA4 Response Handler] - Request Processed Successfully', + response: [ + { + error: '{"validationMessages":[]}', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'ga4_v1_scenario_2', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a invalid event name - where the destination responds with 200 with error for invalid event name', + successCriteria: 'Should return 400 with error and destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: invalidEventNameRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: invalidEventNameErrorMessage, + response: [ + { + error: invalidEventNameErrorMessage, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ga4_v1_scenario_3', + name: 'ga4', + description: + '[Proxy v1 API] :: Test for a invalid parameter value - where the destination responds with 200 with error for invalid parameter value', + successCriteria: 'Should return 200 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers, + params, + JSON: invalidParameterValueRequest, + endpoint: 'https://www.google-analytics.com/debug/mp/collect', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: invalidParameterErrorMessage, + response: [ + { + error: invalidParameterErrorMessage, + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/ga4/dataDelivery/data.ts b/test/integrations/destinations/ga4/dataDelivery/data.ts index 9ccf35e2a1..51827a38e2 100644 --- a/test/integrations/destinations/ga4/dataDelivery/data.ts +++ b/test/integrations/destinations/ga4/dataDelivery/data.ts @@ -1,177 +1,3 @@ -export const data = [ - { - name: 'ga4', - description: 'Successful data delivery', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, - body: { - JSON: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item_list', - params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: { - validationMessages: [], - }, - status: 200, - }, - message: '[GA4 Response Handler] - Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: 'ga4', - description: 'Data delivery failure', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/debug/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, - body: { - JSON: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item', - params: { - category: 'Electronics', - productID: 'ABC123', - productName: 'Example Product', - customer_name: 'Sample User', - link_imageURL: 'https://example.com/images/product.jpg', - customer_email: 'testrudder@gmail.com', - link_productURL: 'https://example.com/products/ABC123', - stockAvailability: true, - details_features_0: 'wireless charging', - details_features_1: 'water-resistant', - engagement_time_msec: 1, - transaction_currency: 'USD', - customer_loyaltyPoints: 500, - transaction_totalAmount: 150.99, - transaction_discountApplied: 20.5, - details_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_color: - 'blue', - details_specifications_specifications_specifications_specifications_weight: - '1.5kg', - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: - 'The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - message: - 'Validation Server Response Handler:: Validation Error for ga4 of field path :events.params | NAME_DUPLICATED-The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - statTags: { - destType: 'GA4', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, -]; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + +export const data = [...testScenariosForV0API, ...testScenariosForV1API]; diff --git a/test/integrations/destinations/ga4/mocks.ts b/test/integrations/destinations/ga4/mocks.ts index 7b47fe9f44..3a27349ff7 100644 --- a/test/integrations/destinations/ga4/mocks.ts +++ b/test/integrations/destinations/ga4/mocks.ts @@ -1,3 +1,5 @@ export const defaultMockFns = () => { - jest.spyOn(Date, 'now').mockImplementation(() => new Date('2022-04-29T05:17:09Z').valueOf()); + return jest + .spyOn(Date, 'now') + .mockImplementation(() => new Date('2022-04-29T05:17:09Z').valueOf()); }; diff --git a/test/integrations/destinations/ga4/network.ts b/test/integrations/destinations/ga4/network.ts index e8c91ef451..b5c8dc8e8e 100644 --- a/test/integrations/destinations/ga4/network.ts +++ b/test/integrations/destinations/ga4/network.ts @@ -1,119 +1,139 @@ -export const networkCallsData = [ +const headers = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const params = { + api_secret: 'dymmyApiSecret', +}; + +const dataDeliveryMocksData = [ { + description: 'Mock response from destination depicting a valid request', httpReq: { - url: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', - }, + method: 'post', + url: 'https://www.google-analytics.com/debug/mp/collect', data: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, events: [ { - name: 'view_item_list', + name: 'sign_up', params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], + method: 'google', engagement_time_msec: 1, }, }, ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - method: 'POST', + headers, + params, }, httpRes: { data: { validationMessages: [], }, status: 200, + statusText: 'OK', }, }, { + description: 'Mock response from destination depicting a invalid event name request', httpReq: { + method: 'post', url: 'https://www.google-analytics.com/debug/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', + data: { + events: [ + { + name: 'campaign@details', + params: { + term: 'summer+travel', + medium: 'cpc', + source: 'google', + content: 'logo link', + campaign: 'Summer_fun', + campaign_id: 'google_1234', + engagement_time_msec: 1, + }, + }, + ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'dummyMeasurementId', + headers, + params, + }, + httpRes: { + data: { + validationMessages: [ + { + fieldPath: 'events', + description: + 'Event at index: [0] has invalid name [campaign@details]. Only alphanumeric characters and underscores are allowed.', + validationCode: 'NAME_INVALID', + }, + ], }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting a invalid parameter value request', + httpReq: { + method: 'post', + url: 'https://www.google-analytics.com/debug/mp/collect', data: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, events: [ { - name: 'view_item', + name: 'add_to_cart', params: { - category: 'Electronics', - productID: 'ABC123', - productName: 'Example Product', - customer_name: 'Sample User', - link_imageURL: 'https://example.com/images/product.jpg', - customer_email: 'testrudder@gmail.com', - link_productURL: 'https://example.com/products/ABC123', - stockAvailability: true, - details_features_0: 'wireless charging', - details_features_1: 'water-resistant', + currency: 'USD', + value: 7.77, engagement_time_msec: 1, - transaction_currency: 'USD', - customer_loyaltyPoints: 500, - transaction_totalAmount: 150.99, - transaction_discountApplied: 20.5, - details_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_color: 'blue', - details_specifications_specifications_specifications_specifications_weight: '1.5kg', + items: [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: '$19', + quantity: 2, + affiliation: 'Google Merchandise Store', + currency: 'USD', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, + ], }, }, ], + user_id: 'dummyUserId', + client_id: 'dummyClientId', + non_personalized_ads: true, }, - method: 'POST', + headers, + params, }, httpRes: { data: { validationMessages: [ { - fieldPath: 'events.params', description: - 'The event param [string_value: "1.5kg"] has a duplicate name [details_specifications_specifications_specifications_specifications_weight].', - validationCode: 'NAME_DUPLICATED', + 'Validation of item.price should prevent conversion from unsupported value [string_value: "$19"]', + validationCode: 'INTERNAL_ERROR', }, ], }, status: 200, + statusText: 'OK', }, }, ]; + +export const networkCallsData = [...dataDeliveryMocksData]; diff --git a/test/integrations/destinations/ga4/processor/data.ts b/test/integrations/destinations/ga4/processor/data.ts index f96ca9e74a..fb65787214 100644 --- a/test/integrations/destinations/ga4/processor/data.ts +++ b/test/integrations/destinations/ga4/processor/data.ts @@ -1,14903 +1,3 @@ -import { defaultMockFns } from '../mocks'; -export const data = [ - { - name: 'ga4', - description: "(gtag) check all property mappings for 'Products Searched' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99v4f979fb997ce453373900f891', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'Products Searched', - properties: { - query: 't-shirts', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-22T10:57:58Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - timestamp_micros: 1650950229000000, - events: [ - { - name: 'search', - params: { - search_term: 't-shirts', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product list viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - rudderId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99v4f979fb997ce453373900f891', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product list viewed', - properties: { - list_id: 'related_products', - category: 'Related_products', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item_list', - params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'promotion viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion viewed', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'view_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 0, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'promotion clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 0, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) exclude only 'products' property from 'promotion clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product clicked', - properties: { - list_id: 'related_products', - category: 'Related_products', - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - timezone: { - name: 'Europe/Tallinn', - }, - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_item', - params: { - item_list_id: 'related_products', - item_list_name: 'Related_products', - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Related_products', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - timezone_name: 'Europe/Tallinn', - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product viewed', - properties: { - currency: 'USD', - total: '7.77', - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_item', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'promotion clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - typesOfClient: 'gtag', - firebaseAppId: '1:17864591371:android:7a9520d3c78962e21f9fee', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'select_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 0, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(firebase) check all property mappings for 'promotion clicked' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - typesOfClient: 'firebase', - firebaseAppId: '1:17864591371:android:7a9520d3c78962e21f9fee', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - firebase_app_id: '1:17864591371:android:7a9520d3c78962e21f9fee', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - app_instance_id: 'f0dd99b6f979fb551ce583373900f937', - events: [ - { - name: 'select_promotion', - params: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 0, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product added' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product removed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product removed', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'remove_from_cart', - params: { - currency: 'USD', - value: 7.77, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'cart viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'cart viewed', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_cart', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'checkout started' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'checkout started', - properties: { - currency: 'USD', - total: 7.77, - coupon: 'SUMMER_FUN', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'begin_checkout', - params: { - currency: 'USD', - value: 7.77, - coupon: 'SUMMER_FUN', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) check all property mappings for 'payment info entered' -> 'add_payment_info' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'payment info entered', - properties: { - currency: 'USD', - value: '7.77', - coupon: 'SUMMER_FUN', - payment_method: 'Credit Card', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - params: { - currency: 'USD', - value: 7.77, - coupon: 'SUMMER_FUN', - payment_type: 'Credit Card', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - name: 'add_payment_info', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) check all property mappings for 'checkout Step Completed' -> 'add_shipping_info' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'checkout Step Completed', - properties: { - currency: 'USD', - value: '7.77', - coupon: 'SUMMER_FUN', - shipping_method: 'Ground', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - params: { - currency: 'USD', - value: 7.77, - coupon: 'SUMMER_FUN', - shipping_tier: 'Ground', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - name: 'add_shipping_info', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'order completed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'order completed', - properties: { - currency: 'USD', - order_id: 'T_12345', - total: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'purchase', - params: { - currency: 'USD', - transaction_id: 'T_12345', - value: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'order refunded' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'order refunded', - properties: { - currency: 'USD', - order_id: 'T_12345', - total: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'refund', - params: { - currency: 'USD', - transaction_id: 'T_12345', - value: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) exclude only 'products' property from 'order refunded' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'order refunded', - properties: { - currency: 'USD', - order_id: 'T_12345', - total: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'refund', - params: { - currency: 'USD', - transaction_id: 'T_12345', - value: 12.21, - affiliation: 'Google Store', - coupon: 'SUMMER_FUN', - shipping: 3.33, - tax: 1.11, - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product added to wishlist' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added to wishlist', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'add_to_wishlist', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product_shared' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product_shared', - properties: { - share_via: 'Twitter', - content_type: 'image', - item_id: 'C_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'share', - params: { - method: 'Twitter', - content_type: 'image', - item_id: 'C_12345', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'product_shared' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product_shared', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'share', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'cart Shared' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'cart Shared', - properties: { - share_via: 'Twitter', - content_type: 'image', - item_id: 'C_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'share', - params: { - method: 'Twitter', - content_type: 'image', - item_id: 'C_12345', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'cart Shared' event name with empty properties: {}", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'cart Shared', - properties: {}, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'share', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) check all property mappings for group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'group', - properties: { - group_id: 'G_12345', - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'group', - params: { - group_id: 'G_12345', - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) group: send only group event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'group', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'group', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'earn virtual currency' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'earn virtual currency', - properties: { - virtual_currency_name: 'Gems', - value: 5, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'earn_virtual_currency', - params: { - virtual_currency_name: 'Gems', - value: 5, - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'earn virtual currency' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'earn virtual currency', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'earn_virtual_currency', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'generate_lead' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'generate_lead', - properties: { - currency: 'USD', - value: 99.99, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 99.99, - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'level_up' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'level_up', - properties: { - level: 5, - character: 'Player 1', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'level_up', - params: { - level: 5, - character: 'Player 1', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'level_up' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'level_up', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'level_up', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'login' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'login', - properties: { - method: 'Google', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'login', - params: { - method: 'Google', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'login' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'login', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'login', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'post_score' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'post_score', - properties: { - score: 10000, - level: 5, - character: 'Player 1', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'post_score', - params: { - score: 10000, - level: 5, - character: 'Player 1', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'login' event name with its required `score` properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'post_score', - properties: { - score: 10000, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'post_score', - params: { - score: 10000, - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'select_content' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'select_content', - properties: { - content_type: 'product', - item_id: 'I_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_content', - params: { - content_type: 'product', - item_id: 'I_12345', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'group' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'select_content', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_content', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'sign_up' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'sign_up', - properties: { - method: 'Google', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'sign_up', - params: { - method: 'Google', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send only 'sign_up' event name without it's properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'sign_up', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'sign_up', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'spend_virtual_currency' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'spend_virtual_currency', - properties: { - value: 5, - virtual_currency_name: 'Gems', - item_name: 'Starter Boost', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'spend_virtual_currency', - params: { - value: 5, - virtual_currency_name: 'Gems', - item_name: 'Starter Boost', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) send only 'spend_virtual_currency' event name with it's required 'value' and 'virtual_currency_name' properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'spend_virtual_currency', - properties: { - value: 5, - virtual_currency_name: 'Gems', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'spend_virtual_currency', - params: { - value: 5, - virtual_currency_name: 'Gems', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send 'tutorial_begin' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutorial_begin', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'tutorial_begin', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send 'tutorial_complete' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutorial_complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'tutorial_complete', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send all properties for 'unlock_achievement' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'unlock_achievement', - properties: { - achievement_id: 'A_12345', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'unlock_achievement', - params: { - achievement_id: 'A_12345', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) send all properties for 'view_search_results' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'view_search_results', - properties: { - search_term: 'Clothing', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_search_results', - params: { - search_term: 'Clothing', - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) pass only 'products: [...]' property for 'view_search_results' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'view_search_results', - properties: { - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_search_results', - params: { - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) pass custom event name 'rudderstack event' to GA4 along with custom properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - properties: { - total: '10', - timezone: { - name: 'Europe/Tallinn', - }, - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'rudderstack_event', - params: { - total: '10', - timezone_name: 'Europe/Tallinn', - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag) pass custom event name 'rudderstack event' to GA4 along with custom properties and user_properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - properties: { - total: '10', - user_properties: { - price: '19', - }, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - user_properties: { - price: { - value: '19', - }, - }, - events: [ - { - name: 'rudderstack_event', - params: { - total: '10', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (gtag) pass reserved event name to GA4', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'ad_click', - properties: { - total: '10', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'track:: Reserved event names are not allowed', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Exclusion]: (gtag) pass reserved property name to GA4 for custom events', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - properties: { - firebase_conversion: 'firebase_conversion', - google_id: '1234', - ga_value: 'ga_value', - value: '10', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'rudderstack_event', - params: { - value: '10', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '[Exclusion]: (gtag) pass reserved property name and reserved properties in `user_properties` to GA4 for custom events', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - properties: { - firebase_conversion: 'firebase_conversion', - google_id: '1234', - ga_value: 'ga_value', - value: '10', - user_properties: { - first_open_time: 'first_open_time', - user_id: 'user_id', - firebase_value: 'firebase_value', - price: '100', - }, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - user_properties: { - price: { - value: '100', - }, - }, - events: [ - { - name: 'rudderstack_event', - params: { - value: '10', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (gtag) pass reserved event names along with reserved properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'app_remove', - properties: { - firebase_conversion: 'firebase_conversion', - google_id: '1234', - ga_value: 'ga_value', - value: '10', - user_properties: { - first_open_time: 'first_open_time', - user_id: 'user_id', - firebase_value: 'firebase_value', - price: '100', - }, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'track:: Reserved event names are not allowed', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (gtag) pass reserved custom prefix names to GA4 events', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'firebase_event1', - properties: { - firebase_conversion: 'firebase_conversion', - google_id: '1234', - ga_value: 'ga_value', - value: '10', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Reserved custom prefix names are not allowed', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'product added' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - total: '7.77', - google_data: 'google_data', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) pass custom event name with its properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'rudderstack event', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'rudderstack_event', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) map 'product added' properties to ga4 'add_to_cart' items array", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - affiliation: 'Google Merchandise Store', - brand: 'Google', - category: 'Related_products', - coupon: 'SUMMER_FUN', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - list_id: 'related_products', - location_id: 'L_12345', - name: 'Monopoly: 3rd Edition', - position: '1', - price: '19', - product_id: '507f1f77bcf86cd799439011', - products: [], - quantity: '2', - total: '7.77', - variant: 'green', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - FORM: {}, - JSON: { - client_id: 'client_id', - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - engagement_time_msec: 1, - items: [ - { - affiliation: 'Google Merchandise Store', - coupon: 'SUMMER_FUN', - currency: 'USD', - discount: 2.22, - index: 1, - item_brand: 'Google', - item_category: 'Related_products', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_id: '507f1f77bcf86cd799439011', - item_list_id: 'related_products', - item_list_name: 'Related Products', - item_name: 'Monopoly: 3rd Edition', - item_variant: 'green', - location_id: 'L_12345', - price: 19, - quantity: 2, - }, - ], - list_id: 'related_products', - value: 7.77, - }, - }, - ], - non_personalized_ads: true, - timestamp_micros: 1650950229000000, - }, - JSON_ARRAY: {}, - XML: {}, - }, - endpoint: 'https://www.google-analytics.com/mp/collect', - files: {}, - headers: { - 'Content-Type': 'application/json', - HOST: 'www.google-analytics.com', - }, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - type: 'REST', - version: '1', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '(gtag) pass event name with invalid data type for products: {...} properties (when products parameter is optional)', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - total: '7.77', - products: { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - affiliation: 'Google Merchandise Store', - brand: 'Google', - category: 'Related_products', - coupon: 'SUMMER_FUN', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - list_id: 'related_products', - location_id: 'L_12345', - name: 'Monopoly: 3rd Edition', - position: '1', - price: '19', - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - variant: 'green', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - FORM: {}, - JSON: { - client_id: 'client_id', - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - engagement_time_msec: 1, - items: [ - { - affiliation: 'Google Merchandise Store', - coupon: 'SUMMER_FUN', - currency: 'USD', - discount: 2.22, - index: 1, - item_brand: 'Google', - item_category: 'Related_products', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_id: '507f1f77bcf86cd799439011', - item_list_id: 'related_products', - item_list_name: 'Related Products', - item_name: 'Monopoly: 3rd Edition', - item_variant: 'green', - location_id: 'L_12345', - price: 19, - quantity: 2, - }, - ], - list_id: 'related_products', - value: 7.77, - }, - }, - ], - non_personalized_ads: true, - timestamp_micros: 1650950229000000, - }, - JSON_ARRAY: {}, - XML: {}, - }, - endpoint: 'https://www.google-analytics.com/mp/collect', - files: {}, - headers: { - 'Content-Type': 'application/json', - HOST: 'www.google-analytics.com', - }, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - type: 'REST', - version: '1', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '[Error] (gtag) pass event name to GA4 with missing fields i.e required in products: [..]', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'One of product_id or name is required', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: - '[Error] (gtag) pass event name to GA4 with missing fields i.e required in properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'Product Viewed', - properties: { - currency: 'USD', - total: '7.77', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'One of product_id or name is required', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (gtag) missing API Secret', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'API Secret not found. Aborting ', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (gtag) missing measurementId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: '', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'measurementId must be provided. Aborting', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '(gtag) firing group event with event name. should take event name by default', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'group', - event: 'tutorial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'join_group', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error] (gtag) payload has missing message.type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Message Type is not present. Aborting message.', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (gtag) payload has missing event name', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event name is required', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '(gtag) taking client_id from anonymousId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - events: [ - { - name: 'tutotial_complete', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error] (firebase) payload has missing ga4AppInstanceId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: '', - firebaseAppId: '1:17864591371:android:7a9520d3c78962e21f9fee', - blockPageViewEvent: false, - typesOfClient: 'firebase', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'ga4AppInstanceId must be provided under externalId', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] (firebase) pass reserved event name', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'app_store_subscription_cancel', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: '', - firebaseAppId: '1:17864591371:android:7a9520d3c78962e21f9fee', - blockPageViewEvent: false, - typesOfClient: 'firebase', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Reserved custom event names are not allowed', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'tutorial complete' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutorial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - non_personalized_ads: true, - events: [ - { - name: 'tutorial_complete', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) check all property mappings for 'cart viewed' event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'cart viewed', - properties: { - currency: 'USD', - total: '7.77', - products: [ - { - product_id: 0, - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'view_cart', - params: { - currency: 'USD', - value: 7.77, - engagement_time_msec: 1, - items: [ - { - item_id: 0, - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (firebase) missing firebaseAppId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: '', - typesOfClient: 'firebase', - firebaseAppId: '', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'firebaseAppId must be provided. Aborting', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '(gtag) firing page call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - rudderId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'page', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - events: [ - { - name: 'page_view', - params: { - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) firing page call with custom properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - rudderId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'page', - event: 'page view', - properties: { - view: 'login', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - events: [ - { - name: 'page_view', - params: { - view: 'login', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (gtag) pass timestamp more than 72 hours into the past', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-20T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - blockPageViewEvent: false, - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Allowed timestamp is [72 hours] into the past', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error]: (gtag) pass timestamp more than 15 min into the future', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-05-05T15:47:57Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'tutotial complete', - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - blockPageViewEvent: false, - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Allowed timestamp is [15 minutes] into the future', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: - '(gtag) pass custom properties along with products: [..] parameters to GA4 standard events along with its stated ones', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'order completed', - properties: { - checkout_id: '12345', - order_id: '1234', - myCustomProp: 'My arbitray value', - affiliation: 'Apple Store', - total: 20, - revenue: 15, - shipping: 22, - tax: 1, - discount: 1.5, - coupon: 'ImagePro', - currency: 'USD', - products: [ - { - product_id: '123', - sku: 'G-32', - name: 'Monopoly', - price: 14, - quantity: 1, - category: 'Games', - item_category2: 'Board games', - url: 'https://www.website.com/product/path', - image_url: 'https://www.website.com/product/path.jpg', - }, - ], - timezone: { - name: 'Europe/Tallinn', - }, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'purchase', - params: { - checkout_id: '12345', - transaction_id: '1234', - myCustomProp: 'My arbitray value', - affiliation: 'Apple Store', - value: 20, - shipping: 22, - tax: 1, - discount: 1.5, - coupon: 'ImagePro', - currency: 'USD', - engagement_time_msec: 1, - items: [ - { - item_id: '123', - sku: 'G-32', - item_name: 'Monopoly', - price: 14, - quantity: 1, - item_category: 'Games', - item_category2: 'Board games', - url: 'https://www.website.com/product/path', - image_url: 'https://www.website.com/product/path.jpg', - }, - ], - timezone_name: 'Europe/Tallinn', - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '(gtag) pass custom properties excluding products: [..] parameter to GA4 standard events along with its stated ones', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'promotion clicked', - properties: { - 'customProp-1': 'check-1', - 'customProp-2': 'check-2', - timezone: { - name: 'Europe/Tallinn', - }, - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - client_id: 'client_id', - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - events: [ - { - name: 'select_promotion', - params: { - 'customProp-1': 'check-1', - 'customProp-2': 'check-2', - timezone_name: 'Europe/Tallinn', - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: "(gtag) message type group -> 'join_group' with custom event", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'group', - traits: { - custom1: 1234, - custom2: 'custom2', - timezone: { - name: 'Europe/Tallinn', - }, - engagementTimeMsec: 100, - sessionId: 655, - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'join_group', - params: { - custom1: 1234, - custom2: 'custom2', - timezone_name: 'Europe/Tallinn', - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag): check args keyword for price x currency multiplication', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - value: 4.8, - engagement_time_msec: 1, - items: [ - { - item_id: '507f1f77bcf86cd799439011', - item_name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - item_category: 'Apparel', - item_brand: 'Google', - item_variant: 'green', - price: 19, - quantity: 2, - index: 1, - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - price: 2.4, - quantity: 2, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - "(gtag): take page properties from context.page for 'page' call along with custom properties", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - rudderId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - page: { - url: 'http://morkey.in', - path: '/cart', - title: 'miphone', - search: 'MI', - referrer: 'morkey', - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'page', - integrations: { - All: true, - }, - properties: { - cust1: 1234, - engagementTimeMsec: 100, - sessionId: 655, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - events: [ - { - name: 'page_view', - params: { - page_referrer: 'morkey', - page_title: 'miphone', - page_location: 'http://morkey.in', - cust1: 1234, - engagement_time_msec: 100, - session_id: 655, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error] GA4: event not as string', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2022-04-26T05:17:09Z', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: { - name: 'promotion_viewed', - }, - properties: { - creative_name: 'Summer Banner', - creative_slot: 'featured_app_1', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '0', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - promotion_id: 'P_12345', - promotion_name: 'Summer Sale', - creative_name: 'summer_banner2', - creative_slot: 'featured_app_1', - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-04-20T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'track:: event name should be string', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '[Error] GA4: client_id not found in all four path', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: true, - sendLoginSignup: true, - newOrExistingUserTrait: 'firstLogin', - loginSignupMethod: 'method', - generateLead: true, - generateLeadValueTrait: 'value', - generateLeadCurrencyTrait: 'currency', - clientIdFieldIdentifier: 'properties.client_id', - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'ga4ClientId, anonymousId or messageId must be provided', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: - "client_id isn't sent from the path defined in the webapp config, falling back to default values i.e here it is anonymousId", - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: true, - sendLoginSignup: true, - newOrExistingUserTrait: 'firstLogin', - loginSignupMethod: 'method', - generateLead: true, - generateLeadValueTrait: 'value', - generateLeadCurrencyTrait: 'currency', - clientIdFieldIdentifier: 'properties.client_id', - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'add_to_cart', - params: { - items: [ - { - index: 1, - price: 19, - coupon: 'SUMMER_FUN', - item_id: '507f1f77bcf86cd799439011', - currency: 'USD', - discount: 2.22, - quantity: 2, - item_name: 'Monopoly: 3rd Edition', - item_brand: 'Google', - affiliation: 'Google Merchandise Store', - location_id: 'L_12345', - item_list_id: 'related_products', - item_variant: 'green', - item_category: 'Apparel', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_name: 'Related Products', - }, - ], - price: 2.4, - value: 4.8, - currency: 'USD', - quantity: 2, - engagement_time_msec: 1, - }, - }, - ], - client_id: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - 'any custom or item property with array value, is flattened with underscore delimeter', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'product added', - originalTimestamp: '2022-04-26T05:17:09Z', - properties: { - currency: 'USD', - total: '7.77', - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - address: { - city: 'kolkata', - district: '24pgs', - }, - categoryLevels: ['Furniture', 'Bedroom Furniture', 'Dressers & Chests'], - products: [ - { - product_id: '1234', - product_details: { - colour: 'red', - shape: 'rectangle', - }, - productLevels: ['test1', 'test2', 'test3'], - }, - ], - }, - integrations: { - All: true, - }, - sentAt: '2022-11-14T15:20:57Z', - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: false, - sendLoginSignup: false, - generateLead: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - timestamp_micros: 1650950229000000, - non_personalized_ads: true, - client_id: 'client_id', - events: [ - { - name: 'add_to_cart', - params: { - currency: 'USD', - value: 7.77, - items: [ - { - item_id: '1234', - product_details_colour: 'red', - product_details_shape: 'rectangle', - productLevels_0: 'test1', - productLevels_1: 'test2', - productLevels_2: 'test3', - }, - ], - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - address_city: 'kolkata', - address_district: '24pgs', - categoryLevels_0: 'Furniture', - categoryLevels_1: 'Bedroom Furniture', - categoryLevels_2: 'Dressers & Chests', - engagement_time_msec: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: 'extract session_id from context.sessionId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - sessionId: 16678456735, - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - client_id: 'client@1234', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: true, - sendLoginSignup: true, - newOrExistingUserTrait: 'firstLogin', - loginSignupMethod: 'method', - generateLead: true, - generateLeadValueTrait: 'value', - generateLeadCurrencyTrait: 'currency', - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'add_to_cart', - params: { - items: [ - { - index: 1, - price: 19, - coupon: 'SUMMER_FUN', - item_id: '507f1f77bcf86cd799439011', - currency: 'USD', - discount: 2.22, - quantity: 2, - item_name: 'Monopoly: 3rd Edition', - item_brand: 'Google', - affiliation: 'Google Merchandise Store', - location_id: 'L_12345', - item_list_id: 'related_products', - item_variant: 'green', - item_category: 'Apparel', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_name: 'Related Products', - }, - ], - price: 2.4, - value: 4.8, - currency: 'USD', - quantity: 2, - session_id: 16678456735, - engagement_time_msec: 1, - client_id: 'client@1234', - }, - }, - ], - client_id: 'client_id', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) send integer userId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - userId: 34567, - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - sessionId: 16678456735, - }, - type: 'track', - event: 'product added', - properties: { - currency: 'USD', - price: 2.4, - quantity: 2, - client_id: 'client@1234', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - name: 'Monopoly: 3rd Edition', - coupon: 'SUMMER_FUN', - category: 'Apparel', - brand: 'Google', - variant: 'green', - price: '19', - quantity: '2', - position: '1', - affiliation: 'Google Merchandise Store', - currency: 'USD', - discount: 2.22, - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_id: 'related_products', - item_list_name: 'Related Products', - location_id: 'L_12345', - }, - ], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - blockPageViewEvent: false, - typesOfClient: 'gtag', - extendPageViewParams: false, - sendUserId: false, - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - enableServerSideIdentify: true, - sendLoginSignup: true, - newOrExistingUserTrait: 'firstLogin', - loginSignupMethod: 'method', - generateLead: true, - generateLeadValueTrait: 'value', - generateLeadCurrencyTrait: 'currency', - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'add_to_cart', - params: { - items: [ - { - index: 1, - price: 19, - coupon: 'SUMMER_FUN', - item_id: '507f1f77bcf86cd799439011', - currency: 'USD', - discount: 2.22, - quantity: 2, - item_name: 'Monopoly: 3rd Edition', - item_brand: 'Google', - affiliation: 'Google Merchandise Store', - location_id: 'L_12345', - item_list_id: 'related_products', - item_variant: 'green', - item_category: 'Apparel', - item_category2: 'Adult', - item_category3: 'Shirts', - item_category4: 'Crew', - item_category5: 'Short sleeve', - item_list_name: 'Related Products', - }, - ], - price: 2.4, - value: 4.8, - currency: 'USD', - quantity: 2, - session_id: 16678456735, - engagement_time_msec: 1, - client_id: 'client@1234', - }, - }, - ], - client_id: 'client_id', - user_id: '34567', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) login event with user_properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'login', - properties: { - method: 'facebook', - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'login', - params: { - method: 'facebook', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) sign_up event', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'sign_up', - properties: { - method: 'google', - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'sign_up', - params: { - method: 'google', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) generate_lead event', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'generate_lead', - properties: { - source: 'instagram', - value: 20, - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 20, - source: 'instagram', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) track call with page information such as url, title, referrer', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - page: { - initial_referrer: '$direct', - path: '/', - referrer: '$direct', - tab_url: 'https://www.rudderstack.com/', - title: 'Document', - url: 'https://www.rudderstack.com/', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'generate_lead', - properties: { - source: 'instagram', - value: 20, - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 20, - source: 'instagram', - page_location: 'https://www.rudderstack.com/', - page_referrer: '$direct', - page_title: 'Document', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '(gtag) track event with hybrid connection mode using buffer cloud mode event approach', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'generate_lead', - properties: { - source: 'instagram', - value: 20, - }, - integrations: { - All: true, - 'Google Analytics 4 (GA4)': { - clientId: '554581488.1683172875', - sessionId: '1683172875', - }, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'hybrid', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 20, - source: 'instagram', - session_id: '1683172875', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: '554581488.1683172875', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: - '(gtag) track event with hybrid connection mode using override client_id and session_id approach', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - sessionId: 1683172874065, - }, - type: 'track', - event: 'generate_lead', - properties: { - source: 'instagram', - value: 20, - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'hybrid', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'generate_lead', - params: { - currency: 'USD', - value: 20, - source: 'instagram', - session_id: 1683172874065, - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - user_properties: { - campaign: { - value: 'advertizing', - }, - }, - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) firing group calls with GA4 hybrid mode connection', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'group', - event: 'tutorial complete', - integrations: { - 'Google Analytics 4': { - clientId: '4718026.1683606287', - sessionId: '1683606287', - sessionNumber: 1, - }, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'hybrid', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://www.google-analytics.com/mp/collect', - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - body: { - JSON: { - non_personalized_ads: true, - client_id: '4718026.1683606287', - events: [ - { - name: 'join_group', - params: { - engagement_time_msec: 1, - session_id: '1683606287', - session_number: 1, - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) sign_up event with all data types of user_properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - traits: { - campaign: 'advertizing', - name: 'rudder', - age: 45, - hobby: ['dancing', 'singing', 'reading'], - enableEURegion: false, - isEnterpriseUser: { - value: false, - }, - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'sign_up', - properties: { - method: 'google', - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6BET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'sign_up', - params: { - method: 'google', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - user_properties: { - age: { - value: 45, - }, - name: { - value: 'rudder', - }, - campaign: { - value: 'advertizing', - }, - enableEURegion: { - value: false, - }, - }, - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '[Error]: (gtag) event name starts with numbers', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: '1234_sign_up', - properties: { - total: '10', - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'cloud', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: - 'Event name must start with a letter and can only contain letters, numbers, and underscores', - statTags: { - destType: 'GA4', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'ga4', - description: '(gtag) event having multiple empty array and object parameters', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: 'logIn', - userId: 'user@1', - group_id: 'group@1', - anon_id: '78e95d6d-58c0-4237-b99e-2ef510b6d502', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'ga4AppInstanceId', - id: 'f0dd99b6f979fb551ce583373900f937', - }, - { - type: 'ga4ClientId', - id: 'client_id', - }, - ], - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - properties: { - user_interest: 'Moderate', - company_interest: '', - profile: [ - { - is_6qa: true, - product: null, - product_fit: 'Moderate', - product_stage: 'Purchase', - intent_score: 89, - profile_score: 52, - product_display$name: 'rudderstack', - }, - ], - user_company: 'Analytics consulting', - user_account: '1', - user_id_mappings: '330098|245252|461224|282599', - company_naics_6sense: '5173', - usr_consent: null, - firebase_user_id: 'kdgMnP', - google_user_id: 'G-123456', - company_domain: 'consulting.net', - company_region: 'New Zealand', - user_product_interests: { - ids: [], - list: [ - { - id: 330098, - name: [], - }, - { - id: 245252, - name: {}, - }, - ], - names: [], - }, - company_country: {}, - company_industry: 'Business Analytics', - company_revenue: '$5M - $10M', - company_annual_revenue: '5568000', - company_sic_description: '', - company_naics_description: [], - }, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'dummyApiSecret', - measurementId: 'G-T40PE6KET4', - firebaseAppId: '', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - connectionMode: 'cloud', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'login', - params: { - company_annual_revenue: '5568000', - company_domain: 'consulting.net', - company_industry: 'Business Analytics', - company_naics_6sense: '5173', - company_region: 'New Zealand', - company_revenue: '$5M - $10M', - engagement_time_msec: 1, - profile_0_intent_score: 89, - profile_0_is_6qa: true, - profile_0_product_display$name: 'rudderstack', - profile_0_product_fit: 'Moderate', - profile_0_product_stage: 'Purchase', - profile_0_profile_score: 52, - user_account: '1', - user_company: 'Analytics consulting', - user_id_mappings: '330098|245252|461224|282599', - user_interest: 'Moderate', - user_product_interests_list_0_id: 330098, - user_product_interests_list_1_id: 245252, - }, - }, - ], - user_id: 'user@1', - client_id: 'client_id', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'dummyApiSecret', - measurement_id: 'G-T40PE6KET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, - { - name: 'ga4', - description: '(gtag) campaign_details custom event', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - anonymousId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - adTrackingEnabled: 'false', - advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', - id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', - manufacturer: 'Google', - model: 'AOSP on IA Emulator', - name: 'generic_x86_arm', - type: 'ios', - attTrackingStatus: 3, - }, - ip: '0.0.0.0', - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - locale: 'en-US', - os: { - name: 'iOS', - version: '14.4.1', - }, - screen: { - density: 2, - }, - campaign: { - id: 'google_1234', - name: 'Summer_fun', - source: 'google', - medium: 'cpc', - term: 'summer+travel', - content: 'logo link', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', - }, - type: 'track', - event: 'Campaign Details', - properties: {}, - integrations: { - All: true, - }, - }, - destination: { - Config: { - apiSecret: 'QyWKGHj8QhG2L4ePAPiXCA', - measurementId: 'G-T40PE6BET4', - typesOfClient: 'gtag', - eventFilteringOption: 'disable', - blacklistedEvents: [ - { - eventName: '', - }, - ], - whitelistedEvents: [ - { - eventName: '', - }, - ], - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: {}, - JSON: { - events: [ - { - name: 'campaign_details', - params: { - campaign_id: 'google_1234', - campaign: 'Summer_fun', - source: 'google', - medium: 'cpc', - term: 'summer+travel', - content: 'logo link', - engagement_time_msec: 1, - }, - }, - ], - user_id: 'user@1', - client_id: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - non_personalized_ads: true, - }, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: { - api_secret: 'QyWKGHj8QhG2L4ePAPiXCA', - measurement_id: 'G-T40PE6BET4', - }, - headers: { - HOST: 'www.google-analytics.com', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://www.google-analytics.com/mp/collect', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - mockFns: defaultMockFns, - }, -]; +import { existingTests } from './exisitngTests'; + +export const data = [...existingTests]; diff --git a/test/integrations/destinations/ga4/processor/ecomTestData.ts b/test/integrations/destinations/ga4/processor/ecomTestData.ts new file mode 100644 index 0000000000..238e44222b --- /dev/null +++ b/test/integrations/destinations/ga4/processor/ecomTestData.ts @@ -0,0 +1,2466 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedTrackPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const commonOutputHeaders = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const commonOutputParams = { + api_secret: 'dummyApiSecret', + measurement_id: 'dummyMeasurementId', +}; + +const rudderId = 'dummyRudderId'; +const anonymousId = 'dummyAnonId'; +const externalId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, +]; +const externalIdWithClientId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'dummyClientId', + }, +]; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; + +const timezone = { + name: 'Europe/Tallinn', +}; +const clientId = 'dummyClientId'; +const userId = 'default-user-id'; + +const coupon = 'SUMMER_FUN'; +const defaultCurrency = 'USD'; +const search_term = 'Clothing'; +const shipping_method = 'Ground'; +const payment_method = 'Credit Card'; +const list_id = 'related_products'; +const category = 'Related_products'; + +const value = 7.77; +const total = '7.77'; +const sessionId = 655; +const engagementTimeMsec = 100; +const non_personalized_ads = true; +const defaultEngagementTimeMsec = 1; +const timestamp_micros = 1650950229000000; + +const commonProductInfo = [ + { + product_id: '507f1f77bcf86cd799439011', + name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + category: 'Apparel', + brand: 'Google', + variant: 'green', + price: '19', + quantity: '2', + position: '1', + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, +]; + +const expectedOutputItems = [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: 19, + quantity: 2, + index: 1, + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, +]; + +const promotionEventsCommonProductInfo = [ + { + product_id: '507f1f77bcf86cd799439011', + name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + category: 'Apparel', + brand: 'Google', + variant: 'green', + price: '19', + quantity: '2', + position: '0', + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + creative_name: 'summer_banner2', + creative_slot: 'featured_app_1', + }, +]; + +const promotionEventsCommonParams = { + creative_name: 'Summer Banner', + creative_slot: 'featured_app_1', + location_id: 'L_12345', + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', +}; + +const promotionEventsExpectedOutputItems = [ + { + item_id: '507f1f77bcf86cd799439011', + item_name: 'Monopoly: 3rd Edition', + coupon: 'SUMMER_FUN', + item_category: 'Apparel', + item_brand: 'Google', + item_variant: 'green', + price: 19, + quantity: 2, + index: 0, + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + creative_name: 'summer_banner2', + creative_slot: 'featured_app_1', + }, +]; + +const promotionEventsExpectedOutputParams = { + creative_name: 'Summer Banner', + creative_slot: 'featured_app_1', + location_id: 'L_12345', + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + engagement_time_msec: defaultEngagementTimeMsec, +}; + +const orderEventsCommonParams = { + currency: defaultCurrency, + order_id: 'T_12345', + total: 12.21, + affiliation: 'Google Store', + coupon, + shipping: 3.33, + tax: 1.11, +}; + +const orderEventsExpectedOutputParams = { + currency: defaultCurrency, + transaction_id: 'T_12345', + value: 12.21, + affiliation: 'Google Store', + coupon, + shipping: 3.33, + tax: 1.11, +}; + +const shareProductsCommonParams = { + share_via: 'Twitter', + content_type: 'image', + item_id: 'C_12345', +}; + +const shareProductsExpectedOutputParams = { + method: 'Twitter', + content_type: 'image', + item_id: 'C_12345', +}; + +const eventEndPoint = 'https://www.google-analytics.com/mp/collect'; + +export const ecommTestData: ProcessorTestData[] = [ + { + id: 'ga4-ecom-test-1', + name: 'ga4', + description: 'Track event call for Products Searched event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product searched event properties and event name should be search', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Products Searched', + sentAt, + context: { + externalId, + }, + properties: { + query: 't-shirts', + }, + anonymousId, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: 'dummyAnonId', + timestamp_micros, + user_id: userId, + events: [ + { + name: 'search', + params: { + search_term: 't-shirts', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-2', + name: 'ga4', + description: 'Track event call for product list viewed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product list viewed event properties and event name should be view_item_list', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product list viewed', + rudderId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + list_id, + category, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: 'default-anonymousId', + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_item_list', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + item_list_id: 'related_products', + item_list_name: 'Related_products', + items: expectedOutputItems, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-3', + name: 'ga4', + description: 'Track event call for promotion viewed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all promotion viewed event properties and event name should be view_promotion', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion viewed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...promotionEventsCommonParams, + products: promotionEventsCommonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_promotion', + params: { + ...promotionEventsExpectedOutputParams, + items: promotionEventsExpectedOutputItems, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-4', + name: 'ga4', + description: 'Track event call for promotion clicked event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all promotion clicked event properties and event name should be select_promotion', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...promotionEventsCommonParams, + products: promotionEventsCommonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_promotion', + params: { + ...promotionEventsExpectedOutputParams, + items: promotionEventsExpectedOutputItems, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-5', + name: 'ga4', + description: + 'Track event call for promotion clicked event by excuding products from properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all promotion clicked event properties except products and event name should be select_promotion', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...promotionEventsCommonParams, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_promotion', + params: { + ...promotionEventsExpectedOutputParams, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-6', + name: 'ga4', + description: 'Track event call for product clicked event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product clicked event properties and event name should be select_item', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...commonProductInfo[0], + list_id, + category, + timezone, + sessionId, + engagementTimeMsec, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_item', + params: { + item_list_id: list_id, + item_list_name: category, + items: [{ ...expectedOutputItems[0], item_category: category }], + timezone_name: timezone.name, + engagement_time_msec: engagementTimeMsec, + session_id: sessionId, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-7', + name: 'ga4', + description: 'Track event call for product viewed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product viewed event properties and event name should be view_item', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product viewed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + ...commonProductInfo[0], + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_item', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-8', + name: 'ga4', + description: 'Track event call for product added event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product added event properties and event name should be add_to_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-9', + name: 'ga4', + description: 'Track event call for product removed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product removed event properties and event name should be remove_from_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product removed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + products: commonProductInfo, + sessionId, + engagementTimeMsec, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'remove_from_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: engagementTimeMsec, + session_id: sessionId, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-10', + name: 'ga4', + description: 'Track event call for cart viewed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all cart viewed event properties and event name should be view_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'cart viewed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-11', + name: 'ga4', + description: 'Track event call for checkout started event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all checkout started event properties and event name should be begin_checkout', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'checkout started', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + coupon, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'begin_checkout', + params: { + currency: defaultCurrency, + value, + coupon, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-12', + name: 'ga4', + description: 'Track event call for payment info entered event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all payment info entered event properties and event name should be add_payment_info', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'payment info entered', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + coupon, + payment_method, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_payment_info', + params: { + currency: defaultCurrency, + value, + coupon, + payment_type: payment_method, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-13', + name: 'ga4', + description: 'Track event call for Checkout Step Completed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all Checkout Step Completed event properties and event name should be add_shipping_info', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Checkout Step Completed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + coupon, + shipping_method, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_shipping_info', + params: { + currency: defaultCurrency, + value, + coupon, + shipping_tier: shipping_method, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-14', + name: 'ga4', + description: 'Track event call for order completed event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all order completed event properties and event name should be purchase', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'order completed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...orderEventsCommonParams, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'purchase', + params: { + ...orderEventsExpectedOutputParams, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-15', + name: 'ga4', + description: 'Track event call for order refunded event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all order refunded event properties and event name should be refund', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'order refunded', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...orderEventsCommonParams, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'refund', + params: { + ...orderEventsExpectedOutputParams, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-16', + name: 'ga4', + description: 'Track event call for order refunded event and exclude products from properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all order refunded event properties except products and event name should be refund', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'order refunded', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...orderEventsCommonParams, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'refund', + params: { + ...orderEventsExpectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-17', + name: 'ga4', + description: 'Track event call for product added to wishlist event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product added to wishlist event properties and event name should be add_to_wishlist', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added to wishlist', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + currency: defaultCurrency, + total, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_wishlist', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-18', + name: 'ga4', + description: 'Track event call for product_shared event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all product_shared event properties and event name should be share', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product_shared', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: shareProductsCommonParams, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'share', + params: { + ...shareProductsExpectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-19', + name: 'ga4', + description: 'Track event call for product_shared event without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no product_shared event properties and event name should be share', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product_shared', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'share', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-20', + name: 'ga4', + description: 'Track event call for cart Shared event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all cart Shared event properties and event name should be share', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'cart Shared', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: shareProductsCommonParams, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'share', + params: { + ...shareProductsExpectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-21', + name: 'ga4', + description: 'Track event call for cart Shared event without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no cart Shared event properties and event name should be share', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'cart Shared', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'share', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-22', + name: 'ga4', + description: + 'Track event call for promotion clicked event with all event properties and type of client is firebase', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all promotion clicked event properties and event name should be select_promotion and response should contain all firebase related params', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { + ...destination.Config, + typesOfClient: 'firebase', + firebaseAppId: 'dummyFirebaseAppId', + }, + }, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + ...promotionEventsCommonParams, + products: promotionEventsCommonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: { + api_secret: 'dummyApiSecret', + firebase_app_id: 'dummyFirebaseAppId', + }, + JSON: { + app_instance_id: 'dummyAppInstanceId', + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_promotion', + params: { + ...promotionEventsExpectedOutputParams, + items: promotionEventsExpectedOutputItems, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-23', + name: 'ga4', + description: 'Track event call for view_search_results event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all view_search_results event properties and event name should be view_search_results', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'view_search_results', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + search_term, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_search_results', + params: { + search_term, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-24', + name: 'ga4', + description: 'Track event call for view_search_results event with only products as properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain only products as view_search_results event properties and event name should be view_search_results', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'view_search_results', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'view_search_results', + params: { + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-25', + name: 'ga4', + description: 'Track event call for product added event with products information at root level', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain items array with product information and event name should be add_to_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + ...commonProductInfo[0], + products: [], + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-26', + name: 'ga4', + description: 'Track event call for product added event with products as an object', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain items array with product information and event name should be add_to_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + ...commonProductInfo[0], + products: commonProductInfo[0], + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + currency: defaultCurrency, + value, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-27', + name: 'ga4', + description: + 'Scenario to test custom properties along with products: [..] parameters to GA4 standard events along with its stated ones', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain both custom and standard properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'order completed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + revenue: 15, + discount: 1.5, + checkout_id: '12345', + myCustomProp: 'My arbitray value', + ...orderEventsCommonParams, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'purchase', + params: { + affiliation: 'Google Store', + checkout_id: '12345', + coupon: 'SUMMER_FUN', + discount: 1.5, + currency: defaultCurrency, + myCustomProp: 'My arbitray value', + shipping: 3.33, + tax: 1.11, + transaction_id: 'T_12345', + value: 12.21, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-28', + name: 'ga4', + description: + 'Scenario to test custom properties excluding products: [..] parameter to GA4 standard events along with its stated ones', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain both custom and standard properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'promotion clicked', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + timezone, + promotion_page: '/products', + promotion_channel: 'facebook', + ...promotionEventsCommonParams, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_promotion', + params: { + promotion_page: '/products', + promotion_channel: 'facebook', + timezone_name: 'Europe/Tallinn', + ...promotionEventsExpectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-29', + name: 'ga4', + description: 'Scenario to test price x currency multiplication for product added event', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain value calculated from price x currency and event name should be add_to_cart', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + price: 2.4, + quantity: 2, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + price: 2.4, + quantity: 2, + value: 4.8, + currency: defaultCurrency, + items: expectedOutputItems, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-ecom-test-30', + name: 'ga4', + description: + 'Scenario to test any custom or item property with array value, is flattened with underscore delimeter', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and all the nested properties should get flattened in final payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + ...commonProductInfo[0], + address: { + city: 'kolkata', + district: '24pgs', + }, + categoryLevels: ['Furniture', 'Bedroom Furniture', 'Dressers & Chests'], + products: [ + { + product_id: '1234', + product_details: { + colour: 'red', + shape: 'rectangle', + }, + productLevels: ['test1', 'test2', 'test3'], + }, + ], + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'add_to_cart', + params: { + value: 38, + ...commonProductInfo[0], + address_city: 'kolkata', + address_district: '24pgs', + categoryLevels_0: 'Furniture', + categoryLevels_1: 'Bedroom Furniture', + categoryLevels_2: 'Dressers & Chests', + items: [ + { + item_id: '1234', + productLevels_0: 'test1', + productLevels_1: 'test2', + productLevels_2: 'test3', + product_details_colour: 'red', + product_details_shape: 'rectangle', + }, + ], + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4/processor/exisitngTests.ts b/test/integrations/destinations/ga4/processor/exisitngTests.ts new file mode 100644 index 0000000000..2913004ca6 --- /dev/null +++ b/test/integrations/destinations/ga4/processor/exisitngTests.ts @@ -0,0 +1,13 @@ +import { pageTestData } from './pageTestData'; +import { ecommTestData } from './ecomTestData'; +import { trackTestData } from './trackTestData'; +import { groupTestData } from './groupTestData'; +import { validationTestData } from './validationTestData'; + +export const existingTests = [ + ...pageTestData, + ...trackTestData, + ...ecommTestData, + ...groupTestData, + ...validationTestData, +]; diff --git a/test/integrations/destinations/ga4/processor/groupTestData.ts b/test/integrations/destinations/ga4/processor/groupTestData.ts new file mode 100644 index 0000000000..68daad3a8e --- /dev/null +++ b/test/integrations/destinations/ga4/processor/groupTestData.ts @@ -0,0 +1,306 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder, generateGroupPayload } from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const commonOutputHeaders = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const commonOutputParams = { + api_secret: 'dummyApiSecret', + measurement_id: 'dummyMeasurementId', +}; + +const anonymousId = 'dummyAnonId'; +const externalIdWithClientId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'dummyClientId', + }, +]; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; +const timezone = { + name: 'Europe/Tallinn', +}; +const clientId = 'dummyClientId'; +const userId = 'default-user-id'; + +const sessionId = 655; +const engagementTimeMsec = 100; +const non_personalized_ads = true; +const defaultEngagementTimeMsec = 1; +const timestamp_micros = 1650950229000000; + +const eventEndPoint = 'https://www.google-analytics.com/mp/collect'; + +export const groupTestData: ProcessorTestData[] = [ + { + id: 'ga4-group-test-1', + name: 'ga4', + description: 'Group event call with event name', + scenario: 'Business', + successCriteria: 'Response status code should be 200 and event name should be join_group', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateGroupPayload({ + type: 'group', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'join_group', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-group-test-2', + name: 'ga4', + description: 'Group event call with event name and properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and event payload should contain all properties and event name should be join_group', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateGroupPayload({ + type: 'group', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + traits: { + org: 'rudderlabs', + sector: 'cdp', + timezone, + engagementTimeMsec, + sessionId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'join_group', + params: { + sector: 'cdp', + org: 'rudderlabs', + timezone_name: timezone.name, + session_id: sessionId, + engagement_time_msec: engagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-group-test-3', + name: 'ga4', + description: 'Scenario to test firing group calls with GA4 hybrid mode connection', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should containe track event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, connectionMode: 'hybrid' }, + }, + message: { + type: 'group', + event: 'tutorial complete', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + sessionId: 1683172874065, + }, + integrations: { + 'Google Analytics 4': { + clientId: '4718026.1683606287', + sessionId: '1683606287', + sessionNumber: 1, + }, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: '4718026.1683606287', + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'join_group', + params: { + session_id: '1683606287', + session_number: 1, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4/processor/pageTestData.ts b/test/integrations/destinations/ga4/processor/pageTestData.ts new file mode 100644 index 0000000000..fa0b187aea --- /dev/null +++ b/test/integrations/destinations/ga4/processor/pageTestData.ts @@ -0,0 +1,304 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generatePageOrScreenPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const commonOutputHeaders = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const commonOutputParams = { + api_secret: 'dummyApiSecret', + measurement_id: 'dummyMeasurementId', +}; + +const anonymousId = 'dummyAnonId'; +const externalId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, +]; +const page = { + url: 'http://morkey.in', + path: '/cart', + title: 'miphone', + search: 'MI', + referrer: 'morkey', +}; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; + +const userId = 'default-userId'; +const non_personalized_ads = true; +const defaultEngagementTimeMsec = 1; +const timestamp_micros = 1650950229000000; + +const expectedOutputParams = { + page_title: 'miphone', + page_referrer: 'morkey', + page_location: 'http://morkey.in', +}; + +const eventEndPoint = 'https://www.google-analytics.com/mp/collect'; + +export const pageTestData: ProcessorTestData[] = [ + { + id: 'ga4-page-test-1', + name: 'ga4', + description: 'Scenario to test page call', + scenario: 'Business', + successCriteria: 'Response status code should be 200 and event name should be page_view', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generatePageOrScreenPayload( + { + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + originalTimestamp, + }, + 'page', + ), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'page_view', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-page-test-2', + name: 'ga4', + description: 'Scenario to test page call with custom properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and event payload should contain all custom properties event name should be page_view', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generatePageOrScreenPayload( + { + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + view: 'login', + }, + originalTimestamp, + }, + 'page', + ), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'page_view', + params: { + view: 'login', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-page-test-3', + name: 'ga4', + description: + 'Scenario to test take page properties from context.page for page call along with custom properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and event payload should contain all custom properties event name should be page_view', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generatePageOrScreenPayload( + { + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + page, + }, + properties: { + id: 'dummyId', + }, + originalTimestamp, + }, + 'page', + ), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'page_view', + params: { + id: 'dummyId', + ...expectedOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4/processor/trackTestData.ts b/test/integrations/destinations/ga4/processor/trackTestData.ts new file mode 100644 index 0000000000..ce991ff845 --- /dev/null +++ b/test/integrations/destinations/ga4/processor/trackTestData.ts @@ -0,0 +1,2951 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedTrackPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const commonOutputHeaders = { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', +}; + +const commonOutputParams = { + api_secret: 'dummyApiSecret', + measurement_id: 'dummyMeasurementId', +}; + +const anonymousId = 'dummyAnonId'; +const externalId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, +]; +const externalIdWithClientId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'dummyClientId', + }, +]; +const page = { + initial_referrer: '$direct', + path: '/', + referrer: '$direct', + tab_url: 'https://www.rudderstack.com/', + title: 'Document', + url: 'https://www.rudderstack.com/', +}; +const campaign = { + id: 'google_1234', + name: 'Summer_fun', + source: 'google', + medium: 'cpc', + term: 'summer+travel', + content: 'logo link', +}; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; + +const clientId = 'dummyClientId'; +const userId = 'default-user-id'; +const groupId = 'dummyGroupId'; +const defaultCurrency = 'USD'; +const value = 7.77; +const total = 10; +const sessionId = 655; +const engagementTimeMsec = 100; +const non_personalized_ads = true; +const defaultEngagementTimeMsec = 1; +const timestamp_micros = 1650950229000000; +const timezone = { + name: 'Europe/Tallinn', +}; + +const expectedCampaignOutputParams = { + campaign_id: 'google_1234', + campaign: 'Summer_fun', + source: 'google', + medium: 'cpc', + term: 'summer+travel', + content: 'logo link', +}; + +const eventEndPoint = 'https://www.google-analytics.com/mp/collect'; + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'ga4-track-test-1', + name: 'ga4', + description: 'Track event call for earn virtual currency event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all earn virtual currency event properties and event name should be earn_virtual_currency', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'earn virtual currency', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + value, + virtual_currency_name: 'Gems', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'earn_virtual_currency', + params: { + value, + virtual_currency_name: 'Gems', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-2', + name: 'ga4', + description: 'Track event call for earn virtual currency without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no earn virtual currency event properties and event name should be earn_virtual_currency', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'earn virtual currency', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'earn_virtual_currency', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-3', + name: 'ga4', + description: 'Track event call for generate_lead event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all generate_lead event properties and event name should be generate_lead', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'generate_lead', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + value, + currency: defaultCurrency, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'generate_lead', + params: { + value, + currency: defaultCurrency, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-4', + name: 'ga4', + description: 'Track event call for level_up event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all level_up event properties and event name should be level_up', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'level_up', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + level: 5, + character: 'Player 1', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'level_up', + params: { + level: 5, + character: 'Player 1', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-5', + name: 'ga4', + description: 'Track event call for level_up without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no level_up event properties and event name should be level_up', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'level_up', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'level_up', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-6', + name: 'ga4', + description: 'Track event call for group event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all group event properties and event name should be group', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'group', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + group_id: groupId, + engagementTimeMsec, + sessionId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'group', + params: { + group_id: groupId, + session_id: sessionId, + engagement_time_msec: engagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-7', + name: 'ga4', + description: 'Track event call for group without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no group event properties and event name should be group', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'group', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'group', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-8', + name: 'ga4', + description: 'Track event call for login event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all login event properties and event name should be login', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'login', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + method: 'Google', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'login', + params: { + method: 'Google', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-9', + name: 'ga4', + description: 'Track event call for login without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no login event properties and event name should be login', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'login', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'login', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-10', + name: 'ga4', + description: 'Track event call for post_score event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all post_score event properties and event name should be post_score', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'post_score', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + score: 10000, + level: 5, + character: 'Player 1', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'post_score', + params: { + score: 10000, + level: 5, + character: 'Player 1', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-11', + name: 'ga4', + description: 'Track event call for post_score event with only required event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain only required post_score event properties and event name should be post_score', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'post_score', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + score: 10000, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'post_score', + params: { + score: 10000, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-12', + name: 'ga4', + description: 'Track event call for select_content event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all select_content event properties and event name should be select_content', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'select_content', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + item_id: 'I_12345', + content_type: 'product', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_content', + params: { + item_id: 'I_12345', + content_type: 'product', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-13', + name: 'ga4', + description: 'Track event call for select_content without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no select_content event properties and event name should be select_content', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'select_content', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'select_content', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-14', + name: 'ga4', + description: 'Track event call for sign_up event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all sign_up event properties and event name should be sign_up', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'sign_up', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + method: 'Google', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'sign_up', + params: { + method: 'Google', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-15', + name: 'ga4', + description: 'Track event call for sign_up without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no sign_up event properties and event name should be sign_up', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'sign_up', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'sign_up', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-16', + name: 'ga4', + description: 'Track event call for spend_virtual_currency event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all spend_virtual_currency event properties and event name should be spend_virtual_currency', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'spend_virtual_currency', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + value: 5, + item_name: 'Starter Boost', + virtual_currency_name: 'Gems', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'spend_virtual_currency', + params: { + value: 5, + item_name: 'Starter Boost', + virtual_currency_name: 'Gems', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-17', + name: 'ga4', + description: 'Track event call for spend_virtual_currency without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no spend_virtual_currency event properties and event name should be spend_virtual_currency', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'spend_virtual_currency', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'spend_virtual_currency', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-18', + name: 'ga4', + description: 'Track event call for tutorial_begin without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no tutorial_begin event properties and event name should be tutorial_begin', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutorial_begin', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutorial_begin', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-19', + name: 'ga4', + description: 'Track event call for tutorial_complete without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no tutorial_complete event properties and event name should be tutorial_complete', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutorial_complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-20', + name: 'ga4', + description: 'Track event call for unlock_achievement event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all unlock_achievement event properties and event name should be unlock_achievement', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'unlock_achievement', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + achievement_id: 'A_12345', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'unlock_achievement', + params: { + achievement_id: 'A_12345', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-21', + name: 'ga4', + description: 'Track event call for rudderstack event event with all event properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all rudderstack event event properties and event name should be rudderstack_event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + timezone, + engagementTimeMsec, + sessionId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'rudderstack_event', + params: { + total, + timezone_name: timezone.name, + session_id: sessionId, + engagement_time_msec: engagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-22', + name: 'ga4', + description: + 'Track event call for rudderstack event event with all event properties and user properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all rudderstack event event properties and user properties and event name should be rudderstack_event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + user_properties: { + price: '19', + }, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + user_properties: { + price: { + value: '19', + }, + }, + events: [ + { + name: 'rudderstack_event', + params: { + total, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-23', + name: 'ga4', + description: 'Scenario to test custom events by passing reserved property names in payload', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all custom event properties and reserved properties should gets filtered from transformed payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + firebase_conversion: 'firebase_conversion', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'rudderstack_event', + params: { + total, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-24', + name: 'ga4', + description: + 'Scenario to test custom events by passing reserved properties and reserved user properties in payload', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain all custom event properties and reserved properties and reserved user properties should gets filtered from transformed payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total, + firebase_conversion: 'firebase_conversion', + user_properties: { + first_open_time: 'first_open_time', + user_id: userId, + firebase_value: 'firebase_value', + }, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'rudderstack_event', + params: { + total, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-25', + name: 'ga4', + description: 'Track event call for custom event without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no custom event properties and event name should be custom event name', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'rudderstack event', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'rudderstack_event', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-26', + name: 'ga4', + description: 'Track event call for taking client_id from anonymousId', + scenario: 'Business', + successCriteria: 'Response status code should be 200 and client_id should be anonymousId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutotial_complete', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-27', + name: 'ga4', + description: 'Track event call for tutorial_complete without properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, event payload should contain no tutorial_complete event properties and event name should be tutorial_complete', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutorial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: clientId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-28', + name: 'ga4', + description: + 'Scenario to test client_id is not sent from the path defined in the webapp config, falling back to default values i.e here it is anonymousId', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, client_id value should be equal to anonymousId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutorial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-29', + name: 'ga4', + description: 'Scenario to test extract session_id from context.sessionId', + scenario: 'Business', + successCriteria: + 'Response status code should be 200, session_id value should be equal to context.sessionId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'tutorial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + sessionId: 16678456735, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + session_id: 16678456735, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-30', + name: 'ga4', + description: 'Scenario to test integer userId', + scenario: 'Business', + successCriteria: 'Response status code should be 200, user_id value should get stringified', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'tutorial complete', + userId: 1234, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + sessionId: 16678456735, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: '1234', + non_personalized_ads, + events: [ + { + name: 'tutorial_complete', + params: { + session_id: 16678456735, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-31', + name: 'ga4', + description: 'Scenario to test login event with user_properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain all user_properties passed with payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'login', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + traits: { + campaign: 'advertizing', + }, + }, + properties: { + method: 'facebook', + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + user_properties: { + campaign: { + value: 'advertizing', + }, + }, + events: [ + { + name: 'login', + params: { + method: 'facebook', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-32', + name: 'ga4', + description: 'Scenario to test track call with page information such as url, title, referrer', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain all page related parameters', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'generate_lead', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + page, + }, + properties: { + value, + source: 'instagram', + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'generate_lead', + params: { + value, + currency: 'USD', + source: 'instagram', + page_location: 'https://www.rudderstack.com/', + page_referrer: '$direct', + page_title: 'Document', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-33', + name: 'ga4', + description: + 'Scenario to test track event with hybrid connection mode using buffer cloud mode event approach', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain track event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, connectionMode: 'hybrid' }, + }, + message: { + type: 'track', + event: 'generate_lead', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + value, + source: 'instagram', + }, + integrations: { + All: true, + 'Google Analytics 4 (GA4)': { + clientId: '554581488.1683172875', + sessionId: '1683172875', + }, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: '554581488.1683172875', + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'generate_lead', + params: { + value, + currency: 'USD', + source: 'instagram', + session_id: '1683172875', + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-34', + name: 'ga4', + description: + 'Scenario to test track event with hybrid connection mode using override client_id and session_id approach', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain track event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, connectionMode: 'hybrid' }, + }, + message: { + type: 'track', + event: 'generate_lead', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + sessionId: 1683172874065, + }, + properties: { + value, + source: 'instagram', + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'generate_lead', + params: { + value, + currency: 'USD', + source: 'instagram', + session_id: 1683172874065, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-35', + name: 'ga4', + description: 'Scenario to test sign_up event with all data types of user_properties', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain all user_properties sent in payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, connectionMode: 'hybrid' }, + }, + message: { + type: 'track', + event: 'sign_up', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + sessionId: 1683172874065, + traits: { + campaign: 'advertizing', + name: 'rudder', + age: 45, + hobby: ['dancing', 'singing', 'reading'], + enableEURegion: false, + isEnterpriseUser: { + value: false, + }, + }, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + user_properties: { + age: { + value: 45, + }, + name: { + value: 'rudder', + }, + campaign: { + value: 'advertizing', + }, + enableEURegion: { + value: false, + }, + }, + events: [ + { + name: 'sign_up', + params: { + session_id: 1683172874065, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-36', + name: 'ga4', + description: 'Scenario to test event having multiple empty array and object parameters', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and in final response empty array and object should gets filtered out', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'login', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + properties: { + user_interest: 'Moderate', + company_interest: '', + profile: [ + { + is_6qa: true, + product: null, + product_fit: 'Moderate', + product_stage: 'Purchase', + intent_score: 89, + profile_score: 52, + product_display$name: 'rudderstack', + }, + ], + user_company: 'Analytics consulting', + user_account: '1', + user_id_mappings: '330098|245252|461224|282599', + company_naics_6sense: '5173', + usr_consent: null, + firebase_user_id: 'kdgMnP', + google_user_id: 'G-123456', + company_domain: 'consulting.net', + company_region: 'New Zealand', + user_product_interests: { + ids: [], + list: [ + { + id: 330098, + name: [], + }, + { + id: 245252, + name: {}, + }, + ], + names: [], + }, + company_country: {}, + company_industry: 'Business Analytics', + company_revenue: '$5M - $10M', + company_annual_revenue: '5568000', + company_sic_description: '', + company_naics_description: [], + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'login', + params: { + company_annual_revenue: '5568000', + company_domain: 'consulting.net', + company_industry: 'Business Analytics', + company_naics_6sense: '5173', + company_region: 'New Zealand', + company_revenue: '$5M - $10M', + engagement_time_msec: defaultEngagementTimeMsec, + profile_0_intent_score: 89, + profile_0_is_6qa: true, + profile_0_product_display$name: 'rudderstack', + profile_0_product_fit: 'Moderate', + profile_0_product_stage: 'Purchase', + profile_0_profile_score: 52, + user_account: '1', + user_company: 'Analytics consulting', + user_id_mappings: '330098|245252|461224|282599', + user_interest: 'Moderate', + user_product_interests_list_0_id: 330098, + user_product_interests_list_1_id: 245252, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-37', + name: 'ga4', + description: "Teack event call with campaign_details custom event and it's properties", + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and resonse should contain all campaign_details event properties and event name should be campaign_details', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'campaign_details', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + campaign, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + events: [ + { + name: 'campaign_details', + params: { + ...expectedCampaignOutputParams, + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-38', + name: 'ga4', + description: 'Send consents setting to ga4 with login event', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain all consent settings', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'login', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + campaign, + }, + originalTimestamp, + integrations: { + All: true, + GA4: { + consents: { + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }, + }, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + consent: { + ad_personalization: 'GRANTED', + ad_user_data: 'GRANTED', + }, + events: [ + { + name: 'login', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-track-test-39', + name: 'ga4', + description: 'Send invalid consents settings to ga4 with login event', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain only valid consent setting', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'login', + userId, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId, + campaign, + }, + originalTimestamp, + integrations: { + All: true, + GA4: { + consents: { + ad_personalization: 'NOT_SPECIFIED', + ad_user_data: 'DENIED', + }, + }, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + params: commonOutputParams, + JSON: { + client_id: anonymousId, + timestamp_micros, + user_id: userId, + non_personalized_ads, + consent: { + ad_user_data: 'DENIED', + }, + events: [ + { + name: 'login', + params: { + engagement_time_msec: defaultEngagementTimeMsec, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4/processor/validationTestData.ts b/test/integrations/destinations/ga4/processor/validationTestData.ts new file mode 100644 index 0000000000..4e5d09bfcf --- /dev/null +++ b/test/integrations/destinations/ga4/processor/validationTestData.ts @@ -0,0 +1,947 @@ +import { defaultMockFns } from '../mocks'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, generateSimplifiedTrackPayload } from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'GA4', + DestinationDefinition: { + ID: '123', + Name: 'GA4', + DisplayName: 'Google Analytics 4 (GA4)', + Config: {}, + }, + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'dummyMeasurementId', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + blacklistedEvents: [ + { + eventName: '', + }, + ], + whitelistedEvents: [ + { + eventName: '', + }, + ], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const deviceInfo = { + adTrackingEnabled: 'false', + advertisingId: 'dummyAdvertisingId', + id: 'device@1', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const anonymousId = 'dummyAnonId'; +const userId = 'default-user-id'; +const externalId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, +]; +const externalIdWithClientId = [ + { + type: 'ga4AppInstanceId', + id: 'dummyAppInstanceId', + }, + { + type: 'ga4ClientId', + id: 'dummyClientId', + }, +]; +const sentAt = '2022-04-20T15:20:57Z'; +const originalTimestamp = '2022-04-26T05:17:09Z'; + +const commonProductInfo = [ + { + coupon: 'SUMMER_FUN', + category: 'Apparel', + brand: 'Google', + variant: 'green', + price: '19', + quantity: '2', + position: '1', + affiliation: 'Google Merchandise Store', + currency: 'USD', + discount: 2.22, + item_category2: 'Adult', + item_category3: 'Shirts', + item_category4: 'Crew', + item_category5: 'Short sleeve', + item_list_id: 'related_products', + item_list_name: 'Related Products', + location_id: 'L_12345', + }, +]; + +const reservedEventsErrorMessage = 'track:: Reserved event names are not allowed'; +const expectedStatTags = { + destType: 'GA4', + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'ga4-validation-test-1', + name: 'ga4', + description: 'Scenario to test reserved event name to GA4', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'ad_click', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total: 10, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: reservedEventsErrorMessage, + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-2', + name: 'ga4', + description: 'Scenario to test reserved event name along with reserved event properties to GA4', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'app_remove', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + firebase_conversion: 'firebase_conversion', + google_id: 'ga_id', + ga_value: 'ga_value', + value: 10, + user_properties: { + first_open_time: 'first_open_time', + user_id: userId, + firebase_value: 'firebase_value', + }, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: reservedEventsErrorMessage, + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-3', + name: 'ga4', + description: 'Scenario to test reserved prefix names to GA4', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'firebase_event1', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + value: 10, + google_id: 'ga_id', + ga_value: 'ga_value', + firebase_conversion: 'firebase_conversion', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Reserved custom prefix names are not allowed', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-4', + name: 'ga4', + description: + 'Scenario to pass event name to GA4 with missing fields i.e required in products: [..]', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'product added', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total: 7.77, + products: commonProductInfo, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'One of product_id or name is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-5', + name: 'ga4', + description: + 'Scenario to pass event name to GA4 with missing fields i.e required in products: [..]', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'Product Viewed', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + properties: { + total: 7.77, + currency: 'USD', + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'One of product_id or name is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-6', + name: 'ga4', + description: 'Scenario to test missing API Secret', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, apiSecret: null }, + Enabled: true, + }, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'API Secret not found. Aborting ', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-7', + name: 'ga4', + description: 'Scenario to test missing measurementId', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, measurementId: null }, + Enabled: true, + }, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'measurementId must be provided. Aborting', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-8', + name: 'ga4', + description: 'Scenario to test missing message type', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Message Type is not present. Aborting message.', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-9', + name: 'ga4', + description: 'Scenario to test missing message event name', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event name is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-10', + name: 'ga4', + description: 'Scenario to test missing ga4AppInstanceId for firebase client type', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { + ...destination.Config, + typesOfClient: 'firebase', + firebaseAppId: 'dummyFirebaseAppId', + }, + }, + message: { + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'ga4AppInstanceId must be provided under externalId', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-11', + name: 'ga4', + description: 'Scenario to test reserved event name for firebase client type', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { + ...destination.Config, + typesOfClient: 'firebase', + firebaseAppId: 'dummyFirebaseAppId', + }, + }, + message: { + type: 'track', + event: 'app_store_subscription_cancel', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Reserved custom event names are not allowed', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-12', + name: 'ga4', + description: 'Scenario to test missing firebaseAppId for firebase client type', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { + ...destination.Config, + typesOfClient: 'firebase', + firebaseAppId: null, + }, + }, + message: { + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'firebaseAppId must be provided. Aborting', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'ga4-validation-test-13', + name: 'ga4', + description: 'Scenario to test timestamp more than 72 hours into the past', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp: '2022-04-20T05:17:09Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Allowed timestamp is [72 hours] into the past', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'ga4-validation-test-14', + name: 'ga4', + description: 'Scenario to test timestamp more than 15 min into the future', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'tutotial complete', + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp: '2022-05-05T15:47:57Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Allowed timestamp is [15 minutes] into the future', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'ga4-validation-test-15', + name: 'ga4', + description: 'Scenario to test event value other then string', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: { + name: 'promotion_viewed', + }, + properties: { + products: commonProductInfo, + }, + anonymousId, + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp: '2022-05-05T15:47:57Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'track:: event name should be string', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'ga4-validation-test-16', + name: 'ga4', + description: 'Scenario to test client_id not found in payload', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'product added', + properties: { + products: commonProductInfo, + }, + sentAt, + context: { + device: deviceInfo, + externalId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'ga4ClientId, anonymousId or messageId must be provided', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'ga4-validation-test-17', + name: 'ga4', + description: 'Scenario to test event name starts with numbers', + scenario: 'Business', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: '1234_sign_up', + sentAt, + context: { + device: deviceInfo, + externalId: externalIdWithClientId, + }, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Event name must start with a letter and can only contain letters, numbers, and underscores', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/ga4_v2/mocks.ts b/test/integrations/destinations/ga4_v2/mocks.ts new file mode 100644 index 0000000000..3a27349ff7 --- /dev/null +++ b/test/integrations/destinations/ga4_v2/mocks.ts @@ -0,0 +1,5 @@ +export const defaultMockFns = () => { + return jest + .spyOn(Date, 'now') + .mockImplementation(() => new Date('2022-04-29T05:17:09Z').valueOf()); +}; diff --git a/test/integrations/destinations/ga4_v2/processor/customMappings.ts b/test/integrations/destinations/ga4_v2/processor/customMappings.ts new file mode 100644 index 0000000000..b1db2121ea --- /dev/null +++ b/test/integrations/destinations/ga4_v2/processor/customMappings.ts @@ -0,0 +1,721 @@ +import { defaultMockFns } from '../mocks'; + +const traits = { + firstName: 'John', + lastName: 'Gomes', + city: 'London', + state: 'UK', + streetAddress: '71 Cherry Court SOUTHAMPTON SO53 5PD UK', + group: 'test group', +}; + +const device = { + adTrackingEnabled: 'true', + advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', + id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, +}; + +const properties = { + list_id: 'random_list_id', + category: 'random_category', + storePrice: 456, + prices: [ + { + id: 'store-price', + value: 456, + }, + { + id: 'desk-price', + value: 567, + }, + ], + products: [ + { + product_id: 883213, + name: 'Salt', + coupon: 'HHH', + price: 100, + position: 1, + quantity: 10, + affiliation: 'NADA', + currency: 'INR', + discount: '2%', + item_category3: 'grocery', + }, + { + product_id: 213123, + name: 'Sugar', + coupon: 'III', + price: 200, + position: 2, + quantity: 20, + affiliation: 'ADNA', + currency: 'INR', + discount: '5%', + item_category2: 'regulars', + item_category3: 'grocery', + some_data: 'someValue', + }, + ], +}; + +const integrations = { + GA4: { + consents: { + ad_personalization: 'GRANTED', + ad_user_data: 'DENIED', + }, + }, +}; + +const eventsMapping = [ + { + rsEventName: 'Product List Viewed', + destEventName: 'view_item_list', + eventProperties: [ + { + to: '$.client_id', + from: '$.context.traits.anonymousId', + }, + { + to: '$.events[0].params.items[*].name', + from: '$.properties.products[*].name', + }, + { + to: '$.events[0].params.prices', + from: '$.properties.storePrice', + }, + { + to: '$.events[0].params.items[*].id', + from: '$.properties.products[*].product_id', + }, + { + to: '$.events[0].params.items[*].key', + from: '$.properties.products[*].some_data', + }, + { + to: '$.events[0].params.items[*].list_id', + from: '$.properties.list_id', + }, + { + to: '$.userProperties.firstName.value', + from: '$.context.traits.firstName', + }, + { + to: '$.userProperties.lastName.value', + from: '$.context.traits.lastName', + }, + ], + }, + { + rsEventName: 'Product Added', + destEventName: 'add_to_cart', + eventProperties: [ + { + to: '$.client_id', + from: '$.context.traits.anonymousId', + }, + { + to: '$.events[0].params.items[*].name', + from: '$.properties.products[*].name', + }, + { + to: '$.events[0].params.prices', + from: '$.properties.storePrice', + }, + { + to: '$.events[0].params.items[*].id', + from: '$.properties.products[*].product_id', + }, + { + to: '$.events[0].params.items[*].key', + from: '$.properties.products[*].some_data', + }, + { + to: '$.events[0].params.items[*].list_id', + from: '$.properties.list_id', + }, + { + to: '$.userProperties.firstName.value', + from: '$.context.traits.firstName', + }, + { + to: '$.userProperties.lastName.value', + from: '$.context.traits.lastName', + }, + ], + }, + { + rsEventName: 'Product Added', + destEventName: 'checkout_started', + eventProperties: [ + { + to: '$.client_id', + from: '$.context.traits.anonymousId', + }, + { + to: '$.events[0].params.items[*].name', + from: '$.properties.products[*].name', + }, + { + to: '$.events[0].params.prices', + from: '$.properties.storePrice', + }, + { + to: '$.events[0].params.items[*].id', + from: '$.properties.products[*].product_id', + }, + { + to: '$.events[0].params.items[*].key', + from: '$.properties.products[*].some_data', + }, + { + to: '$.events[0].params.items[*].list_id', + from: '$.properties.list_id', + }, + { + to: '$.userProperties.firstName.value', + from: '$.context.traits.firstName', + }, + { + to: '$.userProperties.lastName.value', + from: '$.context.traits.lastName', + }, + ], + }, + { + rsEventName: '$group', + destEventName: 'join_group', + eventProperties: [ + { + to: '$.client_id', + from: '$.context.traits.anonymousId', + }, + { + to: '$.events[0].params.group_id', + from: '$.context.traits.group_id', + }, + { + to: '$.userProperties.firstName.value', + from: '$.context.traits.firstName', + }, + { + to: '$.userProperties.lastName.value', + from: '$.context.traits.lastName', + }, + ], + }, +]; + +const destination = { + Config: { + apiSecret: 'dummyApiSecret', + measurementId: 'G-T40PE6KET4', + firebaseAppId: '', + blockPageViewEvent: false, + typesOfClient: 'gtag', + extendPageViewParams: false, + sendUserId: false, + eventFilteringOption: 'disable', + eventsMapping, + }, +}; +export const customMappingTestCases = [ + { + name: 'ga4_v2', + id: 'ga4_custom_mapping_test_0', + description: 'Custom Mapping Test 0', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Product List Viewed', + userId: 'root_user', + anonymousId: 'root_anonId', + context: { + device, + traits, + }, + properties, + originalTimestamp: '2022-04-28T00:23:09.544Z', + integrations, + }, + destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + client_id: 'root_anonId', + events: [ + { + name: 'view_item_list', + params: { + items: [ + { + name: 'Salt', + id: 883213, + list_id: 'random_list_id', + }, + { + id: 213123, + key: 'someValue', + list_id: 'random_list_id', + name: 'Sugar', + }, + ], + prices: 456, + }, + }, + ], + userProperties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + }, + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'ga4_v2', + id: 'ga4_custom_mapping_test_1', + description: 'Custom Mapping Test for multiplexing', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Product Added', + userId: 'root_user', + anonymousId: 'root_anonId', + context: { + device, + traits, + }, + properties, + originalTimestamp: '2022-04-28T00:23:09.544Z', + integrations, + }, + destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + client_id: 'root_anonId', + events: [ + { + name: 'add_to_cart', + params: { + items: [ + { + name: 'Salt', + id: 883213, + list_id: 'random_list_id', + }, + { + name: 'Sugar', + id: 213123, + key: 'someValue', + list_id: 'random_list_id', + }, + ], + prices: 456, + }, + }, + ], + userProperties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + }, + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + client_id: 'root_anonId', + events: [ + { + name: 'checkout_started', + params: { + items: [ + { + name: 'Salt', + id: 883213, + list_id: 'random_list_id', + }, + { + name: 'Sugar', + id: 213123, + key: 'someValue', + list_id: 'random_list_id', + }, + ], + prices: 456, + }, + }, + ], + userProperties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + }, + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'ga4_v2', + id: 'ga4_custom_mapping_test_2', + description: 'Custom Mapping Test For mapping not present in events mapping', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'track', + event: 'Product Viewed', + userId: 'root_user', + anonymousId: 'root_anonId', + context: { + device, + traits, + }, + properties, + originalTimestamp: '2022-04-28T00:23:09.544Z', + integrations, + }, + destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + user_properties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + city: { + value: 'London', + }, + state: { + value: 'UK', + }, + group: { + value: 'test group', + }, + }, + events: [ + { + name: 'Product_Viewed', + params: { + engagement_time_msec: 1, + list_id: 'random_list_id', + category: 'random_category', + storePrice: 456, + prices_0_id: 'store-price', + prices_0_value: 456, + prices_1_id: 'desk-price', + prices_1_value: 567, + products_0_product_id: 883213, + products_0_name: 'Salt', + products_0_coupon: 'HHH', + products_0_price: 100, + products_0_position: 1, + products_0_quantity: 10, + products_0_affiliation: 'NADA', + products_0_currency: 'INR', + products_0_discount: '2%', + products_0_item_category3: 'grocery', + products_1_product_id: 213123, + products_1_name: 'Sugar', + products_1_coupon: 'III', + products_1_price: 200, + products_1_position: 2, + products_1_quantity: 20, + products_1_affiliation: 'ADNA', + products_1_currency: 'INR', + products_1_discount: '5%', + products_1_item_category2: 'regulars', + products_1_item_category3: 'grocery', + products_1_some_data: 'someValue', + }, + }, + ], + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'ga4_v2', + id: 'ga4_custom_mapping_test_3', + description: 'Custom Mapping Test For Group Event Type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'group', + userId: 'root_user', + anonymousId: 'root_anonId', + context: { + device, + traits, + }, + properties, + originalTimestamp: '2022-04-28T00:23:09.544Z', + integrations, + }, + destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://www.google-analytics.com/mp/collect', + headers: { + HOST: 'www.google-analytics.com', + 'Content-Type': 'application/json', + }, + params: { + api_secret: 'dummyApiSecret', + measurement_id: 'G-T40PE6KET4', + }, + body: { + JSON: { + user_id: 'root_user', + timestamp_micros: 1651105389000000, + non_personalized_ads: false, + client_id: 'root_anonId', + events: [ + { + name: 'join_group', + params: { + city: 'London', + engagement_time_msec: 1, + firstName: 'John', + group: 'test group', + lastName: 'Gomes', + state: 'UK', + streetAddress: '71 Cherry Court SOUTHAMPTON SO53 5PD UK', + }, + }, + ], + user_properties: { + firstName: { + value: 'John', + }, + lastName: { + value: 'Gomes', + }, + city: { + value: 'London', + }, + state: { + value: 'UK', + }, + group: { + value: 'test group', + }, + }, + consent: { + ad_user_data: 'DENIED', + ad_personalization: 'GRANTED', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/ga4_v2/processor/data.ts b/test/integrations/destinations/ga4_v2/processor/data.ts new file mode 100644 index 0000000000..ba82792f31 --- /dev/null +++ b/test/integrations/destinations/ga4_v2/processor/data.ts @@ -0,0 +1,3 @@ +import { customMappingTestCases } from './customMappings'; + +export const data = [...customMappingTestCases]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts new file mode 100644 index 0000000000..0cee1418e1 --- /dev/null +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/business.ts @@ -0,0 +1,284 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +const headers = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', +}; + +const params = { + event: 'Product Added', + customerId: '1234567899', + destination: 'google_adwords_enhanced_conversions', +}; + +const validRequestPaylod = { + partialFailure: true, + conversionAdjustments: [ + { + gclidDateTimePair: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + restatementValue: { + adjustedValue: 10, + currency: 'INR', + }, + order_id: '10000', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + adjustmentType: 'ENHANCEMENT', + }, + ], +}; + +const commonRequestParameters = { + headers, + params, + JSON: validRequestPaylod, +}; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'gaec_v0_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: [ + { + results: [ + { + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/7693729833/conversionActions/874224905', + gclidDateTimePair: { + conversionDateTime: '2021-01-01 12:32:45-08:00', + gclid: '1234', + }, + orderId: '12345', + }, + ], + }, + ], + status: 200, + }, + message: 'Request Processed Successfully', + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaec_v0_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Test for a partial failure request with a 200 response from the destination', + successCriteria: 'Should return 400 with partial failure error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + params: { + event: 'Product Added', + customerId: '1234567888', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + destinationResponse: { + code: 3, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + conversionAdjustmentUploadError: 'CONVERSION_ALREADY_ENHANCED', + }, + location: { + fieldPathElements: [ + { + fieldName: 'conversion_adjustments', + index: 0, + }, + ], + }, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.', + }, + ], + }, + ], + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]', + }, + message: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'gaec_v1_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '[{"results":[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/7693729833/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaec_v1_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Test for a partial failure request with a 200 response from the destination', + successCriteria: 'Should return 400 with partial failure error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + params: { + event: 'Product Added', + customerId: '1234567888', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + }, + [generateMetadata(1)], + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + response: [ + { + error: + '[Google Adwords Enhanced Conversions]:: partialFailureError - {"code":3,"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]","details":[{"@type":"type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure","errors":[{"errorCode":{"conversionAdjustmentUploadError":"CONVERSION_ALREADY_ENHANCED"},"message":"Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.","location":{"fieldPathElements":[{"fieldName":"conversion_adjustments","index":0}]}}]}]}', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts index b544baaebd..709ab6d2a8 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/data.ts @@ -1,307 +1,9 @@ +import { v0oauthScenarios, v1oauthScenarios } from './oauth'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567890', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 401, - body: { - output: { - message: - '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: [ - { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - ], - statTags: { - destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - status: 401, - }, - }, - }, - }, - }, - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567899:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567899', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: [ - { - results: [ - { - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/7693729833/conversionActions/874224905', - gclidDateTimePair: { - conversionDateTime: '2021-01-01 12:32:45-08:00', - gclid: '1234', - }, - orderId: '12345', - }, - ], - }, - ], - status: 200, - }, - message: 'Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: 'google_adwords_enhanced_conversions', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v15/customers/1234567891:uploadConversionAdjustments', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': '0987654321', - }, - params: { - event: 'Product Added', - customerId: '1234567891', - destination: 'google_adwords_enhanced_conversions', - }, - body: { - JSON: { - partialFailure: true, - conversionAdjustments: [ - { - gclidDateTimePair: { - gclid: 'gclid1234', - conversionDateTime: '2022-01-01 12:32:45-08:00', - }, - restatementValue: { - adjustedValue: 10, - currency: 'INR', - }, - order_id: '10000', - adjustmentDateTime: '2022-01-01 12:32:45-08:00', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - userIdentifiers: [ - { - addressInfo: { - hashedFirstName: - 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', - hashedLastName: - '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', - state: 'UK', - city: 'London', - hashedStreetAddress: - '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', - }, - }, - ], - adjustmentType: 'ENHANCEMENT', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: [ - { - results: [ - { - conversionAction: { - id: 123434342, - }, - }, - ], - }, - ], - message: '" during Google_adwords_enhanced_conversions response transformation', - statTags: { - destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, + ...v0oauthScenarios, + ...v1oauthScenarios, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts new file mode 100644 index 0000000000..70d9eeaf33 --- /dev/null +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/dataDelivery/oauth.ts @@ -0,0 +1,276 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateProxyV1Payload, + generateProxyV0Payload, + generateMetadata, +} from '../../../testUtils'; + +const requestPayload = { + partialFailure: true, + conversionAdjustments: [ + { + gclidDateTimePair: { + gclid: 'gclid1234', + conversionDateTime: '2022-01-01 12:32:45-08:00', + }, + restatementValue: { + adjustedValue: 10, + currency: 'INR', + }, + order_id: '10000', + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + adjustmentType: 'ENHANCEMENT', + }, + ], +}; + +const headers = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', +}; + +const params = { + event: 'Product Added', + customerId: '1234567890', + destination: 'google_adwords_enhanced_conversions', +}; + +const commonRequestParameters = { + params, + headers, + JSON: requestPayload, +}; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_ENHANCED_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const v0oauthScenarios = [ + { + id: 'gaec_v0_oauth_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + destinationResponse: [ + { + error: { + code: 401, + message: + 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', + status: 'UNAUTHENTICATED', + }, + }, + ], + message: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaec_v0_oauth_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v0 API] :: Oauth where caller does not have permission mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with error', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + headers, + params: { + event: 'Product Added', + customerId: '1234567910', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + destinationResponse: [ + { + error: { + code: 403, + errors: [ + { + domain: 'global', + message: 'The caller does not have permission', + reason: 'forbidden', + }, + ], + message: 'The caller does not have permission', + status: 'PERMISSION_DENIED', + }, + }, + ], + message: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; + +export const v1oauthScenarios = [ + { + id: 'gaec_v1_oauth_scenario_1', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567890:uploadConversionAdjustments', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + message: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + response: [ + { + error: + '""Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project." during Google_adwords_enhanced_conversions response transformation"', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaec_v1_oauth_scenario_2', + name: 'google_adwords_enhanced_conversions', + description: + '[Proxy v1 API] :: Oauth where caller does not have permission mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with error', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + JSON: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + headers, + params: { + event: 'Product Added', + customerId: '1234567910', + destination: 'google_adwords_enhanced_conversions', + }, + endpoint: + 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + response: [ + { + error: + '""The caller does not have permission" during Google_adwords_enhanced_conversions response transformation"', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts index 672cd73bf7..69b3a6103a 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/network.ts @@ -273,4 +273,146 @@ export const networkCallsData = [ status: 400, }, }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567888/googleAds:searchStream', + data: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + data: [ + { + results: [ + { + conversionAction: { + id: 123434345, + }, + }, + ], + }, + ], + status: 200, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567888:uploadConversionAdjustments', + data: { + conversionAdjustments: [ + { + adjustmentDateTime: '2022-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567888/conversionActions/123434345', + gclidDateTimePair: { + conversionDateTime: '2022-01-01 12:32:45-08:00', + gclid: 'gclid1234', + }, + order_id: '10000', + restatementValue: { adjustedValue: 10, currency: 'INR' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + userIdentifiers: [ + { + addressInfo: { + hashedFirstName: + 'a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da', + hashedLastName: + '1c574b17eefa532b6d61c963550a82d2d3dfca4a7fb69e183374cfafd5328ee4', + state: 'UK', + city: 'London', + hashedStreetAddress: + '9a4d2e50828448f137f119a3ebdbbbab8d6731234a67595fdbfeb2a2315dd550', + }, + }, + ], + }, + ], + partialFailure: true, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + status: 200, + data: { + partialFailureError: { + code: 3, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again., at conversion_adjustments[0]', + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v15.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + conversionAdjustmentUploadError: 'CONVERSION_ALREADY_ENHANCED', + }, + message: + 'Conversion already has enhancements with the same Order ID and conversion action. Make sure your data is correctly configured and try again.', + location: { + fieldPathElements: [ + { + fieldName: 'conversion_adjustments', + index: 0, + }, + ], + }, + }, + ], + }, + ], + }, + }, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v15/customers/1234567910/googleAds:searchStream', + data: { + query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Product Added'`, + }, + params: { destination: 'google_adwords_enhanced_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': '0987654321', + }, + method: 'POST', + }, + httpRes: { + data: [ + { + error: { + code: 403, + message: 'The caller does not have permission', + errors: [ + { + message: 'The caller does not have permission', + domain: 'global', + reason: 'forbidden', + }, + ], + status: 'PERMISSION_DENIED', + }, + }, + ], + status: 403, + }, + }, ]; diff --git a/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts b/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts index 0a9542a5d5..13b2609bf8 100644 --- a/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts +++ b/test/integrations/destinations/google_adwords_enhanced_conversions/processor/data.ts @@ -1255,7 +1255,7 @@ export const data = [ }, traits: { phone: '912382193', - firstName: 'John', + firstName: ' John', lastName: 'Gomes', city: 'London', state: 'UK', diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts new file mode 100644 index 0000000000..87aafea0af --- /dev/null +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts @@ -0,0 +1,669 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const transactionAttribute = { + CUSTOM_KEY: 'CUSTOM_VALUE', + currency_code: 'INR', + order_id: 'order id', + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + transaction_date_time: '2019-10-14 11:15:18+00:00', +}; + +const createJobPayload = { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, +}; + +const products = [ + { + product_id: '507f1f77bcf86cd799439011', + quantity: '2', + price: '50', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + }, +]; + +const headers = { + header1: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + header2: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, +}; + +const params = { + param1: { + customerId: '1112223333', + event: 'Sign-up - click', + }, + param2: { + event: 'Sign-up - click', + customerId: '1234567891', + customVariables: [ + { + from: 'Value', + to: 'revenue', + }, + { + from: 'total', + to: 'cost', + }, + ], + properties: { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + conversionCustomVariable: 'conversionCustomVariable', + Value: 'value', + merchantId: '9876merchantId', + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + products, + userIdentifierSource: 'FIRST_PARTY', + conversionEnvironment: 'WEB', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: '1', + currency: 'GBP', + orderId: 'PL-123QR', + }, + }, + param3: {}, + param4: {}, +}; + +params['param3'] = { ...params.param2, customVariables: [] }; + +params['param4'] = { ...params.param3, customerId: '1234567893', conversionEnvironment: 'APP' }; + +const validRequestPayload1 = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: transactionAttribute, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const validRequestPayload2 = { + conversions: [ + { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionData: { + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + }, + cartData: { + merchantId: 9876, + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + items: [ + { + productId: '507f1f77bcf86cd799439011', + quantity: 2, + unitPrice: 50, + }, + ], + }, + userIdentifiers: [ + { + userIdentifierSource: 'FIRST_PARTY', + hashedPhoneNumber: '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', + }, + ], + conversionEnvironment: 'WEB', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: 1, + currencyCode: 'GBP', + orderId: 'PL-123QR', + }, + ], + partialFailure: true, +}; + +const invalidArgumentRequestPayload = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: transactionAttribute, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af26f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const notAllowedToAccessFeatureRequestPayload = { + ...validRequestPayload2, + conversions: [ + { + ...validRequestPayload2.conversions[0], + conversionEnvironment: 'APP', + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'gaoc_v0_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for invalid argument - where the destination responds with 400 with invalid argument error', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header1, + params: params.param1, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v16/customers/11122233331/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + status: 400, + message: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + destinationResponse: { + error: { + code: 400, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v16.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + offlineUserDataJobError: 'INVALID_SHA256_FORMAT', + }, + message: 'The SHA256 encoded value is malformed.', + location: { + fieldPathElements: [ + { + fieldName: 'operations', + index: 0, + }, + { + fieldName: 'create', + }, + { + fieldName: 'user_identifiers', + index: 0, + }, + { + fieldName: 'hashed_email', + }, + ], + }, + }, + ], + requestId: '68697987', + }, + ], + message: 'Request contains an invalid argument.', + status: 'INVALID_ARGUMENT', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid operations request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header1, + params: params.param1, + JSON: validRequestPayload1, + endpoint: 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + destinationResponse: { + response: { + name: 'customers/111-222-3333/operations/abcd=', + }, + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_3', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header2, + params: params.param2, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + destinationResponse: { + response: [ + { + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567891/conversionActions/874224905', + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + gclidDateTimePair: { + gclid: '1234', + conversionDateTime: '2021-01-01 12:32:45-08:00', + }, + orderId: '12345', + }, + ], + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_4', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid conversion action request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header2, + params: params.param3, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: [ + { + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567891/conversionActions/874224905', + gclidDateTimePair: { + conversionDateTime: '2021-01-01 12:32:45-08:00', + gclid: '1234', + }, + orderId: '12345', + }, + ], + status: 200, + }, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + status: 200, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'gaoc_v1_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for invalid argument - where the destination responds with 400 with invalid argument error', + successCriteria: 'Should return 400 with error with destination response', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header1, + params: params.param1, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v16/customers/11122233331/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid operations request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header1, + params: params.param1, + JSON: validRequestPayload1, + endpoint: + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: '{"name":"customers/111-222-3333/operations/abcd="}', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_3', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param2, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: + '[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/1234567891/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_4', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid conversion action request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param3, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: + '[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/1234567891/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_5', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for customer is not allowed Test for a valid request with a successful 200 response from the destinationto access feature partial failure error', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param4, + JSON: notAllowedToAccessFeatureRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v16/customers/1234567893:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions]:: partialFailureError - Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + response: [ + { + error: + '[Google Ads Offline Conversions]:: partialFailureError - Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts index ae75273399..709ab6d2a8 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts @@ -1,768 +1,9 @@ +import { v0oauthScenarios, v1oauthScenarios } from './oauth'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'google_adwords_offline_conversions', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af26f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - status: 400, - message: - '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', - destinationResponse: { - error: { - code: 400, - details: [ - { - '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', - errors: [ - { - errorCode: { - offlineUserDataJobError: 'INVALID_SHA256_FORMAT', - }, - message: 'The SHA256 encoded value is malformed.', - location: { - fieldPathElements: [ - { - fieldName: 'operations', - index: 0, - }, - { - fieldName: 'create', - }, - { - fieldName: 'user_identifiers', - index: 0, - }, - { - fieldName: 'hashed_email', - }, - ], - }, - }, - ], - requestId: '68697987', - }, - ], - message: 'Request contains an invalid argument.', - status: 'INVALID_ARGUMENT', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - destinationResponse: { - response: { - name: 'customers/111-222-3333/operations/abcd=', - }, - status: 200, - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 401, - body: { - output: { - status: 401, - message: - '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 3', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567890:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567890', - customVariables: [ - { - from: 'value', - to: 'revenue', - }, - { - from: 'total', - to: 'cost', - }, - ], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 401, - body: { - output: { - status: 401, - message: - '[Google Ads Offline Conversions]:: [{"error":{"code":401,"message":"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","status":"UNAUTHENTICATED"}}] during google_ads_offline_conversions response transformation', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: [ - { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - ], - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 4', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567891', - customVariables: [ - { - from: 'Value', - to: 'revenue', - }, - { - from: 'total', - to: 'cost', - }, - ], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - Value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedPhoneNumber: - '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - destinationResponse: { - response: [ - { - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/1234567891/conversionActions/874224905', - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - gclidDateTimePair: { - gclid: '1234', - conversionDateTime: '2021-01-01 12:32:45-08:00', - }, - orderId: '12345', - }, - ], - status: 200, - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 5', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567891', - customVariables: [], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedPhoneNumber: - '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: [ - { - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/1234567891/conversionActions/874224905', - gclidDateTimePair: { - conversionDateTime: '2021-01-01 12:32:45-08:00', - gclid: '1234', - }, - orderId: '12345', - }, - ], - status: 200, - }, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - status: 200, - }, - }, - }, - }, - }, + ...v0oauthScenarios, + ...v1oauthScenarios, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts new file mode 100644 index 0000000000..e14e4109f0 --- /dev/null +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts @@ -0,0 +1,263 @@ +import { + generateMetadata, + generateProxyV1Payload, + generateProxyV0Payload, +} from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', +}; + +const commonParams = { + customerId: '1112223333', + event: 'Sign-up - click', +}; + +const commonRequestPayload = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: { + CUSTOM_KEY: 'CUSTOM_VALUE', + currency_code: 'INR', + order_id: 'order id', + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + transaction_date_time: '2019-10-14 11:15:18+00:00', + }, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload: { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, + }, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const commonRequestParameters = { + headers: commonHeaders, + params: commonParams, + JSON: commonRequestPayload, +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const v0oauthScenarios = [ + { + id: 'gaoc_v0_oauth_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: 'The proxy should return 401 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v16/customers/customerid/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + status: 401, + message: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + authErrorCategory: 'REFRESH_TOKEN', + destinationResponse: { + error: { + code: 401, + message: + 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', + status: 'UNAUTHENTICATED', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_oauth_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Oauth where ACCESS_TOKEN_SCOPE_INSUFFICIENT error as mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with authErrorCategory as AUTH_STATUS_INACTIVE', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v16/customers/1234/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + destinationResponse: { + error: { + code: 403, + message: 'Request had insufficient authentication scopes', + status: 'PERMISSION_DENIED', + }, + }, + message: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; + +export const v1oauthScenarios = [ + { + id: 'gaoc_v1_oauth_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: 'The proxy should return 401 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v16/customers/customerid/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + message: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_oauth_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Oauth where ACCESS_TOKEN_SCOPE_INSUFFICIENT error as mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with authErrorCategory as AUTH_STATUS_INACTIVE', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v16/customers/1234/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/network.ts b/test/integrations/destinations/google_adwords_offline_conversions/network.ts index 375879727b..4dad9e0d1b 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/network.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/network.ts @@ -1,7 +1,7 @@ export const networkCallsData = [ { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs:create', + url: 'https://googleads.googleapis.com/v16/customers/11122233331/offlineUserDataJobs:create', data: { job: { storeSalesMetadata: { @@ -30,7 +30,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1112223333/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1112223333/googleAds:searchStream', data: { query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'`, }, @@ -63,7 +63,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID_FOR_ADD_FAILURE:addOperations', + url: 'https://googleads.googleapis.com/v16/customers/11122233331/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID_FOR_ADD_FAILURE:addOperations', data: { enable_partial_failure: false, enable_warnings: false, @@ -108,7 +108,7 @@ export const networkCallsData = [ status: 'INVALID_ARGUMENT', details: [ { - '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + '@type': 'type.googleapis.com/google.ads.googleads.v16.errors.GoogleAdsFailure', errors: [ { errorCode: { @@ -144,7 +144,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs:create', + url: 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs:create', data: { job: { storeSalesMetadata: { @@ -173,7 +173,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID:addOperations', + url: 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID:addOperations', data: { enable_partial_failure: false, enable_warnings: false, @@ -216,7 +216,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID:run', + url: 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs/OFFLINE_USER_DATA_JOB_ID:run', data: { validate_only: false }, params: { destination: 'google_adwords_offline_conversion' }, headers: { @@ -235,8 +235,10 @@ export const networkCallsData = [ }, }, { + description: + 'Mock response from destination depicting a request with invalid authentication credentials', httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs:create', + url: 'https://googleads.googleapis.com/v16/customers/customerid/offlineUserDataJobs:create', data: { job: { storeSalesMetadata: { @@ -269,8 +271,43 @@ export const networkCallsData = [ }, }, { + description: + 'Mock response from destination depicting a request with invalid authentication scopes', httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567890/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1234/offlineUserDataJobs:create', + data: { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, + }, + params: { destination: 'google_adwords_offline_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + method: 'POST', + }, + httpRes: { + status: 403, + data: { + error: { + code: 403, + message: 'Request had insufficient authentication scopes', + status: 'PERMISSION_DENIED', + }, + }, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v16/customers/1234567890/googleAds:searchStream', data: { query: `SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'`, }, @@ -298,7 +335,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567891/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1234567891/googleAds:searchStream', data: { query: "SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'", @@ -331,7 +368,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567891/googleAds:searchStream', + url: 'https://googleads.googleapis.com/v16/customers/1234567891/googleAds:searchStream', data: { query: 'SELECT conversion_custom_variable.name FROM conversion_custom_variable' }, headers: { Authorization: 'Bearer abcd1234', @@ -365,7 +402,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + url: 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', data: { conversions: [ { @@ -432,7 +469,7 @@ export const networkCallsData = [ }, { httpReq: { - url: 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + url: 'https://googleads.googleapis.com/v16/customers/1234567891:uploadClickConversions', data: { conversions: [ { @@ -491,4 +528,121 @@ export const networkCallsData = [ status: 200, }, }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v16/customers/1234567893/googleAds:searchStream', + data: { + query: + "SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'", + }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + method: 'POST', + params: { destination: 'google_adwords_offline_conversion' }, + }, + httpRes: { + data: [ + { + results: [ + { + conversionAction: { + resourceName: 'customers/1234567893/conversionActions/848898417', + id: '848898417', + }, + }, + ], + fieldMask: 'conversionAction.id', + requestId: 'dummyRequestId', + }, + ], + status: 200, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v16/customers/1234567893:uploadClickConversions', + data: { + conversions: [ + { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionData: { + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + }, + cartData: { + merchantId: 9876, + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + items: [{ productId: '507f1f77bcf86cd799439011', quantity: 2, unitPrice: 50 }], + }, + userIdentifiers: [ + { + userIdentifierSource: 'FIRST_PARTY', + hashedPhoneNumber: + '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', + }, + ], + conversionEnvironment: 'APP', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: 1, + currencyCode: 'GBP', + orderId: 'PL-123QR', + conversionAction: 'customers/1234567893/conversionActions/848898417', + }, + ], + partialFailure: true, + }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + method: 'POST', + params: { destination: 'google_adwords_offline_conversion' }, + }, + httpRes: { + status: 200, + data: { + partialFailureError: { + code: 3, + message: + 'Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v16.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + notAllowlistedError: 'CUSTOMER_NOT_ALLOWLISTED_FOR_THIS_FEATURE', + }, + message: 'Customer is not allowlisted for accessing this feature.', + trigger: { + int64Value: '2', + }, + location: { + fieldPathElements: [ + { + fieldName: 'conversions', + index: 0, + }, + { + fieldName: 'conversion_environment', + }, + ], + }, + }, + ], + requestId: 'dummyRequestId', + }, + ], + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts index f47deaef67..ab3e19dc2f 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/processor/data.ts @@ -176,7 +176,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -263,6 +263,10 @@ export const data = [ conversionValue: 1, currencyCode: 'GBP', orderId: 'PL-123QR', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, ], partialFailure: true, @@ -317,7 +321,7 @@ export const data = [ advertisingId: '44c97318-9040-4361-8bc7-4eb30f665ca8', }, traits: { - email: 'alex@example.com', + email: ' alex@example.com', phone: '+1-202-555-0146', firstName: 'John', lastName: 'Gomes', @@ -465,7 +469,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -520,6 +524,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gbraid: 'gbraid', wbraid: 'wbraid', externalAttributionData: { @@ -754,7 +762,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -809,6 +817,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gbraid: 'gbraid', wbraid: 'wbraid', externalAttributionData: { @@ -1043,7 +1055,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -1099,6 +1111,10 @@ export const data = [ conversions: [ { callerId: 'callerId', + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-01-01 12:32:45-08:00', conversionValue: 1, @@ -2015,7 +2031,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2072,6 +2088,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gbraid: 'gbraid', wbraid: 'wbraid', externalAttributionData: { @@ -2128,7 +2148,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2185,6 +2205,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: 'callerId', callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-01-01 12:32:45-08:00', @@ -2350,7 +2374,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2379,6 +2403,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gclid: 'gclid', conversionDateTime: '2022-01-01 12:32:45-08:00', userIdentifiers: [ @@ -2546,7 +2574,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2575,6 +2603,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: 'callerId', callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-01-01 12:32:45-08:00', @@ -2778,7 +2810,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -2832,6 +2864,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: 'callerId', callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-09-20 08:50:04+05:30', @@ -2997,7 +3033,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -3025,6 +3061,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gclid: 'gclid', conversionDateTime: '2022-01-01 12:32:45-08:00', userIdentifiers: [ @@ -3501,7 +3541,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -3532,6 +3572,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, cartData: { items: [ { @@ -3861,7 +3905,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -3892,6 +3936,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, cartData: { items: [ { @@ -4049,7 +4097,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4077,6 +4125,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -4382,7 +4434,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4409,6 +4461,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -4577,7 +4633,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4604,6 +4660,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -4775,7 +4835,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4802,6 +4862,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -4935,7 +4999,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -4962,6 +5026,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -5091,7 +5159,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -5118,6 +5186,10 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', @@ -5245,7 +5317,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -5272,6 +5344,326 @@ export const data = [ addConversionPayload: { operations: { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + transaction_attribute: { + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + currency_code: 'INR', + transaction_date_time: '2019-10-14 16:45:18+05:30', + }, + userIdentifiers: [{}], + }, + }, + enable_partial_failure: false, + enable_warnings: false, + validate_only: false, + }, + executeJobPayload: { + validate_only: false, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: timestampMock, + }, + { + name: 'google_adwords_offline_conversions', + description: 'Test 26 : store conversion consent mapped from UI config', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + traits: {}, + }, + event: 'Product Clicked', + type: 'track', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { + item_id: 'item id', + merchant_id: 'merchant id', + currency: 'INR', + revenue: '100', + store_code: 'store code', + gclid: 'gclid', + conversionDateTime: '2019-10-14T11:15:18.299Z', + product_id: '123445', + quantity: 123, + }, + integrations: { + All: true, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + isCustomerAllowed: false, + customerId: '111-222-3333', + subAccount: true, + loginCustomerId: 'login-customer-id', + userDataConsent: 'GRANTED', + personalizationConsent: 'DENIED', + eventsToOfflineConversionsTypeMapping: [ + { + from: 'Product Clicked', + to: 'store', + }, + ], + eventsToConversionsNamesMapping: [ + { + from: 'Product Clicked', + to: 'Sign-up - click', + }, + ], + hashUserIdentifier: true, + defaultUserIdentifier: 'phone', + validateOnly: false, + rudderAccountId: '2EOknn1JNH7WK1MfNkgr4t3u4fGYKkRK', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + params: { + event: 'Sign-up - click', + customerId: '1112223333', + }, + body: { + JSON: { + event: '1112223333', + isStoreConversion: true, + createJobPayload: { + job: { + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + storeSalesMetadata: { + loyaltyFraction: '1', + transaction_upload_fraction: '1', + }, + }, + }, + addConversionPayload: { + operations: { + create: { + consent: { + adPersonalization: 'DENIED', + adUserData: 'GRANTED', + }, + transaction_attribute: { + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + currency_code: 'INR', + transaction_date_time: '2019-10-14 16:45:18+05:30', + }, + userIdentifiers: [{}], + }, + }, + enable_partial_failure: false, + enable_warnings: false, + validate_only: false, + }, + executeJobPayload: { + validate_only: false, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: timestampMock, + }, + { + name: 'google_adwords_offline_conversions', + description: + 'Test 27 : store conversion consent mapped from UI config even though integration object is present', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + traits: {}, + }, + event: 'Product Clicked', + type: 'track', + messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', + anonymousId: '00000000000000000000000000', + userId: '12345', + properties: { + item_id: 'item id', + merchant_id: 'merchant id', + currency: 'INR', + revenue: '100', + store_code: 'store code', + gclid: 'gclid', + conversionDateTime: '2019-10-14T11:15:18.299Z', + product_id: '123445', + quantity: 123, + }, + integrations: { + google_adwords_offline_conversion: { + consent: { + adUserdata: 'UNSPECIFIED', + adPersonalization: 'GRANTED', + }, + }, + }, + name: 'ApplicationLoaded', + sentAt: '2019-10-14T11:15:53.296Z', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + }, + destination: { + Config: { + isCustomerAllowed: false, + customerId: '111-222-3333', + subAccount: true, + loginCustomerId: 'login-customer-id', + userDataConsent: 'GRANTED', + personalizationConsent: 'DENIED', + eventsToOfflineConversionsTypeMapping: [ + { + from: 'Product Clicked', + to: 'store', + }, + ], + eventsToConversionsNamesMapping: [ + { + from: 'Product Clicked', + to: 'Sign-up - click', + }, + ], + hashUserIdentifier: true, + defaultUserIdentifier: 'phone', + validateOnly: false, + rudderAccountId: '2EOknn1JNH7WK1MfNkgr4t3u4fGYKkRK', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/1112223333/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + params: { + event: 'Sign-up - click', + customerId: '1112223333', + }, + body: { + JSON: { + event: '1112223333', + isStoreConversion: true, + createJobPayload: { + job: { + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + storeSalesMetadata: { + loyaltyFraction: '1', + transaction_upload_fraction: '1', + }, + }, + }, + addConversionPayload: { + operations: { + create: { + consent: { + adPersonalization: 'DENIED', + adUserData: 'GRANTED', + }, transaction_attribute: { store_attribute: { store_code: 'store code', diff --git a/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts index a38980f0e9..9d1ba220c8 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/router/data.ts @@ -487,7 +487,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -511,6 +511,10 @@ export const data = [ operations: [ { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code' }, transaction_amount_micros: '100000000', @@ -528,6 +532,10 @@ export const data = [ }, { create: { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, transaction_attribute: { store_attribute: { store_code: 'store code2' }, transaction_amount_micros: '100000000', @@ -561,7 +569,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/7693729833:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/7693729833:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -589,6 +597,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: '1234', callStartDateTime: '2019-10-14T11:15:18.299Z', conversionDateTime: '2019-10-14 16:45:18+05:30', @@ -673,7 +685,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadClickConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadClickConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -722,6 +734,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, gbraid: 'gbraid', wbraid: 'wbraid', externalAttributionData: { @@ -806,7 +822,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v14/customers/9625812972:uploadCallConversions', + 'https://googleads.googleapis.com/v16/customers/9625812972:uploadCallConversions', headers: { Authorization: 'Bearer abcd1234', 'Content-Type': 'application/json', @@ -855,6 +871,10 @@ export const data = [ JSON: { conversions: [ { + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, callerId: 'callerId', callStartDateTime: '2022-08-28 15:01:30+05:30', conversionDateTime: '2022-01-01 12:32:45-08:00', @@ -979,4 +999,287 @@ export const data = [ }, mockFns: timestampMock, }, + { + name: 'google_adwords_offline_conversions', + description: 'Test 1 should include destination when single store sales event is sent', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'track', + event: 'Order Completed', + sentAt: '2024-05-09T00:02:48.319Z', + userId: '7fe0de4847f6dafb0cba694ef725404a', + channel: 'sources', + context: { + banner: { + key: 'CS', + domain: 'www.champssports.com', + }, + traits: { + email: 'johnwick@gmail.com', + address: { + city: 'homestead', + state: 'fl', + country: 'usa', + postalCode: '33032', + }, + lastName: 'wick', + firstName: 'john', + }, + privacy: { + ccpa: true, + }, + sources: { + job_id: '123344545565466', + version: 'v1.48.7', + job_run_id: 'cou1407gdjb6rkrrtv5g', + task_run_id: 'cou1407gdjb6rkrrtv6g', + }, + snowflake: { + ID: '44acd2006efb6b7d1a0eaf0da2b05b69', + TAX: 8.05, + NAME: 'johnwick', + PHONE: '', + TOTAL: 115, + email: 'JONBOBBYwick@GMAIL.COM', + BANNER: 'CS', + COUPON: '[null]', + REVENUE: 123.05, + CATEGORY: 'Retail', + CURRENCY: 'USD', + DISCOUNT: 0, + LASTNAME: 'wick', + ORDER_ID: '12343-4886-294995', + PRODUCTS: + '[{"sku":"C2302100","product_id":816827,"category":"1","size":"10.5","name":"NIKE AF1 \'07 AN21-WH/BK","brand":"NIKE INC","variant":"WHITE/BLACK","class":"MENS","price":"115.0","division":"CASUAL-ATHLETIC","quantity":"1","discountFlag":"false"}]', + RUDDERID: 'UNAVAILABLE', + SHIPPING: 'n/a', + STORE_ID: '14540', + SUBTOTAL: 115, + FIRSTNAME: 'john', + MESSAGEID: 'UNAVAILABLE', + TIMESTAMP: '2024-05-07T17:27:28.262Z', + TOTAL_VAT: 123.05, + EVENT_DATE: '2024-05-07T00:00:00Z', + STORE_NAME: 'CHAMPS ', + DISCOUNT_VAT: 0, + IS_E_RECEIPT: '1', + SUBTOTAL_VAT: 123.05, + USER_ADDRESS: '', + FLX_CARDNUMBER: '99000002697409', + PAYMENT_METHOD: null, + ACCOUNT_ADDRESS: null, + CM_PHONE_NUMBER: '7868007626', + SHIPPING_METHOD: 'n/a', + STORE_ADDR_CITY: 'CUTLER BAY ', + CM_BILL_ADDRESS1: '13020 SW 256TH ST', + STORE_ADDR_STATE: 'FL', + STORE_ADDR_STREET: + 'SOUTHLAND MALL 20505 SOUTH DIXIE HWY SPACE 1401 ', + STORE_ADDR_COUNTRY: 'UNITED STATES', + STORE_ADDR_ZIPCODE: '33189 ', + ACCOUNT_ADDRESS_CITY: 'HOMESTEAD', + BILLING_ADDRESS_CITY: 'HOMESTEAD', + SHIP_TO_ADDRESS_CITY: 'UNAVAILABLE', + ACCOUNT_ADDRESS_STATE: 'FL', + BILLING_ADDRESS_STATE: 'FL', + SHIP_TO_ADDRESS_STATE: 'UNAVAILABLE', + SHIP_TO_ADDRESS_STREET: 'UNAVAILABLE', + ACCOUNT_ADDRESS_COUNTRY: 'US', + BILLING_ADDRESS_COUNTRY: 'USA', + SHIP_TO_ADDRESS_COUNTRY: 'UNAVAILABLE', + SHIP_TO_ADDRESS_POSTALCODE: 'UNAVAILABLE', + ACCOUNT_ADDRESS_POSTAL_CODE: '33032', + BILLING_ADDRESS_POSTAL_CODE: '33032', + }, + account_id: 'xxxxxxxxxx', + account_mcc: '1234556775', + }, + recordId: '1230', + rudderId: '35d5060a-2756-45d1-9808-cae9aec19166', + messageId: '23d5060b-2756-45c1-9108-c229aec19126', + timestamp: '2024-05-07 17:27:28-00:00', + properties: { + value: 123.05, + currency: 'USD', + order_id: '12343-4886-294995', + products: [ + { + sku: 'C2302100', + price: 115, + quantity: '1', + }, + ], + conversionDateTime: '2024-05-07 17:27:28-00:00', + }, + receivedAt: '2024-05-09T00:02:43.864Z', + request_ip: '10.7.208.179', + originalTimestamp: '2024-05-09T00:02:48.319Z', + }, + metadata: { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + jobId: 1, + userId: 'u1', + }, + destination: { + Config: { + // customerId: '962-581-2972', + customerId: '{{ event.context.account_mcc || "1234556775" }}', + subAccount: false, + loginCustomerId: '{{ event.context.account_mcc || "1234556775" }}', + eventsToOfflineConversionsTypeMapping: [ + { + from: 'Order Completed', + to: 'store', + }, + ], + eventsToConversionsNamesMapping: [ + { + from: 'Order Completed', + to: 'Store sales', + }, + ], + UserIdentifierSource: 'FIRST_PARTY', + conversionEnvironment: 'none', + defaultUserIdentifier: 'email', + hashUserIdentifier: true, + validateOnly: false, + oneTrustCookieCategories: [], + eventDelivery: false, + eventDeliveryTS: 1715104236592, + rudderAccountId: '25u5whFH7gVTnCiAjn4ykoCLGoC', + }, + }, + }, + ], + destType: 'google_adwords_offline_conversions', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/1234556775/offlineUserDataJobs', + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + params: { event: 'Store sales', customerId: '1234556775' }, + body: { + JSON: { + event: '1234556775', + isStoreConversion: true, + createJobPayload: { + job: { + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + storeSalesMetadata: { + loyaltyFraction: '1', + transaction_upload_fraction: '1', + }, + }, + }, + addConversionPayload: { + operations: [ + { + create: { + transaction_attribute: { + transaction_amount_micros: '123050000', + order_id: '12343-4886-294995', + currency_code: 'USD', + transaction_date_time: '2019-10-14 16:45:18+05:30', + }, + userIdentifiers: [ + { + hashedEmail: + 'cd54e8f2e90e2a092a153f7e27e7b47a8ad29adb7943a05d749f0f9836935a2f', + }, + ], + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, + }, + }, + ], + enable_partial_failure: false, + enable_warnings: false, + validate_only: false, + }, + executeJobPayload: { validate_only: false }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + secret: { + access_token: 'abcd1234', + refresh_token: 'efgh5678', + developer_token: 'ijkl91011', + }, + jobId: 1, + userId: 'u1', + }, + ], + destination: { + Config: { + // customerId: '962-581-2972', + customerId: '1234556775', + subAccount: false, + loginCustomerId: '1234556775', + eventsToOfflineConversionsTypeMapping: [ + { + from: 'Order Completed', + to: 'store', + }, + ], + eventsToConversionsNamesMapping: [ + { + from: 'Order Completed', + to: 'Store sales', + }, + ], + UserIdentifierSource: 'FIRST_PARTY', + conversionEnvironment: 'none', + defaultUserIdentifier: 'email', + hashUserIdentifier: true, + validateOnly: false, + oneTrustCookieCategories: [], + eventDelivery: false, + eventDeliveryTS: 1715104236592, + rudderAccountId: '25u5whFH7gVTnCiAjn4ykoCLGoC', + }, + }, + batched: true, + statusCode: 200, + }, + ], + }, + }, + }, + mockFns: timestampMock, + }, ]; diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts new file mode 100644 index 0000000000..2aefb18fdd --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/business.ts @@ -0,0 +1,376 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer dummy-access', + 'Content-Type': 'application/json', + 'developer-token': 'dummy-dev-token', +}; + +const commonParams = { + destination: 'google_adwords_remarketing_lists', + listId: '709078448', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, +}; + +const validRequestPayload1 = { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', + }, + }, + ], + }, + }, + ], +}; + +const validRequestPayload2 = { + enablePartialFailure: true, + operations: [ + { + remove: { + userIdentifiers: [ + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + hashedEmail: '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', + }, + { + hashedPhoneNumber: '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', + }, + }, + ], + }, + }, + ], +}; + +const invalidArgumentRequestPayload = { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: 'abcd@testmail.com', + }, + ], + }, + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'garl_v0_scenario_1', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload1, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { response: '', status: 200 }, + }, + }, + }, + }, + }, + { + id: 'garl_v0_scenario_2', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a invalid argument request with a 400 response from the destination', + successCriteria: 'Should return 400 with invalid argument error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: invalidArgumentRequestPayload, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + status: 400, + message: + 'Request contains an invalid argument. during ga_audience response transformation', + destinationResponse: { + error: { + code: 400, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v9.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + offlineUserDataJobError: 'INVALID_SHA256_FORMAT', + }, + message: 'The SHA256 encoded value is malformed.', + location: { + fieldPathElements: [ + { fieldName: 'operations', index: 0 }, + { fieldName: 'remove' }, + { fieldName: 'user_identifiers', index: 0 }, + { fieldName: 'hashed_email' }, + ], + }, + }, + ], + }, + ], + message: 'Request contains an invalid argument.', + status: 'INVALID_ARGUMENT', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'garl_v0_scenario_3', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload2, + endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { response: '', status: 200 }, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'garl_v1_scenario_1', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload1, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: '""', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'garl_v1_scenario_2', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a invalid argument request with a 400 response from the destination', + successCriteria: 'Should return 400 with invalid argument error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Request contains an invalid argument. during ga_audience response transformation', + response: [ + { + error: + 'Request contains an invalid argument. during ga_audience response transformation', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'garl_v1_scenario_3', + name: 'google_adwords_remarketing_lists', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: commonHeaders, + params: commonParams, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: '""', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts index fe16ffef47..51827a38e2 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/dataDelivery/data.ts @@ -1,261 +1,3 @@ -export const data = [ - { - name: 'google_adwords_remarketing_lists', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - destination: 'google_adwords_remarketing_lists', - listId: '709078448', - customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - create: { - userIdentifiers: [ - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - addressInfo: { - hashedFirstName: - 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', - }, - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: 'Request Processed Successfully', - destinationResponse: { response: '', status: 200 }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_remarketing_lists', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729834/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - listId: '709078448', - customerId: '7693729833', - destination: 'google_adwords_remarketing_lists', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - create: { - userIdentifiers: [ - { - hashedEmail: 'abcd@testmail.com', - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - status: 400, - message: - 'Request contains an invalid argument. during ga_audience response transformation', - destinationResponse: { - error: { - code: 400, - details: [ - { - '@type': 'type.googleapis.com/google.ads.googleads.v9.errors.GoogleAdsFailure', - errors: [ - { - errorCode: { - offlineUserDataJobError: 'INVALID_SHA256_FORMAT', - }, - message: 'The SHA256 encoded value is malformed.', - location: { - fieldPathElements: [ - { fieldName: 'operations', index: 0 }, - { fieldName: 'remove' }, - { fieldName: 'user_identifiers', index: 0 }, - { fieldName: 'hashed_email' }, - ], - }, - }, - ], - }, - ], - message: 'Request contains an invalid argument.', - status: 'INVALID_ARGUMENT', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_remarketing_lists', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', - headers: { - Authorization: 'Bearer dummy-access', - 'Content-Type': 'application/json', - 'developer-token': 'dummy-dev-token', - }, - params: { - listId: '709078448', - customerId: '7693729833', - destination: 'google_adwords_remarketing_lists', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, - }, - body: { - JSON: { - enablePartialFailure: true, - operations: [ - { - remove: { - userIdentifiers: [ - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - hashedEmail: - '85cc9fefa1eff1baab55d10df0cecff2acb25344867a5d0f96e1b1c5e2f10f05', - }, - { - hashedPhoneNumber: - '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', - }, - { - addressInfo: { - hashedFirstName: - 'e56d336922eaab3be8c1244dbaa713e134a8eba50ddbd4f50fd2fe18d72595cd', - }, - }, - ], - }, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: 'Request Processed Successfully', - destinationResponse: { response: '', status: 200 }, - }, - }, - }, - }, - }, -]; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + +export const data = [...testScenariosForV0API, ...testScenariosForV1API]; diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts index a846e0370d..639e28403c 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/processor/data.ts @@ -11,7 +11,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -70,7 +70,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -120,7 +120,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -143,7 +143,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -203,7 +203,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -239,7 +239,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -262,7 +262,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -323,7 +323,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -358,7 +358,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -381,7 +381,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -456,7 +456,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -489,7 +489,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -546,7 +546,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -579,7 +579,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -635,7 +635,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -667,7 +667,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -711,7 +711,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -743,7 +743,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -807,7 +807,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -839,7 +839,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -1425,7 +1425,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2712,7 +2712,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2735,7 +2735,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2820,7 +2820,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2887,7 +2887,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2900,7 +2900,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -2967,7 +2967,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -2990,7 +2990,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -4113,7 +4113,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -5400,7 +5400,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -5413,7 +5413,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -6700,7 +6700,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6723,7 +6723,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6790,7 +6790,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -6857,7 +6857,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6880,7 +6880,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6944,7 +6944,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -6977,7 +6977,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -8100,7 +8100,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -9387,7 +9387,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -9400,7 +9400,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10687,7 +10687,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10710,7 +10710,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10795,7 +10795,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10862,7 +10862,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10875,7 +10875,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -10942,7 +10942,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -10965,7 +10965,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11050,7 +11050,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11115,7 +11115,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11128,7 +11128,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11190,7 +11190,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11213,7 +11213,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11272,7 +11272,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11316,7 +11316,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11419,7 +11419,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11479,7 +11479,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11523,7 +11523,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11546,7 +11546,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11618,7 +11618,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11661,7 +11661,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11684,7 +11684,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11756,7 +11756,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11799,7 +11799,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11822,7 +11822,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11896,7 +11896,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -11942,7 +11942,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -11965,7 +11965,7 @@ export const data = [ { metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, @@ -12039,7 +12039,7 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { Authorization: 'Bearer dummy-access', 'Content-Type': 'application/json', @@ -12082,7 +12082,7 @@ export const data = [ }, metadata: { secret: { - access_token: 'dummy-access', + accessToken: 'dummy-access', refresh_token: 'dummy-refresh', developer_token: 'dummy-dev-token', }, diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts new file mode 100644 index 0000000000..233f160ad3 --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/audience.ts @@ -0,0 +1,129 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLAudienceRouterRequest: RouterTransformationRequest = { + input: [ + { + metadata: generateMetadata(1), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + add: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + { + metadata: generateMetadata(3), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + remove: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + { + metadata: generateMetadata(4), + destination: destination, + message: { + userId: 'user 1', + anonymousId: 'anon-id-new', + event: 'event1', + type: 'audiencelist', + properties: { + listData: { + remove: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + add: [ + { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + ], + }, + enablePartialFailure: true, + }, + context: { ip: '14.5.67.21', library: { name: 'http' } }, + timestamp: '2020-02-02T00:23:09.544Z', + }, + }, + ], + destType: 'google_adwords_remarketing_lists', +}; + +module.exports = { + rETLAudienceRouterRequest, +}; diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts index 31d5c72694..35da4daff5 100644 --- a/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/data.ts @@ -1,3 +1,6 @@ +import { rETLAudienceRouterRequest } from './audience'; +import { rETLRecordRouterRequest } from './record'; + export const data = [ { name: 'google_adwords_remarketing_lists', @@ -7,157 +10,85 @@ export const data = [ version: 'v0', input: { request: { + body: rETLAudienceRouterRequest, + method: 'POST', + }, + }, + output: { + response: { + status: 200, body: { - input: [ + output: [ { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 1, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'General', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', }, - enablePartialFailure: true, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 2, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'userID', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - thirdPartyUserId: 'useri1234', - }, - ], + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, }, - enablePartialFailure: true, - }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', - }, - jobId: 3, - userId: 'u1', - }, - destination: { - Config: { - rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', - listId: '7090784486', - customerId: '7693729833', - loginCustomerId: '', - subAccount: false, - userSchema: ['email', 'phone', 'addressInfo'], - isHashRequired: true, - typeOfList: 'General', - }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - remove: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], + body: { + JSON: { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, }, - enablePartialFailure: true, + files: {}, }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', - }, - }, - { - metadata: { - secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 1, }, - jobId: 4, - userId: 'u1', - }, + ], + batched: false, + statusCode: 200, destination: { Config: { rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', @@ -169,52 +100,21 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, - }, - message: { - userId: 'user 1', - anonymousId: 'anon-id-new', - event: 'event1', - type: 'audiencelist', - properties: { - listData: { - remove: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], - add: [ - { - email: 'test@abc.com', - phone: '@09876543210', - firstName: 'test', - lastName: 'rudderlabs', - country: 'US', - postalCode: '1245', - }, - ], - }, - enablePartialFailure: true, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', }, - context: { ip: '14.5.67.21', library: { name: 'http' } }, - timestamp: '2020-02-02T00:23:09.544Z', + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, - ], - destType: 'google_adwords_remarketing_lists', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: [ { batchedRequest: [ { @@ -222,11 +122,10 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', @@ -238,7 +137,7 @@ export const data = [ enablePartialFailure: true, operations: [ { - create: { + remove: { userIdentifiers: [ { hashedEmail: @@ -272,13 +171,16 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 1, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 3, }, ], batched: false, @@ -294,6 +196,19 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, { @@ -303,11 +218,10 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', @@ -318,7 +232,81 @@ export const data = [ JSON: { enablePartialFailure: true, operations: [ - { create: { userIdentifiers: [{ thirdPartyUserId: 'useri1234' }] } }, + { + remove: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', + headers: { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + }, + params: { + listId: '7090784486', + customerId: '7693729833', + consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + }, + body: { + JSON: { + enablePartialFailure: true, + operations: [ + { + create: { + userIdentifiers: [ + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, + ], + }, + }, ], }, JSON_ARRAY: {}, @@ -330,13 +318,16 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 2, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 4, }, ], batched: false, @@ -350,10 +341,45 @@ export const data = [ subAccount: false, userSchema: ['email', 'phone', 'addressInfo'], isHashRequired: true, - typeOfList: 'userID', + typeOfList: 'General', + }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, + ], + }, + }, + }, + }, + { + name: 'google_adwords_remarketing_lists record event tests', + description: 'Test 1', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: rETLRecordRouterRequest, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ { batchedRequest: [ { @@ -361,20 +387,21 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { remove: { @@ -411,16 +438,19 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, - jobId: 3, - userId: 'u1', + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 1, }, ], - batched: false, + batched: true, statusCode: 200, destination: { Config: { @@ -433,6 +463,19 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', }, }, { @@ -442,23 +485,24 @@ export const data = [ type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { - remove: { + create: { userIdentifiers: [ { hashedEmail: @@ -478,6 +522,24 @@ export const data = [ postalCode: '1245', }, }, + { + hashedEmail: + 'd3142c8f9c9129484daf28df80cc5c955791efed5e69afabb603bc8cb9ffd419', + }, + { + hashedPhoneNumber: + '8846dcb6ab2d73a0e67dbd569fa17cec2d9d391e5b05d1dd42919bc21ae82c45', + }, + { + addressInfo: { + hashedFirstName: + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', + hashedLastName: + 'dcf000c2386fb76d22cefc0d118a8511bb75999019cd373df52044bccd1bd251', + countryCode: 'US', + postalCode: '1245', + }, + }, ], }, }, @@ -489,25 +551,83 @@ export const data = [ }, files: {}, }, + ], + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 2, + }, + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + jobId: 3, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + }, + }, + { + batchedRequest: [ { version: '1', type: 'REST', method: 'POST', endpoint: - 'https://googleads.googleapis.com/v15/customers/7693729833/offlineUserDataJobs', + 'https://googleads.googleapis.com/v16/customers/7693729833/offlineUserDataJobs', headers: { - Authorization: 'Bearer abcd1234', + Authorization: 'Bearer default-accessToken', 'Content-Type': 'application/json', - 'developer-token': 'ijkl9101', }, params: { listId: '7090784486', customerId: '7693729833', - consent: { adPersonalization: 'UNSPECIFIED', adUserData: 'UNSPECIFIED' }, + consent: { + adPersonalization: 'UNSPECIFIED', + adUserData: 'UNSPECIFIED', + }, }, body: { JSON: { - enablePartialFailure: true, operations: [ { create: { @@ -544,16 +664,19 @@ export const data = [ ], metadata: [ { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, secret: { - access_token: 'abcd1234', - refresh_token: 'efgh5678', - developer_token: 'ijkl9101', + accessToken: 'default-accessToken', }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', jobId: 4, - userId: 'u1', }, ], - batched: false, + batched: true, statusCode: 200, destination: { Config: { @@ -566,6 +689,48 @@ export const data = [ isHashRequired: true, typeOfList: 'General', }, + DestinationDefinition: { + Config: {}, + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + }, + Enabled: true, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + IsConnectionEnabled: true, + IsProcessorEnabled: true, + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Transformations: [], + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + }, + }, + { + metadata: [ + { + attemptNum: 1, + destinationId: 'default-destinationId', + dontBatch: false, + secret: { + accessToken: 'default-accessToken', + }, + sourceId: 'default-sourceId', + userId: 'default-userId', + workspaceId: 'default-workspaceId', + jobId: 5, + }, + ], + batched: false, + statusCode: 400, + error: 'Invalid action type in record event', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destinationId: 'default-destinationId', + destType: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + module: 'destination', + implementation: 'native', + feature: 'router', + workspaceId: 'default-workspaceId', }, }, ], diff --git a/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts b/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts new file mode 100644 index 0000000000..743213bcc1 --- /dev/null +++ b/test/integrations/destinations/google_adwords_remarketing_lists/router/record.ts @@ -0,0 +1,158 @@ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + Config: { + rudderAccountId: '258Yea7usSKNpbkIaesL9oJ9iYw', + listId: '7090784486', + customerId: '7693729833', + loginCustomerId: '', + subAccount: false, + userSchema: ['email', 'phone', 'addressInfo'], + isHashRequired: true, + typeOfList: 'General', + }, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Enabled: true, + WorkspaceID: '1TSN08muJTZwH8iCDmnnRt1pmLd', + DestinationDefinition: { + ID: '1aIXqM806xAVm92nx07YwKbRrO9', + Name: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + DisplayName: 'GOOGLE_ADWORDS_REMARKETING_LISTS', + Config: {}, + }, + Transformations: [], + IsConnectionEnabled: true, + IsProcessorEnabled: true, +}; + +export const rETLRecordRouterRequest: RouterTransformationRequest = { + input: [ + { + destination: destination, + message: { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(2), + }, + { + destination: destination, + message: { + action: 'update', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(4), + }, + { + destination: destination, + message: { + action: 'delete', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(1), + }, + { + destination: destination, + message: { + action: 'lol', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(5), + }, + { + destination: destination, + message: { + action: 'insert', + context: { + ip: '14.5.67.21', + library: { + name: 'http', + }, + }, + recordId: '2', + rudderId: '2', + fields: { + email: 'test@abc.com', + phone: '@09876543210', + firstName: 'test', + lastName: 'rudderlabs', + country: 'US', + postalCode: '1245', + }, + type: 'record', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'google_adwords_remarketing_lists', +}; + +module.exports = { + rETLRecordRouterRequest, +}; diff --git a/test/integrations/destinations/heap/processor/data.ts b/test/integrations/destinations/heap/processor/data.ts index 6efa45435c..f503134148 100644 --- a/test/integrations/destinations/heap/processor/data.ts +++ b/test/integrations/destinations/heap/processor/data.ts @@ -961,4 +961,105 @@ export const data = [ }, }, }, + { + name: 'heap', + description: 'Test 8 -> Identify: No userId is present', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + appId: '', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + DisplayName: 'Heap.io', + ID: '1WTbl0l5GjOQKOvfmcGwk0T49kV', + Name: 'HEAP', + }, + Enabled: true, + ID: '1WTcDSEOE437e4ePH10BJNELXmE', + Name: 'heap test', + Transformations: [], + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + message: { + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + ip: '0.0.0.0', + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + locale: 'en-US', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', + }, + integrations: { + All: true, + }, + traits: { + email: 'sampath@gmail.com', + }, + messageId: 'fca2e71a-5d30-48e1-ba45-761c16e3820f', + originalTimestamp: '2020-01-16T13:21:59.076Z', + receivedAt: '2020-01-16T18:52:03.871+05:30', + request_ip: '[::1]:62312', + sentAt: '2020-01-16T13:22:03.85Z', + timestamp: '2020-01-16T18:51:59.097+05:30', + type: 'identify', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'userId is required: Workflow: procWorkflow, Step: validateuserId, ChildStep: undefined, OriginalError: userId is required', + statTags: { + destinationId: 'destId', + workspaceId: 'wspId', + destType: 'HEAP', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + }, + statusCode: 400, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/hs/network.ts b/test/integrations/destinations/hs/network.ts index e29cc27562..3d3b8fd83f 100644 --- a/test/integrations/destinations/hs/network.ts +++ b/test/integrations/destinations/hs/network.ts @@ -460,6 +460,37 @@ export const networkCallsData = [ status: 200, }, }, + { + httpReq: { + url: 'https://api.hubapi.com/crm/v3/objects/contacts/search', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer dummy-access-token-hs-additonal-email', + }, + }, + httpRes: { + data: { + total: 1, + results: [ + { + id: '103689', + properties: { + createdate: '2022-07-15T15:25:08.975Z', + email: 'primary@email.com', + hs_object_id: '103604', + hs_additional_emails: 'abc@extraemail.com;secondary@email.com', + lastmodifieddate: '2022-07-15T15:26:49.590Z', + }, + createdAt: '2022-07-15T15:25:08.975Z', + updatedAt: '2022-07-15T15:26:49.590Z', + archived: false, + }, + ], + }, + status: 200, + }, + }, { httpReq: { url: 'https://api.hubapi.com/crm/v3/objects/contacts/search', diff --git a/test/integrations/destinations/hs/processor/data.ts b/test/integrations/destinations/hs/processor/data.ts index f45f3a719b..f503ae92ac 100644 --- a/test/integrations/destinations/hs/processor/data.ts +++ b/test/integrations/destinations/hs/processor/data.ts @@ -1534,7 +1534,6 @@ export const data = [ firstname: 'Test Hubspot', anonymousId: '12345', country: 'India', - email: 'testhubspot2@email.com', }, }, XML: {}, @@ -5373,4 +5372,57 @@ export const data = [ }, }, }, + { + name: 'hs', + description: 'if event name is anything other than string we throw error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'web', + context: { + traits: { + email: 'testhubspot2@email.com', + firstname: 'Test Hubspot', + }, + }, + type: 'track', + originalTimestamp: '2019-10-15T09:35:31.291Z', + userId: '12345', + event: { name: 'event' }, + properties: { + user_actual_role: 'system_admin, system_user', + user_actual_id: 12345, + }, + sentAt: '2019-10-14T11:15:53.296Z', + }, + destination: destination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event is a required field and should be a string', + statTags: { + destType: 'HS', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/hs/router/data.ts b/test/integrations/destinations/hs/router/data.ts index 3a30232f9f..ab3ca8cba8 100644 --- a/test/integrations/destinations/hs/router/data.ts +++ b/test/integrations/destinations/hs/router/data.ts @@ -1033,7 +1033,6 @@ export const data = [ firstname: 'Test Hubspot', anonymousId: '12345', country: 'India', - email: 'testhubspot2@email.com', }, id: '103605', }, @@ -1688,4 +1687,145 @@ export const data = [ }, }, }, + { + name: 'hs', + description: 'getting duplicate records for secondary property', + feature: 'router', + module: 'destination', + version: 'v0', + scenario: 'buisness', + successCriteria: + 'should return 200 status code with contact needs to be updated and no email property', + input: { + request: { + body: { + input: [ + { + message: { + type: 'identify', + sentAt: '2024-03-19T18:46:36.348Z', + traits: { + lastname: 'Peñarete', + firstname: 'Karen', + }, + userId: 'secondary@email.com', + channel: 'sources', + context: { + externalId: [ + { + id: 'secondary@email.com', + type: 'HS-contacts', + identifierType: 'email', + }, + ], + mappedToDestination: 'true', + }, + originalTimestamp: '2024-03-19T18:46:36.348Z', + }, + metadata: { jobId: 3, userId: 'u1' }, + destination: { + Config: { + authorizationType: 'newPrivateAppApi', + accessToken: 'dummy-access-token-hs-additonal-email', + hubID: 'dummy-hubId', + apiKey: 'dummy-apikey', + apiVersion: 'newApi', + lookupField: 'email', + hubspotEvents: [], + }, + secretConfig: {}, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + name: 'Hubspot', + enabled: true, + workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', + deleted: false, + createdAt: '2020-12-30T08:39:32.005Z', + updatedAt: '2021-02-03T16:22:31.374Z', + destinationDefinition: { + id: '1aIXqM806xAVm92nx07YwKbRrO9', + name: 'HS', + displayName: 'Hubspot', + createdAt: '2020-04-09T09:24:31.794Z', + updatedAt: '2021-01-11T11:03:28.103Z', + }, + transformations: [], + isConnectionEnabled: true, + isProcessorEnabled: true, + }, + }, + ], + destType: 'hs', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.hubapi.com/crm/v3/objects/contacts/batch/update', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer dummy-access-token-hs-additonal-email', + }, + params: {}, + body: { + JSON: { + inputs: [ + { + properties: { lastname: 'Peñarete', firstname: 'Karen' }, + id: '103689', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [{ jobId: 3, userId: 'u1' }], + batched: true, + statusCode: 200, + destination: { + Config: { + authorizationType: 'newPrivateAppApi', + accessToken: 'dummy-access-token-hs-additonal-email', + hubID: 'dummy-hubId', + apiKey: 'dummy-apikey', + apiVersion: 'newApi', + lookupField: 'email', + hubspotEvents: [], + }, + secretConfig: {}, + ID: '1mMy5cqbtfuaKZv1IhVQKnBdVwe', + name: 'Hubspot', + enabled: true, + workspaceId: '1TSN08muJTZwH8iCDmnnRt1pmLd', + deleted: false, + createdAt: '2020-12-30T08:39:32.005Z', + updatedAt: '2021-02-03T16:22:31.374Z', + destinationDefinition: { + id: '1aIXqM806xAVm92nx07YwKbRrO9', + name: 'HS', + displayName: 'Hubspot', + createdAt: '2020-04-09T09:24:31.794Z', + updatedAt: '2021-01-11T11:03:28.103Z', + }, + transformations: [], + isConnectionEnabled: true, + isProcessorEnabled: true, + }, + }, + ], + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/impact/processor/data.ts b/test/integrations/destinations/impact/processor/data.ts index e467956d62..0841f44c62 100644 --- a/test/integrations/destinations/impact/processor/data.ts +++ b/test/integrations/destinations/impact/processor/data.ts @@ -26,7 +26,7 @@ export const data = [ namespace: 'com.rudderlabs.javascript', }, traits: { - email: 'user123@email.com', + email: ' user123@email.com', phone: '+917836362334', userId: 'user123', }, @@ -891,6 +891,7 @@ export const data = [ price: 332, quantity: 1, sku: 'G-32', + customRSProductField: 'customRSVal', }, ], }, @@ -941,6 +942,10 @@ export const data = [ from: 'variant', to: 'ItemCategory', }, + { + from: 'customRSProductField', + to: 'customImpactProductField', + }, ], enableIdentifyEvents: false, enablePageEvents: false, @@ -990,6 +995,7 @@ export const data = [ DeviceLocale: 'en-US', EventTypeCode: 'Order Completed', ItemQuantity1: 1, + customImpactProductField1: 'customRSVal', OrderPromoCode: '10OFF-ROCKET', CustomProfileId: '97c46c81-3140-456d-b2a9-690d70aaca35', }, diff --git a/test/integrations/destinations/intercom/dataDelivery/business.ts b/test/integrations/destinations/intercom/dataDelivery/business.ts new file mode 100644 index 0000000000..2490041832 --- /dev/null +++ b/test/integrations/destinations/intercom/dataDelivery/business.ts @@ -0,0 +1,615 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const unauthorizedResponseHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer invalidApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const createUserPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const conflictUserPayload = { + email: 'test_1@test.com', + name: 'Rudder Labs', + signed_up_at: 1601496060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_2', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const updateUserPayload = { + email: 'test_1@test.com', + phone: '9876543211', + name: 'Sample Name', + signed_up_at: 1601493060, + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const createCompanyPayload = { + company_id: 'rudderlabs', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + custom_attributes: { isOpenSource: true }, +}; + +const sendMessagePayload = { + from: { + type: 'user', + id: 'id@1', + }, + body: 'heyy, how are you', + referer: 'https://twitter.com/bob', +}; + +const createUserRequestParameters = { + JSON: createUserPayload, + headers: commonHeaders, +}; + +const updateUserRequestParameters = { + JSON: updateUserPayload, + headers: commonHeaders, +}; + +const createCompanyRequestParameters = { + JSON: createCompanyPayload, + headers: commonHeaders, +}; + +const sendMessageRequestParameters = { + JSON: sendMessagePayload, + headers: commonHeaders, +}; + +const metadataArray = [generateMetadata(1)]; + +export const testScenariosForV0API = [ + { + id: 'intercom_v0_other_scenario_1', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Invalid Credentials Handling during Destination Authentication', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...createUserRequestParameters, + headers: unauthorizedResponseHeaders, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + request_id: 'request123', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_2', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...createCompanyRequestParameters, + endpoint: 'https://api.eu.intercom.io/companies', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'parameter_invalid', + message: "Custom attribute 'isOpenSource' does not exist", + }, + ], + request_id: 'request_1', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_3', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Plan-Restricted Response Handling from Destination', + successCriteria: 'Should return 403 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...sendMessageRequestParameters, + endpoint: 'https://api.intercom.io/messages', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'api_plan_restricted', + message: 'Active subscription needed.', + }, + ], + request_id: 'request124', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 403, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_4', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Rate Limit Exceeded Handling from Destination', + successCriteria: 'Should return 429 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...updateUserRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 429, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'rate_limit_exceeded', + message: 'The rate limit for the App has been exceeded', + }, + ], + request_id: 'request125', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 429, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_5', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Conflict User Handling from Destination', + successCriteria: 'Should return 409 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: conflictUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/contacts', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 409, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'conflict', + message: 'A contact matching those details already exists with id=test1', + }, + ], + request_id: 'request126', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 409, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_6', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Unsupported Media Handling from Destination', + successCriteria: 'Should return 406 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: createUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/users', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 406, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'media_type_not_acceptable', + message: 'The Accept header should send a media type of application/json', + }, + ], + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 406, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'intercom_v1_other_scenario_1', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Invalid Credentials Handling during Destination Authentication', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...createUserRequestParameters, + headers: unauthorizedResponseHeaders, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request123","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]}', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_2', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...createCompanyRequestParameters, + endpoint: 'https://api.eu.intercom.io/companies', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request_1","errors":[{"code":"parameter_invalid","message":"Custom attribute \'isOpenSource\' does not exist"}]}', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_3', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Plan-Restricted Response Handling from Destination', + successCriteria: 'Should return 403 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...sendMessageRequestParameters, + endpoint: 'https://api.intercom.io/messages', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request124","errors":[{"code":"api_plan_restricted","message":"Active subscription needed."}]}', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + status: 403, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_4', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Rate Limit Exceeded Handling from Destination', + successCriteria: 'Should return 429 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...updateUserRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request125","errors":[{"code":"rate_limit_exceeded","message":"The rate limit for the App has been exceeded"}]}', + metadata: generateMetadata(1), + statusCode: 429, + }, + ], + status: 429, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_5', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Conflict User Handling from Destination', + successCriteria: 'Should return 409 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: conflictUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/contacts', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request126","errors":[{"code":"conflict","message":"A contact matching those details already exists with id=test1"}]}', + metadata: generateMetadata(1), + statusCode: 409, + }, + ], + status: 409, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_6', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Unsupported Media Handling from Destination', + successCriteria: 'Should return 406 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: createUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/users', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"errors":[{"code":"media_type_not_acceptable","message":"The Accept header should send a media type of application/json"}],"type":"error.list"}', + metadata: generateMetadata(1), + statusCode: 406, + }, + ], + status: 406, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/dataDelivery/data.ts b/test/integrations/destinations/intercom/dataDelivery/data.ts index db7aafc963..e5d2f4696b 100644 --- a/test/integrations/destinations/intercom/dataDelivery/data.ts +++ b/test/integrations/destinations/intercom/dataDelivery/data.ts @@ -1,91 +1,9 @@ +import { otherScenariosV0, otherScenariosV1 } from './other'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'intercom', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users/test1', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - update_last_request_at: true, - user_id: 'test_user_id_1', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - 'address.city': 'Kolkata', - 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', - }, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 500, - body: { - output: { - status: 500, - message: - '[Intercom Response Handler] Request failed for destination intercom with status: 408', - destinationResponse: { - response: { - type: 'error.list', - request_id: '000on04msi4jpk7d3u60', - errors: [ - { - code: 'Request Timeout', - message: 'The server would not wait any longer for the client', - }, - ], - }, - status: 408, - }, - statTags: { - destType: 'INTERCOM', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'retryable', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - }, - }, - }, - }, - }, + ...otherScenariosV0, + ...otherScenariosV1, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/intercom/dataDelivery/other.ts b/test/integrations/destinations/intercom/dataDelivery/other.ts new file mode 100644 index 0000000000..46cdcac468 --- /dev/null +++ b/test/integrations/destinations/intercom/dataDelivery/other.ts @@ -0,0 +1,384 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const createUserPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const commonRequestParameters = { + JSON: createUserPayload, + headers: commonHeaders, +}; + +const expectedStatTags = { + destType: 'INTERCOM', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +const metadataArray = [generateMetadata(1)]; + +export const otherScenariosV0 = [ + { + id: 'intercom_v0_other_scenario_1', + name: 'intercom', + description: '[Proxy v0 API] :: Request Timeout Error Handling from Destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Intercom Response Handler] Request failed for destination intercom with status: 408', + destinationResponse: { + response: { + type: 'error.list', + request_id: '000on04msi4jpk7d3u60', + errors: [ + { + code: 'Request Timeout', + message: 'The server would not wait any longer for the client', + }, + ], + }, + status: 408, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_2', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 503, + body: { + output: { + status: 503, + message: 'Request Processed Successfully', + destinationResponse: { + type: 'error.list', + request_id: 'request127', + errors: [ + { + code: 'service_unavailable', + message: 'Sorry, the API service is temporarily unavailable', + }, + ], + }, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_3', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test3', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'client_error', + message: 'Unknown server error', + }, + ], + request_id: 'request128', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_4', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test4', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 504, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'server_timeout', + message: 'Server timed out when making request', + }, + ], + request_id: 'request129', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 504, + }, + }, + }, + }, + }, +]; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'intercom_v1_other_scenario_1', + name: 'intercom', + description: '[Proxy v1 API] :: Request Timeout Error Handling from Destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Intercom Response Handler] Request failed for destination intercom with status: 408', + response: [ + { + error: + '{"type":"error.list","request_id":"000on04msi4jpk7d3u60","errors":[{"code":"Request Timeout","message":"The server would not wait any longer for the client"}]}', + metadata: generateMetadata(1), + statusCode: 500, + }, + ], + statTags: expectedStatTags, + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_2', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test2', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request127","errors":[{"code":"service_unavailable","message":"Sorry, the API service is temporarily unavailable"}]}', + metadata: generateMetadata(1), + statusCode: 503, + }, + ], + status: 503, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_3', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test3', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request128","errors":[{"code":"client_error","message":"Unknown server error"}]}', + metadata: generateMetadata(1), + statusCode: 500, + }, + ], + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_4', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test4', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request129","errors":[{"code":"server_timeout","message":"Server timed out when making request"}]}', + metadata: generateMetadata(1), + statusCode: 504, + }, + ], + status: 504, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/network.ts b/test/integrations/destinations/intercom/network.ts index 74c861259f..69f5ea1247 100644 --- a/test/integrations/destinations/intercom/network.ts +++ b/test/integrations/destinations/intercom/network.ts @@ -1,3 +1,183 @@ +const commonHeaders = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', +}; + +const v0VersionHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + 'User-Agent': 'RudderLabs', +}; + +const v1VersionHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '2.10', + 'User-Agent': 'RudderLabs', +}; + +const userPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const companyPayload = { + company_id: 'rudderlabs', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', +}; + +const v1Headers = { + 'Content-Type': 'application/json', + Authorization: 'Bearer abcd=', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const companyData1 = { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + plan: 'basic', + size: 50, + industry: 'IT', + monthly_spend: 2131231, + remote_created_at: 1683017572, + custom_attributes: { + key1: 'val1', + employees: 450, + email: 'test@test.com', + }, +}; + +const companyData2 = { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + website: 'url', + plan: 'basic', + size: 50, + industry: 'IT', + monthly_spend: 2131231, + remote_created_at: 1683017572, + custom_attributes: { + key1: 'val1', + employees: 450, + email: 'test@test.com', + 'key2.a': 'a', + 'key2.b': 'b', + 'key3[0]': 1, + 'key3[1]': 2, + 'key3[2]': 3, + key4: null, + }, +}; + +const companyData3 = { + company_id: 'test_company_id', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + monthly_spend: 2131231, + custom_attributes: { + email: 'comanyemail@abc.com', + }, +}; + +const userData1 = { + user_id: 'sdfrsdfsdfsf', + companies: [ + { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + }, + ], +}; + +const userData2 = { + email: 'testUser@test.com', + companies: [ + { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + }, + ], +}; + +const userData3 = { + user_id: 'sdfrsdfsdfsf', + email: 'testUser@test.com', + companies: [ + { + company_id: 'test_company_id_wdasda', + name: 'rudderUpdate', + }, + ], +}; + +const createCompanyDummyResp = { + type: 'company', + company_id: 'test_company_id_wdasda', + id: '657264e9018c0a647s45', + name: 'rudderUpdate', + website: 'url', + plan: 'basic', + size: 50, + industry: 'IT', + monthly_spend: 2131231, + remote_created_at: 1683017572, + created_at: 1701930212, + updated_at: 1701930212, + custom_attributes: { + key1: 'val1', + employees: 450, + email: 'test@test.com', + }, +}; + +const attachUserDummyResp = { + type: 'user', + id: '6662e5abd27951dd35e024e9', + user_id: 'user123', + anonymous: false, + email: 'test+5@rudderlabs.com', + app_id: '1234', + companies: { + type: 'company.list', + companies: [ + { + type: 'company', + company_id: 'company_id', + id: '6664ec390b9416d083be97fc', + name: 'Company', + }, + ], + }, + created_at: 1717757355, + updated_at: 1717890105, + tags: { + type: 'tag.list', + tags: [], + }, + custom_attributes: {}, +}; + const deleteNwData = [ { httpReq: { @@ -6,11 +186,7 @@ const deleteNwData = [ data: { intercom_user_id: '1', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { data: { @@ -33,11 +209,7 @@ const deleteNwData = [ data: { intercom_user_id: '12', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -54,11 +226,7 @@ const deleteNwData = [ data: { intercom_user_id: '7', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -75,11 +243,7 @@ const deleteNwData = [ data: { intercom_user_id: '9', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -89,6 +253,8 @@ const deleteNwData = [ }, }, }, +]; +const deliveryCallsData = [ { httpReq: { method: 'post', @@ -99,11 +265,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -131,11 +293,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+2@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -172,11 +330,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+5@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -213,11 +367,7 @@ const deleteNwData = [ value: [{ field: 'phone', operator: '=', value: '+91 9299999999' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -254,11 +404,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+4@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -310,19 +456,8 @@ const deleteNwData = [ httpReq: { method: 'post', url: 'https://api.eu.intercom.io/companies', - data: { - company_id: 'rudderlabs', - name: 'RudderStack', - website: 'www.rudderstack.com', - plan: 'enterprise', - size: 500, - industry: 'CDP', - }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + data: companyPayload, + headers: commonHeaders, }, httpRes: { status: 200, @@ -346,19 +481,10 @@ const deleteNwData = [ method: 'post', url: 'https://api.eu.intercom.io/companies', data: { - company_id: 'rudderlabs', - name: 'RudderStack', - website: 'www.rudderstack.com', - plan: 'enterprise', - size: 500, - industry: 'CDP', + ...companyPayload, custom_attributes: { isOpenSource: true }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 401, @@ -374,55 +500,463 @@ const deleteNwData = [ }, }, }, -]; -const deliveryCallsData = [ + { + httpReq: { + url: 'https://api.intercom.io/users/test1', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: '000on04msi4jpk7d3u60', + errors: [ + { + code: 'Request Timeout', + message: 'The server would not wait any longer for the client', + }, + ], + }, + status: 408, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test1', + data: userPayload, + params: {}, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer invalidApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request123', + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + }, + status: 401, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/messages', + data: { + from: { + type: 'user', + id: 'id@1', + }, + body: 'heyy, how are you', + referer: 'https://twitter.com/bob', + }, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request124', + errors: [ + { + code: 'api_plan_restricted', + message: 'Active subscription needed.', + }, + ], + }, + status: 403, + }, + }, { httpReq: { url: 'https://api.intercom.io/users/test1', data: { email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', + phone: '9876543211', + name: 'Sample Name', signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', update_last_request_at: true, user_id: 'test_user_id_1', custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', 'address.city': 'Kolkata', 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', }, }, params: {}, - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - 'User-Agent': 'RudderLabs', + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request125', + errors: [ + { + code: 'rate_limit_exceeded', + message: 'The rate limit for the App has been exceeded', + }, + ], }, + status: 429, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/contacts', + data: { + email: 'test_1@test.com', + name: 'Rudder Labs', + signed_up_at: 1601496060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_2', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, + }, + params: {}, + headers: v1VersionHeaders, method: 'POST', }, httpRes: { data: { type: 'error.list', - request_id: '000on04msi4jpk7d3u60', + request_id: 'request126', errors: [ { - code: 'Request Timeout', - message: 'The server would not wait any longer for the client', + code: 'conflict', + message: 'A contact matching those details already exists with id=test1', }, ], }, - status: 408, + status: 409, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users', + data: userPayload, + params: {}, + headers: v1VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + errors: [ + { + code: 'media_type_not_acceptable', + message: 'The Accept header should send a media type of application/json', + }, + ], + type: 'error.list', + }, + status: 406, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test2', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request127', + errors: [ + { + code: 'service_unavailable', + message: 'Sorry, the API service is temporarily unavailable', + }, + ], + }, + status: 503, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test3', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request128', + errors: [ + { + code: 'client_error', + message: 'Unknown server error', + }, + ], + }, + status: 500, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test4', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request129', + errors: [ + { + code: 'server_timeout', + message: 'Server timed out when making request', + }, + ], + }, + status: 504, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.eu.intercom.io/contacts/70701240741e45d040/companies', + data: { + id: '657264e9018c0a647s45', + }, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'company', + company_id: 'rudderlabs', + id: '657264e9018c0a647s45', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + remote_created_at: 1374138000, + created_at: 1701930212, + updated_at: 1701930212, + }, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: companyPayload, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'company', + company_id: 'rudderlabs', + id: '657264e9018c0a647s45', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + remote_created_at: 1374138000, + created_at: 1701930212, + updated_at: 1701930212, + }, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/users', + data: { + user_id: 'user@5', + email: 'test+5@rudderlabs.com', + companies: [ + { + company_id: 'rudderlabs', + name: 'RudderStack', + }, + ], + }, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'company', + company_id: 'rudderlabs', + id: '657264e9018c0a647s45', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + remote_created_at: 1374138000, + created_at: 1701930212, + updated_at: 1701930212, + companies: { + type: 'company.list', + companies: [ + { + type: 'company', + company_id: 'rudderlabs', + id: '657264e9018c0a647s45', + name: 'RudderStack', + }, + ], + }, + }, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: companyData1, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: createCompanyDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: { + ...companyData1, + website: 'url', + }, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: createCompanyDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: companyData2, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: createCompanyDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/companies', + data: companyData3, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: createCompanyDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/users', + data: userData1, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: attachUserDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/users', + data: userData2, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: attachUserDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.intercom.io/users', + data: userData3, + headers: v1Headers, + }, + httpRes: { + status: 200, + data: attachUserDummyResp, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.eu.intercom.io/tags', + data: { + name: 'tag1', + companies: [ + { + id: '657264e9018c0a647s45', + }, + ], + }, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'tag', + name: 'tag1', + id: '123', + }, + }, + }, + { + httpReq: { + method: 'post', + url: 'https://api.eu.intercom.io/tags', + data: { + name: 'tag2', + companies: [ + { + id: '657264e9018c0a647s45', + }, + ], + }, + headers: commonHeaders, + }, + httpRes: { + status: 200, + data: { + type: 'tag', + name: 'tag2', + id: '123', + }, }, }, ]; diff --git a/test/integrations/destinations/intercom/processor/data.ts b/test/integrations/destinations/intercom/processor/data.ts index 2c562ed4e9..7bc697bc2d 100644 --- a/test/integrations/destinations/intercom/processor/data.ts +++ b/test/integrations/destinations/intercom/processor/data.ts @@ -1,4149 +1,11 @@ +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { groupTestData } from './groupTestData'; +import { validationTestData } from './validationTestData'; + export const data = [ - { - name: 'intercom', - description: 'No message type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - age: 23, - email: 'adc@test.com', - firstname: 'Test', - birthday: '2022-05-13T12:51:01.470Z', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36', - }, - event: 'Product Searched', - originalTimestamp: '2020-09-22T14:42:44.724Z', - timestamp: '2022-09-22T20:12:44.757+05:30', - userId: 'user@1', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 1, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 1, - }, - statusCode: 400, - error: - 'message Type is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message Type is not present. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Unsupported message type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - traits: { - age: 23, - email: 'adc@test.com', - firstname: 'Test', - birthday: '2022-05-13T12:51:01.470Z', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36', - }, - event: 'Product Searched', - type: 'page', - originalTimestamp: '2020-09-22T14:42:44.724Z', - timestamp: '2022-09-22T20:12:44.757+05:30', - userId: 'user@1', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 2, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 2, - }, - statusCode: 400, - error: - 'message type page is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type page is not supported', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Missing required config', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - context: { - traits: { - age: 23, - email: 'adc@test.com', - firstName: 'Test', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 3, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 3, - }, - statusCode: 400, - error: - 'Access Token is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Access Token is not present. Aborting', - statTags: { - errorCategory: 'dataValidation', - errorType: 'configuration', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Create customer with email as lookup field', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - context: { - traits: { - age: 23, - email: 'test@rudderlabs.com', - phone: '+91 9999999999', - firstName: 'Test', - lastName: 'Rudderlabs', - address: 'california usa', - ownerId: '13', - lastSeenAt: '2023-11-10T14:42:44.724Z', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 4, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - email: 'test@rudderlabs.com', - external_id: 'user@1', - last_seen_at: 1699627364, - name: 'Test Rudderlabs', - owner_id: 13, - phone: '+91 9999999999', - custom_attributes: { - address: 'california usa', - age: 23, - }, - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/contacts', - headers: { - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'Intercom-Version': '2.10', - }, - userId: '', - version: '1', - type: 'REST', - method: 'POST', - files: {}, - params: {}, - }, - metadata: { jobId: 4 }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Update customer with email as lookup field', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@2', - channel: 'web', - context: { - traits: { - age: 32, - email: 'test+2@rudderlabs.com', - phone: '+91 9299999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '14', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 5, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - email: 'test+2@rudderlabs.com', - external_id: 'user@2', - name: 'Test RudderStack', - owner_id: 14, - phone: '+91 9299999999', - custom_attributes: { - age: 32, - }, - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/contacts/7070129940741e45d040', - headers: { - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'Intercom-Version': '2.10', - }, - userId: '', - version: '1', - type: 'REST', - method: 'PUT', - files: {}, - params: {}, - }, - metadata: { jobId: 5 }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Missing required parameters for an identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: 'anon@2', - channel: 'web', - context: { - traits: { - age: 32, - phone: '+91 9299999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '14', - role: 'user', - source: 'rudder-sdk', - }, - }, - integrations: { - INTERCOM: { - lookup: 'phone', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 6, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 6, - }, - statusCode: 400, - error: - 'Either email or userId is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForLatestVersion, ChildStep: undefined, OriginalError: Either email or userId is required for Identify call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Unauthorized error while searching contact for an identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@3', - channel: 'web', - context: { - traits: { - phone: '+91 9399999999', - email: 'test+3@rudderlabs.com', - firstName: 'Test', - lastName: 'Rudder', - ownerId: '15', - role: 'admin', - source: 'rudder-android-sdk', - }, - }, - integrations: { - INTERCOM: { - lookup: 'email', - }, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'invalidApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 7, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 7, - }, - statusCode: 401, - error: - '{"message":"{\\"message\\":\\"Unable to search contact due to : [{\\\\\\"code\\\\\\":\\\\\\"unauthorized\\\\\\",\\\\\\"message\\\\\\":\\\\\\"Access Token Invalid\\\\\\"}]: Workflow: procWorkflow, Step: searchContact, ChildStep: undefined, OriginalError: Unable to search contact due to : [{\\\\\\"code\\\\\\":\\\\\\"unauthorized\\\\\\",\\\\\\"message\\\\\\":\\\\\\"Access Token Invalid\\\\\\"}]\\",\\"destinationResponse\\":{\\"response\\":{\\"type\\":\\"error.list\\",\\"request_id\\":\\"request_1\\",\\"errors\\":[{\\"code\\":\\"unauthorized\\",\\"message\\":\\"Access Token Invalid\\"}]},\\"status\\":401}}","destinationResponse":{"response":{"type":"error.list","request_id":"request_1","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]},"status":401}}', - statTags: { - errorCategory: 'network', - errorType: 'aborted', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Track call without event name', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@3', - channel: 'web', - context: { - traits: { - age: 32, - email: 'test+3@rudderlabs.com', - phone: '+91 9399999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '15', - }, - }, - properties: { - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - }, - type: 'track', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 8, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 8, - }, - statusCode: 400, - error: - 'Event name is required for track call: Workflow: procWorkflow, Step: trackPayload, ChildStep: undefined, OriginalError: Event name is required for track call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Successful track call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@2', - channel: 'web', - context: { - traits: { - age: 32, - email: 'test+2@rudderlabs.com', - phone: '+91 9299999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '14', - }, - }, - properties: { - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - }, - event: 'Product Viewed', - type: 'track', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 9, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - FORM: {}, - JSON: { - created_at: 1700628164, - email: 'test+2@rudderlabs.com', - event_name: 'Product Viewed', - metadata: { - price: { - amount: 3000, - currency: 'USD', - }, - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - }, - user_id: 'user@2', - }, - JSON_ARRAY: {}, - XML: {}, - }, - endpoint: 'https://api.intercom.io/events', - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - 'Intercom-Version': '2.10', - }, - method: 'POST', - type: 'REST', - userId: '', - version: '1', - params: {}, - files: {}, - }, - statusCode: 200, - metadata: { - jobId: 9, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Group call without groupId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@4', - channel: 'web', - context: { - traits: { - email: 'test+4@rudderlabs.com', - phone: '+91 9499999999', - firstName: 'John', - lastName: 'Doe', - ownerId: '16', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 10, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - jobId: 10, - }, - statusCode: 400, - error: - 'groupId is required for group call: Workflow: procWorkflow, Step: groupPayloadForLatestVersion, ChildStep: validateMessageAndPreparePayload, OriginalError: groupId is required for group call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Successful group call to create or update company', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@4', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+4@rudderlabs.com', - phone: '+91 9499999999', - firstName: 'John', - lastName: 'Doe', - ownerId: '16', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 11, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - company_id: 'rudderlabs', - industry: 'CDP', - name: 'RudderStack', - plan: 'enterprise', - size: 500, - website: 'www.rudderstack.com', - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/companies', - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - 'Intercom-Version': '2.10', - }, - method: 'POST', - type: 'REST', - userId: '', - version: '1', - params: {}, - files: {}, - }, - statusCode: 200, - metadata: { - jobId: 11, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Successful group call to add user to company', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@5', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'eu', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 12, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - id: '657264e9018c0a647s45', - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.eu.intercom.io/contacts/70701240741e45d040/companies', - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - 'Intercom-Version': '2.10', - }, - method: 'POST', - type: 'REST', - userId: '', - version: '1', - params: {}, - files: {}, - }, - statusCode: 200, - metadata: { - jobId: 12, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Identify rEtl test', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - externalId: [ - { - id: 'user@1', - type: 'INTERCOM-customer', - identifierType: 'user_id', - }, - ], - mappedToDestination: 'true', - }, - traits: { - email: 'test@rudderlabs.com', - phone: '+91 9999999999', - name: 'Test Rudderlabs', - owner_id: 13, - }, - type: 'identify', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 13, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - email: 'test@rudderlabs.com', - name: 'Test Rudderlabs', - phone: '+91 9999999999', - owner_id: 13, - user_id: 'user@1', - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/contacts', - headers: { - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'Intercom-Version': '2.10', - }, - userId: '', - version: '1', - type: 'REST', - method: 'POST', - files: {}, - params: {}, - }, - metadata: { jobId: 13 }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Track rEtl test', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - userId: 'user@1', - channel: 'web', - context: { - mappedToDestination: 'true', - }, - traits: { - event_name: 'Product Viewed', - user_id: 'user@1', - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - }, - event: 'Product Viewed', - type: 'track', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'standard', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 14, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - JSON: { - event_name: 'Product Viewed', - price: { - amount: 3000, - currency: 'USD', - }, - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - user_id: 'user@1', - }, - XML: {}, - FORM: {}, - JSON_ARRAY: {}, - }, - endpoint: 'https://api.intercom.io/events', - headers: { - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'Intercom-Version': '2.10', - }, - userId: '', - version: '1', - type: 'REST', - method: 'POST', - files: {}, - params: {}, - }, - metadata: { jobId: 14 }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - address: { - city: 'Kolkata', - state: 'West Bengal', - }, - originalArray: [ - { - nested_field: 'nested value', - tags: ['tag_1', 'tag_2', 'tag_3'], - }, - { - nested_field: 'nested value', - tags: ['tag_1'], - }, - { - nested_field: 'nested value', - }, - ], - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 15, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - 'address.city': 'Kolkata', - 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', - }, - update_last_request_at: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 15, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 16, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Test Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 16, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 17, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 17, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 18, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 18, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old Version: Identify call without email and userId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 19, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Either of `email` or `userId` is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForOlderVersion, ChildStep: undefined, OriginalError: Either of `email` or `userId` is required for Identify call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - metadata: { - jobId: 19, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - company: { - name: 'Test Comp', - id: 'company_id', - industry: 'test industry', - key1: 'value1', - key2: { - a: 'a', - }, - key3: [1, 2, 3], - }, - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 20, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - companies: [ - { - company_id: 'company_id', - custom_attributes: { - key1: 'value1', - key2: '{"a":"a"}', - key3: '[1,2,3]', - }, - name: 'Test Comp', - industry: 'test industry', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 20, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - company: { - name: 'Test Comp', - industry: 'test industry', - key1: 'value1', - key2: null, - key3: ['value1', 'value2'], - key4: { - foo: 'bar', - }, - }, - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - updateLastRequestAt: false, - }, - }, - metadata: { - jobId: 21, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: false, - name: 'Name', - companies: [ - { - company_id: 'c0277b5c814453e5135f515f943d085a', - custom_attributes: { - key1: 'value1', - key3: '["value1","value2"]', - key4: '{"foo":"bar"}', - }, - name: 'Test Comp', - industry: 'test industry', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 21, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - company: { - industry: 'test industry', - key1: 'value1', - key2: null, - }, - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 22, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - companies: [], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 22, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful track call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - properties: { - property1: 1, - property2: 'test', - property3: true, - property4: '2020-10-05T09:09:03.731Z', - property5: { - property1: 1, - property2: 'test', - property3: { - subProp1: { - a: 'a', - b: 'b', - }, - subProp2: ['a', 'b'], - }, - }, - properties6: null, - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - article: { - url: 'https://example.org/ab1de.html', - value: 'the dude abides', - }, - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'track', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 23, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/events', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'test_user_id_1', - email: 'test_1@test.com', - event_name: 'Test Event 2', - created: 1601493061, - metadata: { - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - article: { - url: 'https://example.org/ab1de.html', - value: 'the dude abides', - }, - property1: 1, - property2: 'test', - property3: true, - property4: '2020-10-05T09:09:03.731Z', - 'property5.property1': 1, - 'property5.property2': 'test', - 'property5.property3.subProp1.a': 'a', - 'property5.property3.subProp1.b': 'b', - 'property5.property3.subProp2[0]': 'a', - 'property5.property3.subProp2[1]': 'b', - properties6: null, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 23, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version - successful track call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'track', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 24, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/events', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - event_name: 'Test Event 2', - created: 1601493061, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 24, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : Track call without email or userId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'track', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 25, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Either email or userId is required for Track call: Workflow: procWorkflow, Step: trackPayload, ChildStep: undefined, OriginalError: Either email or userId is required for Track call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - metadata: { - jobId: 25, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - externalId: [ - { - id: '10156', - type: 'INTERCOM-customer', - identifierType: 'user_id', - }, - ], - mappedToDestination: 'true', - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - metadata: { - jobId: 26, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - update_last_request_at: true, - user_id: '10156', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 26, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - sendAnonymousId: true, - }, - }, - metadata: { - jobId: 27, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - user_id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - update_last_request_at: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - metadata: { - jobId: 27, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : Identify call without email or userId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'intercomApiKey', - apiVersion: 'v1', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 28, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - statusCode: 400, - error: - 'Either of `email` or `userId` is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForOlderVersion, ChildStep: undefined, OriginalError: Either of `email` or `userId` is required for Identify call', - statTags: { - errorCategory: 'dataValidation', - errorType: 'instrumentation', - destType: 'INTERCOM', - module: 'destination', - implementation: 'cdkV2', - feature: 'processor', - }, - metadata: { - jobId: 28, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - groupId: 'test_company_id_wdasda', - traits: { - employees: 450, - plan: 'basic', - userId: 'sdfrsdfsdfsf', - email: 'test@test.com', - name: 'rudderUpdate', - size: '50', - industry: 'IT', - monthlySpend: '2131231', - remoteCreatedAt: '1683017572', - key1: 'val1', - }, - anonymousId: 'sdfrsdfsdfsf', - integrations: { - All: true, - }, - type: 'group', - userId: 'sdfrsdfsdfsf', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'abcd=', - appId: 'asdasdasd', - apiVersion: 'v1', - collectContext: false, - }, - }, - metadata: { - jobId: 29, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/companies', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - plan: 'basic', - size: 50, - industry: 'IT', - monthly_spend: 2131231, - remote_created_at: 1683017572, - custom_attributes: { - employees: 450, - email: 'test@test.com', - key1: 'val1', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'sdfrsdfsdfsf', - }, - statusCode: 200, - metadata: { - jobId: 29, - }, - }, - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'sdfrsdfsdfsf', - companies: [ - { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'sdfrsdfsdfsf', - }, - statusCode: 200, - metadata: { - jobId: 29, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - groupId: 'test_company_id', - traits: { - plan: 'basic', - name: 'rudderUpdate', - size: 50, - industry: 'IT', - monthlySpend: '2131231', - email: 'comanyemail@abc.com', - }, - anonymousId: '12312312', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - userAgent: 'unknown', - }, - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - type: 'group', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'abcd=', - apiVersion: 'v1', - appId: 'asdasdasd', - collectContext: false, - }, - }, - metadata: { - jobId: 30, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/companies', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - company_id: 'test_company_id', - name: 'rudderUpdate', - plan: 'basic', - size: 50, - industry: 'IT', - monthly_spend: 2131231, - custom_attributes: { - email: 'comanyemail@abc.com', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '12312312', - }, - statusCode: 200, - metadata: { - jobId: 30, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - groupId: 'test_company_id_wdasda', - context: { - traits: { - email: 'testUser@test.com', - }, - }, - traits: { - employees: 450, - plan: 'basic', - email: 'test@test.com', - name: 'rudderUpdate', - size: '50', - industry: 'IT', - website: 'url', - monthlySpend: '2131231', - remoteCreatedAt: '1683017572', - key1: 'val1', - }, - anonymousId: 'sdfrsdfsdfsf', - integrations: { - All: true, - }, - type: 'group', - userId: 'sdfrsdfsdfsf', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'abcd=', - apiVersion: 'v1', - appId: 'asdasdasd', - collectContext: false, - }, - }, - metadata: { - jobId: 31, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/companies', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - plan: 'basic', - size: 50, - website: 'url', - industry: 'IT', - monthly_spend: 2131231, - remote_created_at: 1683017572, - custom_attributes: { - employees: 450, - email: 'test@test.com', - key1: 'val1', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'sdfrsdfsdfsf', - }, - statusCode: 200, - metadata: { - jobId: 31, - }, - }, - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'sdfrsdfsdfsf', - email: 'testUser@test.com', - companies: [ - { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'sdfrsdfsdfsf', - }, - statusCode: 200, - metadata: { - jobId: 31, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Old version : successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - groupId: 'test_company_id_wdasda', - context: { - traits: { - email: 'testUser@test.com', - }, - }, - traits: { - employees: 450, - plan: 'basic', - email: 'test@test.com', - name: 'rudderUpdate', - size: '50', - industry: 'IT', - website: 'url', - monthlySpend: '2131231', - remoteCreatedAt: '1683017572', - key1: 'val1', - key2: { - a: 'a', - b: 'b', - }, - key3: [1, 2, 3], - key4: null, - }, - anonymousId: 'anonId', - integrations: { - All: true, - }, - type: 'group', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'abcd=', - appId: 'asdasdasd', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: true, - }, - }, - metadata: { - jobId: 32, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/companies', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - plan: 'basic', - size: 50, - website: 'url', - industry: 'IT', - monthly_spend: 2131231, - remote_created_at: 1683017572, - custom_attributes: { - employees: 450, - email: 'test@test.com', - key1: 'val1', - 'key2.a': 'a', - 'key2.b': 'b', - 'key3[0]': 1, - 'key3[1]': 2, - 'key3[2]': 3, - key4: null, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'anonId', - }, - statusCode: 200, - metadata: { - jobId: 32, - }, - }, - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer abcd=', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'anonId', - email: 'testUser@test.com', - companies: [ - { - company_id: 'test_company_id_wdasda', - name: 'rudderUpdate', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: 'anonId', - }, - statusCode: 200, - metadata: { - jobId: 32, - }, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Test 0', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - address: { - city: 'Kolkata', - state: 'West Bengal', - }, - originalArray: [ - { - nested_field: 'nested value', - tags: ['tag_1', 'tag_2', 'tag_3'], - }, - { - nested_field: 'nested value', - tags: ['tag_1'], - }, - { - nested_field: 'nested value', - }, - ], - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - Config: { - apiKey: 'intercomApiKey', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - user_id: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - 'address.city': 'Kolkata', - 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', - }, - update_last_request_at: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Test 1', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - Config: { - apiKey: 'intercomApiKey', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Test Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'intercom', - description: 'Test 2', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - destination: { - Config: { - apiKey: 'intercomApiKey', - appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', - collectContext: false, - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - }, - update_last_request_at: true, - name: 'Name', - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...groupTestData, + ...validationTestData, ]; diff --git a/test/integrations/destinations/intercom/processor/groupTestData.ts b/test/integrations/destinations/intercom/processor/groupTestData.ts new file mode 100644 index 0000000000..cb81df7bd2 --- /dev/null +++ b/test/integrations/destinations/intercom/processor/groupTestData.ts @@ -0,0 +1,509 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedGroupPayload, +} from '../../../testUtils'; + +const v1Config = { + apiKey: 'abcd=', + appId: 'asdasdasd', + apiVersion: 'v1', + collectContext: false, +}; + +const v2Config = { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, +}; + +const v1Headers = { + 'Content-Type': 'application/json', + Authorization: 'Bearer abcd=', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const v2Headers = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.10', +}; + +const destination: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: {}, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const v1Destination = { ...destination, Config: v1Config }; +const v2Destination = { ...destination, Config: v2Config }; + +const userTraits = { + ownerId: '17', + firstName: 'John', + lastName: 'Snow', + phone: '+91 9599999999', + email: 'test+5@rudderlabs.com', +}; + +const group1Traits = { + size: 500, + industry: 'CDP', + plan: 'enterprise', + name: 'RudderStack', + website: 'www.rudderstack.com', +}; + +const group2Traits = { + size: '50', + key1: 'val1', + plan: 'basic', + industry: 'IT', + employees: 450, + monthlySpend: '2131231', + userId: 'sdfrsdfsdfsf', + email: 'test@test.com', + name: 'rudderUpdate', + remoteCreatedAt: '1683017572', +}; + +const timestamp = '2023-11-22T10:12:44.757+05:30'; +const originalTimestamp = '2023-11-10T14:42:44.724Z'; + +const endpoint = 'https://api.intercom.io/companies'; + +export const groupTestData = [ + { + id: 'intercom-group-test-1', + name: 'intercom', + description: 'V2 version : Successful group call to create or update company', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create or update company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: generateSimplifiedGroupPayload({ + userId: 'user@4', + groupId: 'rudderlabs', + context: { + traits: { email: 'test+4@rudderlabs.com' }, + }, + traits: group1Traits, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint, + headers: v2Headers, + JSON: { company_id: 'rudderlabs', ...group1Traits }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-2', + name: 'intercom', + description: 'V2 version : Successful group call to add user to company', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain add user to company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v2Destination, Config: { ...v2Destination.Config, apiServer: 'eu' } }, + message: generateSimplifiedGroupPayload({ + userId: 'user@5', + groupId: 'rudderlabs', + context: { + traits: userTraits, + }, + traits: group1Traits, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: 'https://api.eu.intercom.io/contacts/70701240741e45d040/companies', + headers: v2Headers, + JSON: { id: '657264e9018c0a647s45' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-3', + name: 'intercom', + description: + 'V1 version : successful group call to create company and add user to company based on userId', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create company and add user to company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: generateSimplifiedGroupPayload({ + userId: 'sdfrsdfsdfsf', + anonymousId: 'sdfrsdfsdfsf', + groupId: 'test_company_id_wdasda', + context: { + traits: {}, + }, + traits: group2Traits, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: 'sdfrsdfsdfsf', + endpoint, + headers: v1Headers, + JSON: { + company_id: 'test_company_id_wdasda', + custom_attributes: { + email: 'test@test.com', + employees: 450, + key1: 'val1', + }, + industry: 'IT', + monthly_spend: 2131231, + name: 'rudderUpdate', + plan: 'basic', + remote_created_at: 1683017572, + size: 50, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + { + output: transformResultBuilder({ + userId: 'sdfrsdfsdfsf', + endpoint: 'https://api.intercom.io/users', + headers: v1Headers, + JSON: { + companies: [ + { + name: 'rudderUpdate', + company_id: 'test_company_id_wdasda', + }, + ], + user_id: 'sdfrsdfsdfsf', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-4', + name: 'intercom', + description: + 'V1 version : successful group call to create company and add user to company based on userId', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create company and add user to company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: generateSimplifiedGroupPayload({ + userId: 'sdfrsdfsdfsf', + anonymousId: 'sdfrsdfsdfsf', + groupId: 'test_company_id_wdasda', + context: { + traits: { + email: 'testUser@test.com', + }, + }, + traits: { ...group2Traits, website: 'url' }, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: 'sdfrsdfsdfsf', + endpoint, + headers: v1Headers, + JSON: { + company_id: 'test_company_id_wdasda', + custom_attributes: { + email: 'test@test.com', + employees: 450, + key1: 'val1', + }, + industry: 'IT', + monthly_spend: 2131231, + name: 'rudderUpdate', + plan: 'basic', + remote_created_at: 1683017572, + size: 50, + website: 'url', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + { + output: transformResultBuilder({ + userId: 'sdfrsdfsdfsf', + endpoint: 'https://api.intercom.io/users', + headers: v1Headers, + JSON: { + companies: [ + { + name: 'rudderUpdate', + company_id: 'test_company_id_wdasda', + }, + ], + email: 'testUser@test.com', + user_id: 'sdfrsdfsdfsf', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-5', + name: 'intercom', + description: + 'V1 version : successful group call without userId (anonId will be considered as userId)', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create company and add user to company payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v1Destination, Config: v1Destination.Config, sendAnonymousId: true }, + message: { + anonymousId: 'anonId', + groupId: 'test_company_id_wdasda', + context: { + traits: { + email: 'testUser@test.com', + }, + }, + traits: { + ...group2Traits, + website: 'url', + key1: 'val1', + key2: { + a: 'a', + b: 'b', + }, + key3: [1, 2, 3], + key4: null, + }, + type: 'group', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: 'anonId', + endpoint, + headers: v1Headers, + JSON: { + company_id: 'test_company_id_wdasda', + custom_attributes: { + email: 'test@test.com', + employees: 450, + key1: 'val1', + 'key2.a': 'a', + 'key2.b': 'b', + 'key3[0]': 1, + 'key3[1]': 2, + 'key3[2]': 3, + key4: null, + }, + industry: 'IT', + monthly_spend: 2131231, + name: 'rudderUpdate', + plan: 'basic', + remote_created_at: 1683017572, + size: 50, + website: 'url', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + { + output: transformResultBuilder({ + userId: 'anonId', + endpoint: 'https://api.intercom.io/users', + headers: v1Headers, + JSON: { + companies: [ + { + name: 'rudderUpdate', + company_id: 'test_company_id_wdasda', + }, + ], + email: 'testUser@test.com', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-group-test-6', + name: 'intercom', + description: 'V1 version : successful group call with email as custom attribute', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create company payload with email as custom attribute', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '12312312', + groupId: 'test_company_id', + context: { + traits: {}, + }, + traits: { ...group1Traits, monthlySpend: '2131231', email: 'comanyemail@abc.com' }, + type: 'group', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '12312312', + endpoint, + headers: v1Headers, + JSON: { + ...group1Traits, + custom_attributes: { + email: 'comanyemail@abc.com', + }, + monthly_spend: 2131231, + company_id: 'test_company_id', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/processor/identifyTestData.ts b/test/integrations/destinations/intercom/processor/identifyTestData.ts new file mode 100644 index 0000000000..d88b7cf7f5 --- /dev/null +++ b/test/integrations/destinations/intercom/processor/identifyTestData.ts @@ -0,0 +1,1025 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedIdentifyPayload, +} from '../../../testUtils'; + +const v1Config = { + apiKey: 'intercomApiKey', + apiVersion: 'v1', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, +}; + +const v2Config = { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, +}; + +const v2Headers = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.10', +}; + +const v1Headers = { + 'Content-Type': 'application/json', + Authorization: 'Bearer intercomApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const destination: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: {}, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const v1Destination = { ...destination, Config: v1Config }; +const v2Destination = { ...destination, Config: v2Config }; + +const user1Traits = { + age: 23, + ownerId: '13', + firstName: 'Test', + lastName: 'Rudderlabs', + phone: '+91 9999999999', + address: 'california usa', + email: 'test@rudderlabs.com', + lastSeenAt: '2023-11-10T14:42:44.724Z', +}; + +const user2Traits = { + age: 32, + ownerId: '14', + firstName: 'Test', + lastName: 'RudderStack', + phone: '+91 9299999999', + email: 'test+2@rudderlabs.com', +}; + +const user3Traits = { + owner_id: 13, + name: 'Test Rudderlabs', + phone: '+91 9999999999', + email: 'test@rudderlabs.com', +}; + +const user4Traits = { + key1: 'value1', + name: 'Test Name', + firstName: 'Test', + lastName: 'Name', + phone: '9876543210', + userId: 'test_user_id_1', + email: 'test_1@test.com', + createdAt: '2020-09-30T19:11:00.337Z', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + address: { + city: 'Kolkata', + state: 'West Bengal', + }, + originalArray: [ + { + nested_field: 'nested value', + tags: ['tag_1', 'tag_2', 'tag_3'], + }, + { + nested_field: 'nested value', + tags: ['tag_1'], + }, + { + nested_field: 'nested value', + }, + ], +}; + +const user5Traits = { + firstName: 'Test', + lastName: 'Name', + key1: 'value1', + phone: '9876543210', + email: 'test_1@test.com', + createdAt: '2020-09-30T19:11:00.337Z', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', +}; + +const user6Traits = { + lastName: 'Name', + key1: 'value1', + phone: '9876543210', + email: 'test_1@test.com', + createdAt: '2020-09-30T19:11:00.337Z', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + company: { + key1: 'value1', + name: 'Test Comp', + id: 'company_id', + industry: 'test industry', + key2: { + a: 'a', + }, + key3: [1, 2, 3], + }, +}; + +const expectedUser1Traits = { + owner_id: 13, + external_id: 'user@1', + last_seen_at: 1699627364, + name: 'Test Rudderlabs', + phone: '+91 9999999999', + email: 'test@rudderlabs.com', + custom_attributes: { + age: 23, + address: 'california usa', + }, +}; + +const expectedUser2Traits = { + owner_id: 14, + external_id: 'user@2', + name: 'Test RudderStack', + phone: '+91 9299999999', + email: 'test+2@rudderlabs.com', + custom_attributes: { + age: 32, + }, +}; + +const expectedUser3Traits = { + owner_id: 13, + user_id: 'user@1', + name: 'Test Rudderlabs', + phone: '+91 9999999999', + email: 'test@rudderlabs.com', +}; + +const expectedUser4Traits = { + name: 'Test Name', + phone: '9876543210', + email: 'test_1@test.com', + signed_up_at: 1601493060, + user_id: 'test_user_id_1', + last_seen_user_agent: 'unknown', + custom_attributes: { + key1: 'value1', + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + 'originalArray[0].nested_field': 'nested value', + 'originalArray[0].tags[0]': 'tag_1', + 'originalArray[0].tags[1]': 'tag_2', + 'originalArray[0].tags[2]': 'tag_3', + 'originalArray[1].nested_field': 'nested value', + 'originalArray[1].tags[0]': 'tag_1', + 'originalArray[2].nested_field': 'nested value', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + update_last_request_at: true, +}; + +const expectedUser5Traits = { + name: 'Test Name', + phone: '9876543210', + email: 'test_1@test.com', + signed_up_at: 1601493060, + update_last_request_at: true, + last_seen_user_agent: 'unknown', + custom_attributes: { + key1: 'value1', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, +}; + +const expectedUser6Traits = { + name: 'Name', + phone: '9876543210', + email: 'test_1@test.com', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + custom_attributes: { + key1: 'value1', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + }, + companies: [ + { + name: 'Test Comp', + industry: 'test industry', + company_id: 'company_id', + custom_attributes: { + key1: 'value1', + key2: '{"a":"a"}', + key3: '[1,2,3]', + }, + }, + ], +}; + +const timestamp = '2023-11-22T10:12:44.757+05:30'; +const originalTimestamp = '2023-11-10T14:42:44.724Z'; + +const v2Endpoint = 'https://api.intercom.io/contacts'; +const v1Endpoint = 'https://api.intercom.io/users'; + +export const identifyTestData = [ + { + id: 'intercom-identify-test-1', + name: 'intercom', + description: 'V2 version : Create customer with email as lookup field', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: generateSimplifiedIdentifyPayload({ + userId: 'user@1', + context: { + traits: user1Traits, + }, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: v2Endpoint, + headers: v2Headers, + JSON: expectedUser1Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-2', + name: 'intercom', + description: 'V2 version : Update customer with email as lookup field', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: generateSimplifiedIdentifyPayload({ + userId: 'user@2', + context: { + traits: user2Traits, + }, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: `${v2Endpoint}/7070129940741e45d040`, + headers: v2Headers, + method: 'PUT', + JSON: expectedUser2Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-3', + name: 'intercom', + description: 'V2 version : Identify rEtl test', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + context: { + externalId: [ + { + id: 'user@1', + type: 'INTERCOM-customer', + identifierType: 'user_id', + }, + ], + mappedToDestination: 'true', + }, + traits: user3Traits, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: v2Endpoint, + headers: v2Headers, + method: 'POST', + JSON: expectedUser3Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-4', + name: 'intercom', + description: 'V1 version : successful identify call to create user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user4Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser4Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-5', + name: 'intercom', + description: 'V1 version : successful identify call to create user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user5Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser5Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-6', + name: 'intercom', + description: 'V1 version : successful identify call to update user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { ...user5Traits, firstName: undefined }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { ...expectedUser5Traits, name: 'Name' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-7', + name: 'intercom', + description: 'V1 version : successful identify call to update user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { ...user5Traits, lastName: undefined }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { ...expectedUser5Traits, name: 'Test' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-8', + name: 'intercom', + description: 'V1 version : successful identify call to create user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user6Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser6Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-9', + name: 'intercom', + description: 'V1 version : successful identify call to create user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { + ...user6Traits, + company: { + name: 'Test Comp', + industry: 'test industry', + key1: 'value1', + key2: null, + key3: ['value1', 'value2'], + key4: { + foo: 'bar', + }, + }, + }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { + ...expectedUser6Traits, + companies: [ + { + ...expectedUser6Traits.companies[0], + custom_attributes: { + key1: 'value1', + key3: '["value1","value2"]', + key4: '{"foo":"bar"}', + }, + company_id: 'c0277b5c814453e5135f515f943d085a', + }, + ], + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-10', + name: 'intercom', + description: 'V1 version : successful identify call to update user', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { + ...user5Traits, + firstName: undefined, + company: { + industry: 'test industry', + key1: 'value1', + key2: null, + }, + }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { ...expectedUser5Traits, companies: [], name: 'Name' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-11', + name: 'intercom', + description: + 'No Version : Successful identify call to create user without giving apiVersion in configuration', + scenario: 'Business', + successCriteria: + 'Response should take v1 apiVersion by default and response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v1Destination, apiVersion: undefined }, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user4Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser4Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-12', + name: 'intercom', + description: + 'No Version : Successful identify call to create user without giving apiVersion in configuration', + scenario: 'Business', + successCriteria: + 'Response should take v1 apiVersion by default and response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v1Destination, apiVersion: undefined }, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user5Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser5Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-13', + name: 'intercom', + description: + 'No Version : Successful identify call to update user without giving apiVersion in configuration', + scenario: 'Business', + successCriteria: + 'Response should take v1 apiVersion by default and response status code should be 200 and response should contain update user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...v1Destination, apiVersion: undefined }, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: { ...user5Traits, firstName: undefined }, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { ...expectedUser5Traits, name: 'Name' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-14', + name: 'intercom', + description: + 'V1 version : Successful identify call to update user with sendAnonymousId configuration set to true', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain update user payload with all traits and userId should be equal to anonymousId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: user5Traits, + userAgent: 'unknown', + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: expectedUser5Traits, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-identify-test-15', + name: 'intercom', + description: 'V1 version : Identify rEtl test', + scenario: 'Business', + successCriteria: + 'Response status code should be 200 and response should contain create user payload with all traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + context: { + externalId: [ + { + id: '10156', + type: 'INTERCOM-customer', + identifierType: 'user_id', + }, + ], + mappedToDestination: 'true', + traits: user5Traits, + }, + type: 'identify', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint: v1Endpoint, + headers: v1Headers, + method: 'POST', + JSON: { + ...user5Traits, + user_id: '10156', + name: 'Test Name', + update_last_request_at: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/processor/trackTestData.ts b/test/integrations/destinations/intercom/processor/trackTestData.ts new file mode 100644 index 0000000000..15bed25d68 --- /dev/null +++ b/test/integrations/destinations/intercom/processor/trackTestData.ts @@ -0,0 +1,361 @@ +import { Destination } from '../../../../../src/types'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedTrackPayload, +} from '../../../testUtils'; + +const v1Config = { + apiKey: 'intercomApiKey', + apiVersion: 'v1', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, +}; + +const v2Config = { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, +}; + +const v1Headers = { + 'Content-Type': 'application/json', + Authorization: 'Bearer intercomApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const v2Headers = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.10', +}; + +const destination: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: {}, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const v1Destination = { ...destination, Config: v1Config }; +const v2Destination = { ...destination, Config: v2Config }; + +const userTraits = { + age: 23, + ownerId: '13', + firstName: 'Test', + lastName: 'Rudderlabs', + phone: '+91 9999999999', + address: 'california usa', + email: 'test@rudderlabs.com', + lastSeenAt: '2023-11-10T14:42:44.724Z', +}; + +const properties = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, +}; + +const nestedProperties = { + property1: 1, + property2: 'test', + property3: true, + property4: '2020-10-05T09:09:03.731Z', + property5: { + property1: 1, + property2: 'test', + property3: { + subProp1: { + a: 'a', + b: 'b', + }, + subProp2: ['a', 'b'], + }, + }, + properties6: null, + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + article: { + url: 'https://example.org/ab1de.html', + value: 'the dude abides', + }, +}; + +const expectedNestedProperties = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + article: { + url: 'https://example.org/ab1de.html', + value: 'the dude abides', + }, + property1: 1, + property2: 'test', + property3: true, + property4: '2020-10-05T09:09:03.731Z', + 'property5.property1': 1, + 'property5.property2': 'test', + 'property5.property3.subProp1.a': 'a', + 'property5.property3.subProp1.b': 'b', + 'property5.property3.subProp2[0]': 'a', + 'property5.property3.subProp2[1]': 'b', + properties6: null, +}; + +const expectedOutput = { + user_id: 'user@2', + created: 1699627364, + event_name: 'Test Event 2', + email: 'test@rudderlabs.com', +}; + +const timestamp = '2023-11-22T10:12:44.757+05:30'; +const originalTimestamp = '2023-11-10T14:42:44.724Z'; + +const endpoint = 'https://api.intercom.io/events'; + +export const trackTestData = [ + { + id: 'intercom-track-test-1', + name: 'intercom', + description: 'V2 version : Successful track call', + scenario: 'Business', + successCriteria: + "Response status code should be 200 and response should contain event name and it's properties", + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: generateSimplifiedTrackPayload({ + userId: 'user@2', + event: 'Product Viewed', + context: { + traits: userTraits, + }, + properties, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint, + headers: v2Headers, + JSON: { + user_id: 'user@2', + metadata: properties, + created_at: 1699627364, + event_name: 'Product Viewed', + email: 'test@rudderlabs.com', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-track-test-2', + name: 'intercom', + description: 'V2 version : Track rEtl test', + scenario: 'Business', + successCriteria: + "Response status code should be 200 and response should contain event name and it's properties", + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + userId: 'user@2', + event: 'Product Viewed', + context: { + mappedToDestination: 'true', + }, + traits: { + ...properties, + user_id: 'user@1', + event_name: 'Product Viewed', + }, + type: 'track', + timestamp, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + endpoint, + headers: v2Headers, + JSON: { + ...properties, + user_id: 'user@1', + event_name: 'Product Viewed', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-track-test-3', + name: 'intercom', + description: 'V1 version : successful track call with nested properties', + scenario: 'Business', + successCriteria: + "Response status code should be 200 and response should contain event name and it's properties", + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: generateSimplifiedTrackPayload({ + userId: 'user@2', + event: 'Test Event 2', + context: { + traits: userTraits, + }, + properties: nestedProperties, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: 'default-anonymousId', + endpoint, + headers: v1Headers, + JSON: { + ...expectedOutput, + metadata: { + ...expectedNestedProperties, + }, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-track-test-4', + name: 'intercom', + description: 'V1 version : successful track call without properties', + scenario: 'Business', + successCriteria: 'Response status code should be 200 and response should contain event name', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: generateSimplifiedTrackPayload({ + userId: 'user@2', + event: 'Test Event 2', + context: { + traits: userTraits, + }, + timestamp, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + endpoint, + headers: v1Headers, + JSON: expectedOutput, + userId: 'default-anonymousId', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/processor/validationTestData.ts b/test/integrations/destinations/intercom/processor/validationTestData.ts new file mode 100644 index 0000000000..45fe3c1b9e --- /dev/null +++ b/test/integrations/destinations/intercom/processor/validationTestData.ts @@ -0,0 +1,554 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; + +const v1Config = { + apiKey: 'intercomApiKey', + apiVersion: 'v1', + appId: '9e9cdea1-78fa-4829-a9b2-5d7f7e96d1a0', + collectContext: false, +}; + +const v2Config = { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, +}; + +const destination: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'standard', + sendAnonymousId: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const v1Destination = { ...destination, Config: v1Config }; +const v2Destination = { ...destination, Config: v2Config }; + +const userTraits = { + age: 23, + ownerId: '14', + role: 'user', + source: 'rudder-sdk', + firstname: 'Test', + lastName: 'RudderStack', + phone: '+91 9299999999', + birthday: '2022-05-13T12:51:01.470Z', +}; + +const properties = { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, +}; + +const groupTraits = { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', +}; + +const expectedStatTags = { + destType: 'INTERCOM', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'intercom-validation-test-1', + name: 'intercom', + description: '[Error - V2 version]: Check for no message type', + scenario: 'Framework', + successCriteria: 'Response status code should be 400 with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + event: 'Product Searched', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message Type is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message Type is not present. Aborting', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-2', + name: 'intercom', + description: '[Error - V2 version]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by intercom destination and the error message should be Event type alias is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + userId: 'user@45', + type: 'page', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type page is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type page is not supported', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-3', + name: 'intercom', + description: '[Error - V2 version]: Missing required config', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw configuration error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...v2Destination, + Config: { ...v2Destination.Config, apiKey: null }, + }, + message: { + userId: 'user@1', + type: 'identify', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Access Token is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Access Token is not present. Aborting', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-4', + name: 'intercom', + description: '[Error - V2 version]: Missing required parameters for an identify call', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + anonymousId: 'anon@2', + type: 'identify', + context: { + traits: userTraits, + }, + integrations: { + INTERCOM: { + lookup: 'phone', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either email or userId is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForLatestVersion, ChildStep: undefined, OriginalError: Either email or userId is required for Identify call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-5', + name: 'intercom', + description: '[Error - V2 version]: Missing required parameters for an identify call', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + anonymousId: 'anon@2', + type: 'identify', + context: { + traits: userTraits, + }, + integrations: { + INTERCOM: { + lookup: 'phone', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either email or userId is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForLatestVersion, ChildStep: undefined, OriginalError: Either email or userId is required for Identify call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-6', + name: 'intercom', + description: + '[Error - V2 version]: Unauthorized error while searching contact for an identify call', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw network error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...v2Destination, + Config: { ...v2Destination.Config, apiKey: 'invalidApiKey' }, + }, + message: { + userId: 'user@3', + type: 'identify', + context: { + traits: { + phone: '+91 9399999999', + email: 'test+3@rudderlabs.com', + firstName: 'Test', + lastName: 'Rudder', + ownerId: '15', + role: 'admin', + source: 'rudder-android-sdk', + }, + }, + integrations: { + INTERCOM: { + lookup: 'email', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"{\\"message\\":\\"Unable to search contact due to : [{\\\\\\"code\\\\\\":\\\\\\"unauthorized\\\\\\",\\\\\\"message\\\\\\":\\\\\\"Access Token Invalid\\\\\\"}]: Workflow: procWorkflow, Step: searchContact, ChildStep: undefined, OriginalError: Unable to search contact due to : [{\\\\\\"code\\\\\\":\\\\\\"unauthorized\\\\\\",\\\\\\"message\\\\\\":\\\\\\"Access Token Invalid\\\\\\"}]\\",\\"destinationResponse\\":{\\"response\\":{\\"type\\":\\"error.list\\",\\"request_id\\":\\"request_1\\",\\"errors\\":[{\\"code\\":\\"unauthorized\\",\\"message\\":\\"Access Token Invalid\\"}]},\\"status\\":401}}","destinationResponse":{"response":{"type":"error.list","request_id":"request_1","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]},"status":401}}', + statTags: { ...expectedStatTags, errorCategory: 'network', errorType: 'aborted' }, + statusCode: 401, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-7', + name: 'intercom', + description: '[Error - V2 version]: Track call without event name', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + userId: 'user@3', + type: 'track', + context: { + traits: userTraits, + }, + properties, + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Event name is required for track call: Workflow: procWorkflow, Step: trackPayload, ChildStep: undefined, OriginalError: Event name is required for track call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-8', + name: 'intercom', + description: '[Error - V2 version]: Group call without groupId', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v2Destination, + message: { + userId: 'user@4', + type: 'group', + context: { + traits: { + email: 'test+4@rudderlabs.com', + phone: '+91 9499999999', + firstName: 'John', + lastName: 'Doe', + ownerId: '16', + }, + }, + traits: groupTraits, + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'groupId is required for group call: Workflow: procWorkflow, Step: groupPayloadForLatestVersion, ChildStep: validateMessageAndPreparePayload, OriginalError: groupId is required for group call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-9', + name: 'intercom', + description: '[Error - V1 version]: Identify call without email and userId', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + type: 'identify', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: userTraits, + }, + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either of `email` or `userId` is required for Identify call: Workflow: procWorkflow, Step: identifyPayloadForOlderVersion, ChildStep: undefined, OriginalError: Either of `email` or `userId` is required for Identify call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'intercom-validation-test-10', + name: 'intercom', + description: '[Error - V1 version]: Track call without email or userId', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400 and it should throw instrumentation error with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: v1Destination, + message: { + type: 'track', + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + context: { + traits: userTraits, + }, + event: 'Test Event 2', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either email or userId is required for Track call: Workflow: procWorkflow, Step: trackPayload, ChildStep: undefined, OriginalError: Either email or userId is required for Track call', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/router/data.ts b/test/integrations/destinations/intercom/router/data.ts index 2ce8621ca1..f0e6b46f4a 100644 --- a/test/integrations/destinations/intercom/router/data.ts +++ b/test/integrations/destinations/intercom/router/data.ts @@ -1,187 +1,655 @@ -export const data = [ +import { Destination, RouterTransformationRequest } from '../../../../../src/types'; +import { RouterTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; + +const destination1: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiServer: 'standard', + apiVersion: 'v2', + sendAnonymousId: false, + updateLastRequestAt: true, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination2: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiServer: 'standard', + apiVersion: 'v2', + sendAnonymousId: false, + updateLastRequestAt: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination3: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v2', + apiServer: 'eu', + sendAnonymousId: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination4: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + updateLastRequestAt: false, + collectContext: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination5: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + collectContext: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination6: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + updateLastRequestAt: false, + collectContext: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const destination7: Destination = { + ID: '123', + Name: 'intercom', + DestinationDefinition: { + ID: '123', + Name: 'intercom', + DisplayName: 'Intercom', + Config: {}, + }, + Config: { + apiKey: 'testApiKey', + apiVersion: 'v1', + sendAnonymousId: false, + collectContext: false, + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const routerRequest1: RouterTransformationRequest = { + input: [ + { + destination: destination1, + message: { + userId: 'user@1', + channel: 'web', + context: { + traits: { + age: 23, + email: 'test@rudderlabs.com', + phone: '+91 9999999999', + firstName: 'Test', + lastName: 'Rudderlabs', + address: 'california usa', + ownerId: '13', + }, + }, + type: 'identify', + integrations: { All: true }, + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(1), + }, + { + destination: destination2, + message: { + userId: 'user@3', + channel: 'web', + context: { + traits: { + age: 32, + email: 'test+3@rudderlabs.com', + phone: '+91 9399999999', + firstName: 'Test', + lastName: 'RudderStack', + ownerId: '15', + }, + }, + properties: { + revenue: { + amount: 1232, + currency: 'inr', + test: 123, + }, + price: { + amount: 3000, + currency: 'USD', + }, + }, + event: 'Product Viewed', + type: 'track', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(2), + }, + { + destination: destination3, + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(3), + }, + { + destination: destination3, + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + tags: ['tag1', 'tag2'], + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(5), + }, + { + destination: destination3, + message: { + userId: 'user@6', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + isOpenSource: true, + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(4), + }, + ], + destType: 'intercom', +}; + +const routerRequest2: RouterTransformationRequest = { + input: [ + { + destination: destination4, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + name: 'Test Name', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + userId: 'test_user_id_1', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: generateMetadata(1), + }, + { + destination: destination4, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: generateMetadata(2), + }, + { + destination: destination5, + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'intercom', +}; + +const routerRequest3: RouterTransformationRequest = { + input: [ + { + destination: destination6, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + name: 'Test Name', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + userId: 'test_user_id_1', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: generateMetadata(1), + }, + { + destination: destination6, + message: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + channel: 'mobile', + context: { + app: { + build: '1.0', + name: 'Test_Example', + namespace: 'com.example.testapp', + version: '1.0', + }, + device: { + id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + manufacturer: 'Apple', + model: 'iPhone', + name: 'iPod touch (7th generation)', + type: 'iOS', + }, + library: { + name: 'test-ios-library', + version: '1.0.7', + }, + locale: 'en-US', + network: { + bluetooth: false, + carrier: 'unavailable', + cellular: false, + wifi: true, + }, + os: { + name: 'iOS', + version: '14.0', + }, + screen: { + density: 2, + height: 320, + width: 568, + }, + timezone: 'Asia/Kolkata', + traits: { + anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', + firstName: 'Test', + lastName: 'Name', + createdAt: '2020-09-30T19:11:00.337Z', + email: 'test_1@test.com', + phone: '9876543210', + key1: 'value1', + }, + userAgent: 'unknown', + }, + event: 'Test Event 2', + integrations: { + All: true, + }, + messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', + originalTimestamp: '2020-09-30T19:11:00.337Z', + receivedAt: '2020-10-01T00:41:11.369+05:30', + request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', + sentAt: '2020-09-30T19:11:10.382Z', + timestamp: '2020-10-01T00:41:01.324+05:30', + type: 'identify', + }, + metadata: generateMetadata(2), + }, + { + destination: destination7, + message: { + userId: 'user@5', + groupId: 'rudderlabs', + channel: 'web', + context: { + traits: { + email: 'test+5@rudderlabs.com', + phone: '+91 9599999999', + firstName: 'John', + lastName: 'Snow', + ownerId: '17', + }, + }, + traits: { + name: 'RudderStack', + size: 500, + website: 'www.rudderstack.com', + industry: 'CDP', + plan: 'enterprise', + }, + type: 'group', + originalTimestamp: '2023-11-10T14:42:44.724Z', + timestamp: '2023-11-22T10:12:44.757+05:30', + }, + metadata: generateMetadata(3), + }, + ], + destType: 'intercom', +}; + +export const data: RouterTestData[] = [ { + id: 'Intercom-router-test-1', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for apiVersion v2', name: 'intercom', - description: 'Intercom router tests', + description: 'Intercom router tests for apiVersion v2', feature: 'router', module: 'destination', version: 'v0', input: { request: { - body: { - input: [ - { - message: { - userId: 'user@1', - channel: 'web', - context: { - traits: { - age: 23, - email: 'test@rudderlabs.com', - phone: '+91 9999999999', - firstName: 'Test', - lastName: 'Rudderlabs', - address: 'california usa', - ownerId: '13', - }, - }, - type: 'identify', - integrations: { All: true }, - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiServer: 'standard', - apiVersion: 'v2', - sendAnonymousId: false, - updateLastRequestAt: true, - }, - }, - metadata: { jobId: 1 }, - }, - { - message: { - userId: 'user@3', - channel: 'web', - context: { - traits: { - age: 32, - email: 'test+3@rudderlabs.com', - phone: '+91 9399999999', - firstName: 'Test', - lastName: 'RudderStack', - ownerId: '15', - }, - }, - properties: { - revenue: { - amount: 1232, - currency: 'inr', - test: 123, - }, - price: { - amount: 3000, - currency: 'USD', - }, - }, - event: 'Product Viewed', - type: 'track', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiServer: 'standard', - apiVersion: 'v2', - sendAnonymousId: false, - updateLastRequestAt: false, - }, - }, - metadata: { - jobId: 2, - }, - }, - { - message: { - userId: 'user@5', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'eu', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 3, - }, - }, - { - message: { - userId: 'user@6', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - isOpenSource: true, - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v2', - apiServer: 'eu', - sendAnonymousId: false, - }, - }, - metadata: { - jobId: 4, - }, - }, - ], - destType: 'intercom', - }, - method: 'POST', + body: routerRequest1, }, }, output: { @@ -221,21 +689,8 @@ export const data = [ type: 'REST', version: '1', }, - destination: { - Config: { - apiKey: 'testApiKey', - apiServer: 'standard', - apiVersion: 'v2', - sendAnonymousId: false, - updateLastRequestAt: true, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, - metadata: [{ jobId: 1 }], + destination: destination1, + metadata: [generateMetadata(1)], statusCode: 200, }, { @@ -276,21 +731,8 @@ export const data = [ type: 'REST', version: '1', }, - destination: { - Config: { - apiKey: 'testApiKey', - apiServer: 'standard', - apiVersion: 'v2', - sendAnonymousId: false, - updateLastRequestAt: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, - metadata: [{ jobId: 2 }], + destination: destination2, + metadata: [generateMetadata(2)], statusCode: 200, }, { @@ -317,25 +759,37 @@ export const data = [ type: 'REST', version: '1', }, - destination: { - Config: { - apiKey: 'testApiKey', - apiServer: 'eu', - apiVersion: 'v2', - sendAnonymousId: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, + destination: destination3, + metadata: [generateMetadata(3)], + statusCode: 299, + }, + { + batched: false, + batchedRequest: { + body: { + JSON: { + id: '657264e9018c0a647s45', }, + XML: {}, + FORM: {}, + JSON_ARRAY: {}, }, - }, - metadata: [ - { - jobId: 3, + endpoint: 'https://api.eu.intercom.io/contacts/70701240741e45d040/companies', + files: {}, + headers: { + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', + Accept: 'application/json', + 'Intercom-Version': '2.10', }, - ], - statusCode: 200, + method: 'POST', + params: {}, + type: 'REST', + version: '1', + }, + destination: destination3, + metadata: [generateMetadata(5)], + statusCode: 299, }, { batched: false, @@ -348,25 +802,11 @@ export const data = [ feature: 'router', implementation: 'cdkV2', module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', }, - destination: { - Config: { - apiKey: 'testApiKey', - apiServer: 'eu', - apiVersion: 'v2', - sendAnonymousId: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, - metadata: [ - { - jobId: 4, - }, - ], + destination: destination3, + metadata: [generateMetadata(4)], statusCode: 401, }, ], @@ -375,222 +815,18 @@ export const data = [ }, }, { + id: 'Intercom-router-test-2', + scenario: 'Framework', + successCriteria: + 'Events should be transformed successfully for apiVersion v1 and cdk v2 enabled', name: 'intercom', - description: 'Test 0', + description: 'Intercom router tests for apiVersion v1 and cdk v2 enabled', feature: 'router', module: 'destination', version: 'v0', input: { request: { - body: { - input: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - metadata: { - jobId: 1, - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - updateLastRequestAt: false, - collectContext: false, - }, - }, - }, - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - metadata: { - jobId: 2, - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - updateLastRequestAt: false, - collectContext: false, - }, - }, - }, - { - message: { - userId: 'user@5', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - collectContext: false, - }, - }, - metadata: { - jobId: 3, - }, - }, - ], - destType: 'intercom', - }, + body: routerRequest2, }, }, output: { @@ -632,27 +868,10 @@ export const data = [ files: {}, userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', }, - metadata: [ - { - jobId: 1, - }, - ], + metadata: [generateMetadata(1)], batched: false, statusCode: 200, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - updateLastRequestAt: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, + destination: destination4, }, { batchedRequest: { @@ -687,27 +906,10 @@ export const data = [ files: {}, userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', }, - metadata: [ - { - jobId: 2, - }, - ], + metadata: [generateMetadata(2)], batched: false, statusCode: 200, - destination: { - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - updateLastRequestAt: false, - }, - }, + destination: destination4, }, { batched: false, @@ -769,25 +971,9 @@ export const data = [ version: '1', }, ], - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - }, - DestinationDefinition: { - Config: { - cdkV2Enabled: true, - }, - }, - }, - metadata: [ - { - jobId: 3, - }, - ], - statusCode: 200, + destination: destination5, + metadata: [generateMetadata(3)], + statusCode: 299, }, ], }, @@ -795,207 +981,18 @@ export const data = [ }, }, { + id: 'Intercom-router-test-3', + scenario: 'Framework', + successCriteria: + 'Events should be transformed successfully for apiVersion v1 and cdk v2 not enabled', name: 'intercom', - description: 'Test 0', + description: 'Intercom router tests for apiVersion v1 and cdk v2 not enabled', feature: 'router', module: 'destination', version: 'v0', input: { request: { - body: { - input: [ - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - name: 'Test Name', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - userId: 'test_user_id_1', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - metadata: { - jobId: 1, - }, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - updateLastRequestAt: false, - collectContext: false, - }, - }, - }, - { - message: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - channel: 'mobile', - context: { - app: { - build: '1.0', - name: 'Test_Example', - namespace: 'com.example.testapp', - version: '1.0', - }, - device: { - id: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - manufacturer: 'Apple', - model: 'iPhone', - name: 'iPod touch (7th generation)', - type: 'iOS', - }, - library: { - name: 'test-ios-library', - version: '1.0.7', - }, - locale: 'en-US', - network: { - bluetooth: false, - carrier: 'unavailable', - cellular: false, - wifi: true, - }, - os: { - name: 'iOS', - version: '14.0', - }, - screen: { - density: 2, - height: 320, - width: 568, - }, - timezone: 'Asia/Kolkata', - traits: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - firstName: 'Test', - lastName: 'Name', - createdAt: '2020-09-30T19:11:00.337Z', - email: 'test_1@test.com', - phone: '9876543210', - key1: 'value1', - }, - userAgent: 'unknown', - }, - event: 'Test Event 2', - integrations: { - All: true, - }, - messageId: '1601493060-39010c49-e6e4-4626-a75c-0dbf1925c9e8', - originalTimestamp: '2020-09-30T19:11:00.337Z', - receivedAt: '2020-10-01T00:41:11.369+05:30', - request_ip: '2405:201:8005:9856:7911:25e7:5603:5e18', - sentAt: '2020-09-30T19:11:10.382Z', - timestamp: '2020-10-01T00:41:01.324+05:30', - type: 'identify', - }, - metadata: { - jobId: 2, - }, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - updateLastRequestAt: false, - collectContext: false, - }, - }, - }, - { - message: { - userId: 'user@5', - groupId: 'rudderlabs', - channel: 'web', - context: { - traits: { - email: 'test+5@rudderlabs.com', - phone: '+91 9599999999', - firstName: 'John', - lastName: 'Snow', - ownerId: '17', - }, - }, - traits: { - name: 'RudderStack', - size: 500, - website: 'www.rudderstack.com', - industry: 'CDP', - plan: 'enterprise', - }, - type: 'group', - originalTimestamp: '2023-11-10T14:42:44.724Z', - timestamp: '2023-11-22T10:12:44.757+05:30', - }, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - sendAnonymousId: false, - collectContext: false, - }, - }, - metadata: { - jobId: 3, - }, - }, - ], - destType: 'intercom', - }, + body: routerRequest3, }, }, output: { @@ -1037,22 +1034,10 @@ export const data = [ files: {}, userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', }, - metadata: [ - { - jobId: 1, - }, - ], + metadata: [generateMetadata(1)], batched: false, statusCode: 200, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - updateLastRequestAt: false, - }, - }, + destination: destination6, }, { batchedRequest: { @@ -1087,22 +1072,10 @@ export const data = [ files: {}, userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', }, - metadata: [ - { - jobId: 2, - }, - ], + metadata: [generateMetadata(2)], batched: false, statusCode: 200, - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - updateLastRequestAt: false, - }, - }, + destination: destination6, }, { batched: false, @@ -1164,19 +1137,8 @@ export const data = [ version: '1', }, ], - destination: { - Config: { - apiKey: 'testApiKey', - apiVersion: 'v1', - collectContext: false, - sendAnonymousId: false, - }, - }, - metadata: [ - { - jobId: 3, - }, - ], + destination: destination7, + metadata: [generateMetadata(3)], statusCode: 200, }, ], diff --git a/test/integrations/destinations/iterable/processor/aliasTestData.ts b/test/integrations/destinations/iterable/processor/aliasTestData.ts new file mode 100644 index 0000000000..cac43767bb --- /dev/null +++ b/test/integrations/destinations/iterable/processor/aliasTestData.ts @@ -0,0 +1,97 @@ +import { generateMetadata, transformResultBuilder } from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, +}; + +const headers = { + api_key: 'testApiKey', + 'Content-Type': 'application/json', +}; + +const properties = { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', +}; + +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +export const aliasTestData: ProcessorTestData[] = [ + { + id: 'iterable-alias-test-1', + name: 'iterable', + description: 'Alias call with userId and previousId', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update email payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId: 'anonId', + userId: 'new@email.com', + previousId: 'old@email.com', + name: 'ApplicationLoaded', + context: {}, + properties, + type: 'alias', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: 'https://api.iterable.com/api/users/updateEmail', + JSON: { + currentEmail: 'old@email.com', + newEmail: 'new@email.com', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/processor/data.ts b/test/integrations/destinations/iterable/processor/data.ts index 19b370b513..12e5738641 100644 --- a/test/integrations/destinations/iterable/processor/data.ts +++ b/test/integrations/destinations/iterable/processor/data.ts @@ -1,3784 +1,13 @@ +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { pageScreenTestData } from './pageScreenTestData'; +import { aliasTestData } from './aliasTestData'; +import { validationTestData } from './validationTestData'; + export const data = [ - { - name: 'iterable', - description: 'Test 0', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'page', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Invalid page call', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 1', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'identify', - sentAt: '2020-08-28T16:26:06.466Z', - traits: { - city: 'Bangalore', - name: 'manashi', - email: 'manashi@website.com', - country: 'India', - }, - context: { - traits: { - city: 'Bangalore', - name: 'manashi', - email: 'manashi@website.com', - country: 'India', - preferUserId: false, - }, - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-cc3ef811f686139ee527b806ee0129ef-163a3a88-266f-447e-8cce-34a8f42f8dcd', - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.462Z', - }, - destination: { - Config: { - preferUserId: false, - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - email: 'manashi@website.com', - userId: 'abcdeeeeeeeexxxx102', - dataFields: { - city: 'Bangalore', - name: 'manashi', - email: 'manashi@website.com', - country: 'India', - }, - preferUserId: false, - mergeNestedObjects: true, - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/users/update', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 2', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: 'Email Opened', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-570110489d3e99b234b18af9a9eca9d4-6009779e-82d7-469d-aaeb-5ccf162b0453', - properties: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - userId: 'abcdeeeeeeeexxxx102', - createdAt: 1598631966468, - eventName: 'Email Opened', - dataFields: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/events/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 3', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - }, - userId: '12345', - eventName: 'ApplicationLoaded page', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 4', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - campaignId: '123456', - templateId: '1213458', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: true, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - campaignId: '123456', - templateId: '1213458', - }, - userId: '12345', - eventName: 'Loaded a Page', - createdAt: 1571051718299, - campaignId: 123456, - templateId: 1213458, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 5', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - name: 'test-name', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: false, - trackNamedPages: true, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - name: 'test-name', - }, - userId: '12345', - eventName: 'ApplicationLoaded page', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 6', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - }, - userId: '12345', - eventName: 'ApplicationLoaded page', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 7', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'screen', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - }, - userId: '12345', - eventName: 'ApplicationLoaded screen', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 8', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'screen', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - campaignId: '123456', - templateId: '1213458', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: true, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - category: 'test-category', - campaignId: '123456', - templateId: '1213458', - }, - userId: '12345', - eventName: 'Loaded a Screen', - createdAt: 1571051718299, - campaignId: 123456, - templateId: 1213458, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 9', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'screen', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - name: 'test-name', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: false, - trackNamedPages: true, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - name: 'test-name', - }, - userId: '12345', - eventName: 'ApplicationLoaded screen', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 10', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'screen', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'sayan@gmail.com', - dataFields: { - path: '/abc', - referrer: '', - search: '', - title: '', - url: '', - }, - userId: '12345', - eventName: 'ApplicationLoaded screen', - createdAt: 1571051718299, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 11', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'group', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Message type group not supported', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 12', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - mergeNestedObjects: false, - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: false, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 13', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - token: 'sample_push_token', - name: 'sample_device_name', - model: 'sample_device_model', - manufacturer: 'sample_device_manufacturer', - type: 'ios', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: true, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 14', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - token: 'sample_push_token', - name: 'sample_device_name', - model: 'sample_device_model', - manufacturer: 'sample_device_manufacturer', - type: 'android', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: true, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 15', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'product added', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - campaignId: '1', - templateId: '0', - orderId: 10000, - total: 1000, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: '2', - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['cars'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 16', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'order completed', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'Cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: 2, - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: '22', - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/trackPurchase', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - dataFields: { - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'Cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: 2, - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: '22', - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - id: '10000', - createdAt: 1571051718299, - campaignId: 123456, - templateId: 1213458, - total: 1000, - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['Cars'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 17', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - type: 'track', - messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', - originalTimestamp: '2019-10-14T11:15:18.300Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - event: 'test track event GA3', - properties: { - email: 'ruchira@rudderlabs.com', - campaignId: '1', - templateId: '0', - category: 'test-category', - user_actual_role: 'system_admin, system_user', - user_actual_id: 12345, - }, - integrations: { - All: true, - }, - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/events/track', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - campaignId: '1', - templateId: '0', - category: 'test-category', - user_actual_role: 'system_admin, system_user', - user_actual_id: 12345, - email: 'ruchira@rudderlabs.com', - }, - userId: '12345', - eventName: 'test track event GA3', - createdAt: 1571051718300, - campaignId: 1, - templateId: 0, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 18', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: true, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 19', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - id: '72e528f869711c3d', - manufacturer: 'Google', - model: 'sdk_gphone_x86', - name: 'generic_x86_arm', - token: 'some_device_token', - type: 'android', - }, - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'userId or email is mandatory for this request', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 20', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'page', - sentAt: '2020-08-28T16:26:16.473Z', - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Invalid page call', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'configuration', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 21', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: 'Product Added', - sentAt: '2021-07-09T05:27:17.908Z', - userId: '8751', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.0.16', - namespace: 'com.rudderlabs.javascript', - }, - page: { - url: 'https://joybird.com/cabinets/vira-console-cabinet/', - path: '/cabinets/vira-console-cabinet/', - title: 'Vira Console Cabinet | Joybird', - search: '', - referrer: '$direct', - referring_domain: '', - }, - locale: 'en-us', - screen: { - density: 2, - }, - traits: { - email: 'jessica@jlpdesign.net', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.16', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15', - }, - rudderId: '1c42e104-97ec-4f54-a328-2379623583fe', - messageId: 'e58f6624-a1c3-48f4-a6af-610389602304', - timestamp: '2021-07-09T05:27:18.131Z', - properties: { - sku: 'JB24691400-W05', - name: 'Vira Console Cabinet', - price: 797, - cart_id: 'bd9b8dbf4ef8ee01d4206b04fe2ee6ae', - variant: 'Oak', - quantity: 1, - quickship: true, - full_price: 1328, - product_id: 10606, - non_interaction: 1, - }, - receivedAt: '2021-07-09T05:27:18.131Z', - request_ip: '162.224.233.114', - anonymousId: '8a7ff986-62d8-45ca-9a16-8895b3f9d341', - integrations: { - All: true, - }, - originalTimestamp: '2021-07-09T05:27:17.908Z', - }, - destination: { - Config: { - credentials: 'abc', - eventToTopicMap: [ - { - from: 'track', - to: 'projects/big-query-integration-poc/topics/test', - }, - ], - }, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - }, - params: {}, - body: { - JSON: { - user: { - email: 'jessica@jlpdesign.net', - dataFields: { - email: 'jessica@jlpdesign.net', - }, - userId: '8751', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: 10606, - sku: 'JB24691400-W05', - name: 'Vira Console Cabinet', - price: 797, - quantity: 1, - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 22', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'sources', - context: { - externalId: [ - { - id: 'lynnanderson@smith.net', - identifierType: 'email', - type: 'ITERABLE-users', - }, - ], - mappedToDestination: 'true', - sources: { - batch_id: 'f5f240d0-0acb-46e0-b043-57fb0aabbadd', - job_id: '1zAj94bEy8komdqnYtSoDp0VmGs/Syncher', - job_run_id: 'c5tar6cqgmgmcjvupdhg', - task_id: 'tt_10_rows_check', - task_run_id: 'c5tar6cqgmgmcjvupdi0', - version: 'release.v1.6.8', - }, - }, - messageId: '2f052f7c-f694-4849-a7ed-a432f7ffa0a4', - originalTimestamp: '2021-10-28T14:03:50.503Z', - receivedAt: '2021-10-28T14:03:46.567Z', - recordId: '8', - request_ip: '10.1.94.92', - rudderId: 'c0f6843e-e3d6-4946-9752-fa339fbadef2', - sentAt: '2021-10-28T14:03:50.503Z', - timestamp: '2021-10-28T14:03:46.566Z', - traits: { - administrative_unit: 'Minnesota', - am_pm: 'AM', - boolean: true, - firstname: 'Jacqueline', - pPower: 'AM', - userId: 'Jacqueline', - }, - type: 'identify', - userId: 'lynnanderson@smith.net', - }, - destination: { - ID: '1zia9wKshXt80YksLmUdJnr7IHI', - Name: 'test_iterable', - DestinationDefinition: { - ID: '1iVQvTRMsPPyJzwol0ifH93QTQ6', - Name: 'ITERABLE', - DisplayName: 'Iterable', - Config: { - destConfig: { - defaultConfig: [ - 'apiKey', - 'mapToSingleEvent', - 'trackAllPages', - 'trackCategorisedPages', - 'trackNamedPages', - ], - }, - excludeKeys: [], - includeKeys: [], - saveDestinationResponse: true, - secretKeys: [], - supportedMessageTypes: ['identify', 'page', 'screen', 'track'], - supportedSourceTypes: [ - 'android', - 'ios', - 'web', - 'unity', - 'amp', - 'cloud', - 'warehouse', - 'reactnative', - 'flutter', - 'cordova', - ], - supportsVisualMapper: true, - transformAt: 'processor', - transformAtV1: 'processor', - }, - ResponseRules: null, - }, - Config: { - apiKey: '12345', - mapToSingleEvent: true, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: true, - }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - libraries: [], - request: { - query: {}, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'lynnanderson@smith.net', - dataFields: { - administrative_unit: 'Minnesota', - am_pm: 'AM', - boolean: true, - firstname: 'Jacqueline', - pPower: 'AM', - userId: 'Jacqueline', - email: 'lynnanderson@smith.net', - }, - userId: 'lynnanderson@smith.net', - preferUserId: true, - mergeNestedObjects: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 23', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'sources', - context: { - externalId: [ - { - id: 'Matthew', - identifierType: 'userId', - type: 'ITERABLE-users', - }, - ], - mappedToDestination: 'true', - sources: { - batch_id: '230d7c79-a2c2-4b2a-90bb-06ba988d3bb4', - job_id: '1zjj9aF5UkmavBi4HtM3kWOGvy0/Syncher', - job_run_id: 'c5tb4gsqgmgmcjvuplhg', - task_id: 'tt_10_rows', - task_run_id: 'c5tb4gsqgmgmcjvupli0', - version: 'release.v1.6.8', - }, - }, - messageId: 'c4c97310-463b-4300-9215-5cfddcb2a769', - originalTimestamp: '2021-10-28T14:23:43.254Z', - receivedAt: '2021-10-28T14:23:38.300Z', - recordId: '3', - request_ip: '10.1.94.92', - rudderId: '7300f5e3-bdb5-489e-ac7e-47876e487de9', - sentAt: '2021-10-28T14:23:43.254Z', - timestamp: '2021-10-28T14:23:38.299Z', - traits: { - price: 'GB', - }, - type: 'identify', - userId: 'Matthew', - }, - destination: { - ID: '1zjjHN4RQ6t4DPj3HVpp0b6XW4A', - Name: 'test_userId_uniq', - DestinationDefinition: { - ID: '1iVQvTRMsPPyJzwol0ifH93QTQ6', - Name: 'ITERABLE', - DisplayName: 'Iterable', - Config: { - destConfig: { - defaultConfig: [ - 'apiKey', - 'mapToSingleEvent', - 'trackAllPages', - 'trackCategorisedPages', - 'trackNamedPages', - ], - }, - excludeKeys: [], - includeKeys: [], - saveDestinationResponse: true, - secretKeys: [], - supportedMessageTypes: ['identify', 'page', 'screen', 'track'], - supportedSourceTypes: [ - 'android', - 'ios', - 'web', - 'unity', - 'amp', - 'cloud', - 'warehouse', - 'reactnative', - 'flutter', - 'cordova', - ], - supportsVisualMapper: true, - transformAt: 'processor', - transformAtV1: 'processor', - }, - ResponseRules: null, - }, - Config: { - apiKey: '12345', - mapToSingleEvent: true, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: true, - }, - Enabled: true, - Transformations: [], - IsProcessorEnabled: true, - }, - libraries: [], - request: { - query: {}, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - dataFields: { - price: 'GB', - userId: 'Matthew', - }, - userId: 'Matthew', - preferUserId: true, - mergeNestedObjects: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 24', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - device: { - token: 'sample_push_token', - name: 'sample_device_name', - model: 'sample_device_model', - manufacturer: 'sample_device_manufacturer', - type: 'watchos', - }, - traits: { - email: 'ruchira@rudderlabs.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - screen: { - density: 2, - }, - }, - type: 'identify', - messageId: '84e26acc-56a5-4835-8233-591137fca468', - originalTimestamp: '2019-10-14T09:03:17.562Z', - anonymousId: '00000000000000000000000000', - userId: '123456', - integrations: { - All: true, - }, - sentAt: '2019-10-14T09:03:22.563Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/users/update', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - email: 'ruchira@rudderlabs.com', - dataFields: { - email: 'ruchira@rudderlabs.com', - }, - userId: '123456', - preferUserId: true, - mergeNestedObjects: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 25', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'alias', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - userId: 'new@email.com', - previousId: 'old@email.com', - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - currentEmail: 'old@email.com', - newEmail: 'new@email.com', - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/users/updateEmail', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 26', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'alias', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - userId: 'new@email.com', - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Missing required value from "previousId"', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 27', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'alias', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-6f62b91e789a636929ca38aed01c5f6e-103c720d-81bd-4742-98d6-d45a65aed23e', - properties: { - url: 'https://dominos.com', - title: 'Pizza', - referrer: 'https://google.com', - }, - previousId: 'old@email.com', - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: false, - trackCategorisedPages: true, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Missing required value from "userId"', - statTags: { - destType: 'ITERABLE', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 28', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'john@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'product added', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - campaignId: '1', - templateId: '0', - orderId: 10000, - total: 1000, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: ['bikes', 'cars', 'motors'], - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: '2', - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: ['Bikes2', 'cars2', 'motors2'], - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - user: { - email: 'john@gmail.com', - dataFields: { - email: 'john@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['bikes', 'cars', 'motors'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Bikes2', 'cars2', 'motors2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 29', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'product added', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - campaignId: '1', - templateId: '0', - orderId: 10000, - total: 1000, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - price: '19', - position: '1', - category: 'shirts,pants,trousers', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - quantity: '2', - }, - { - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - ], - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd799439011', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - categories: ['shirts', 'pants', 'trousers'], - price: 19, - quantity: 2, - imageUrl: 'https://www.example.com/product/path.jpg', - url: 'https://www.example.com/product/path', - }, - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 30', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'product added', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - campaignId: '1', - templateId: '0', - orderId: 10000, - total: 1000, - product_id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - price: '192', - quantity: 22, - position: '12', - category: 'Cars2', - url: 'https://www.example.com/product/path2', - image_url: 'https://www.example.com/product/path.jpg2', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - apiKey: '12345', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/updateCart', - headers: { - 'Content-Type': 'application/json', - api_key: '12345', - }, - params: {}, - body: { - JSON: { - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: '507f1f77bcf86cd7994390112', - sku: '45790-322', - name: 'Monopoly: 3rd Edition2', - categories: ['Cars2'], - price: 192, - quantity: 22, - imageUrl: 'https://www.example.com/product/path.jpg2', - url: 'https://www.example.com/product/path2', - }, - ], - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 31', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - passcode: - 'fbee74a147828e2932c701d19dc1f2dcfa4ac0048be3aa3a88d427090a59dc1c0fa002f1', - accountId: '476550467', - trackAnonymous: true, - enableObjectIdMapping: false, - }, - }, - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - traits: { - email: 'sayan@gmail.com', - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - }, - event: 'order completed', - type: 'track', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - originalTimestamp: '2019-10-14T11:15:18.299Z', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - product_id: 1234, - name: 'Shoes', - price: 45, - quantity: 1, - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - }, - integrations: { - All: true, - }, - name: 'ApplicationLoaded', - sentAt: '2019-10-14T11:15:53.296Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.iterable.com/api/commerce/trackPurchase', - headers: { - 'Content-Type': 'application/json', - }, - params: {}, - body: { - JSON: { - dataFields: { - product_id: 1234, - name: 'Shoes', - price: 45, - quantity: 1, - orderId: 10000, - total: '1000', - campaignId: '123456', - templateId: '1213458', - }, - id: '10000', - createdAt: 1571051718299, - campaignId: 123456, - templateId: 1213458, - total: 1000, - user: { - email: 'sayan@gmail.com', - dataFields: { - email: 'sayan@gmail.com', - }, - userId: '12345', - preferUserId: true, - mergeNestedObjects: true, - }, - items: [ - { - id: 1234, - name: 'Shoes', - price: 45, - quantity: 1, - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 32', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-570110489d3e99b234b18af9a9eca9d4-6009779e-82d7-469d-aaeb-5ccf162b0453', - properties: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - userId: 'abcdeeeeeeeexxxx102', - createdAt: 1598631966468, - dataFields: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/events/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'iterable', - description: 'Test 33', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: '', - sentAt: '2020-08-28T16:26:16.473Z', - context: { - library: { - name: 'analytics-node', - version: '0.0.3', - }, - }, - _metadata: { - nodeVersion: '10.22.0', - }, - messageId: - 'node-570110489d3e99b234b18af9a9eca9d4-6009779e-82d7-469d-aaeb-5ccf162b0453', - properties: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - anonymousId: 'abcdeeeeeeeexxxx102', - originalTimestamp: '2020-08-28T16:26:06.468Z', - }, - destination: { - Config: { - apiKey: '62d12498c37c4fd8a1a546c2d35c2f60', - mapToSingleEvent: false, - trackAllPages: true, - trackCategorisedPages: false, - trackNamedPages: false, - }, - Enabled: true, - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - JSON: { - userId: 'abcdeeeeeeeexxxx102', - createdAt: 1598631966468, - dataFields: { - subject: 'resume validate', - sendtime: '2020-01-01', - sendlocation: 'akashdeep@gmail.com', - }, - }, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - api_key: '62d12498c37c4fd8a1a546c2d35c2f60', - 'Content-Type': 'application/json', - }, - version: '1', - endpoint: 'https://api.iterable.com/api/events/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...pageScreenTestData, + ...aliasTestData, + ...validationTestData, ]; diff --git a/test/integrations/destinations/iterable/processor/identifyTestData.ts b/test/integrations/destinations/iterable/processor/identifyTestData.ts new file mode 100644 index 0000000000..d05f87a11f --- /dev/null +++ b/test/integrations/destinations/iterable/processor/identifyTestData.ts @@ -0,0 +1,407 @@ +import { + generateMetadata, + transformResultBuilder, + generateIndentifyPayload, +} from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, +}; + +const headers = { + api_key: 'testApiKey', + 'Content-Type': 'application/json', +}; + +const user1Traits = { + name: 'manashi', + country: 'India', + city: 'Bangalore', + email: 'manashi@website.com', +}; + +const user2Traits = { + am_pm: 'AM', + pPower: 'AM', + boolean: true, + userId: 'Jacqueline', + firstname: 'Jacqueline', + administrative_unit: 'Minnesota', +}; + +const userId = 'userId'; +const anonymousId = 'anonId'; +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +const updateUserEndpoint = 'https://api.iterable.com/api/users/update'; + +export const identifyTestData: ProcessorTestData[] = [ + { + id: 'iterable-identify-test-1', + name: 'iterable', + description: 'Indentify call to update user in iterable with user traits', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload with all user traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId, + context: { + traits: user1Traits, + }, + traits: user1Traits, + type: 'identify', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: user1Traits.email, + userId: anonymousId, + dataFields: user1Traits, + preferUserId: false, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-2', + name: 'iterable', + description: 'Indentify call to update user email', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload with new email sent in payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateIndentifyPayload({ + userId, + anonymousId, + context: { + traits: { email: 'ruchira@rudderlabs.com' }, + }, + type: 'identify', + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: 'ruchira@rudderlabs.com', + userId, + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: false, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-3', + name: 'iterable', + description: 'Indentify call to update user email with preferUserId config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload with new email sent in payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, + message: generateIndentifyPayload({ + userId, + anonymousId, + context: { + traits: { email: 'ruchira@rudderlabs.com' }, + }, + type: 'identify', + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: 'ruchira@rudderlabs.com', + userId, + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: true, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-4', + name: 'iterable', + description: + 'Indentify call to update user email with traits present at root instead of context.traits', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload with new email sent in payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, + message: generateIndentifyPayload({ + userId, + anonymousId, + context: { + traits: {}, + }, + traits: { email: 'ruchira@rudderlabs.com' }, + type: 'identify', + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: 'ruchira@rudderlabs.com', + userId, + dataFields: { + email: 'ruchira@rudderlabs.com', + }, + preferUserId: true, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-5', + name: 'iterable', + description: 'Iterable rEtl test to update user', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { ...destination, Config: { ...destination.Config, preferUserId: true } }, + message: { + userId, + anonymousId, + context: { + externalId: [ + { + id: 'lynnanderson@smith.net', + identifierType: 'email', + type: 'ITERABLE-users', + }, + ], + mappedToDestination: 'true', + }, + traits: user2Traits, + type: 'identify', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + email: 'lynnanderson@smith.net', + userId, + dataFields: { ...user2Traits, email: 'lynnanderson@smith.net' }, + preferUserId: true, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-identify-test-6', + name: 'iterable', + description: 'Iterable rEtl test to update user traits', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain update user payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'Matthew', + anonymousId, + context: { + externalId: [ + { + id: 'Matthew', + identifierType: 'userId', + type: 'ITERABLE-users', + }, + ], + mappedToDestination: 'true', + }, + traits: user2Traits, + type: 'identify', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateUserEndpoint, + JSON: { + userId: 'Matthew', + dataFields: { ...user2Traits, userId: 'Matthew' }, + preferUserId: false, + mergeNestedObjects: true, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/processor/pageScreenTestData.ts b/test/integrations/destinations/iterable/processor/pageScreenTestData.ts new file mode 100644 index 0000000000..074d6b56df --- /dev/null +++ b/test/integrations/destinations/iterable/processor/pageScreenTestData.ts @@ -0,0 +1,409 @@ +import { generateMetadata, transformResultBuilder } from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, +}; + +const headers = { + api_key: 'testApiKey', + 'Content-Type': 'application/json', +}; + +const properties = { + path: '/abc', + referrer: '', + search: '', + title: '', + url: '', + category: 'test-category', +}; + +const anonymousId = 'anonId'; +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +const pageEndpoint = 'https://api.iterable.com/api/events/track'; + +export const pageScreenTestData: ProcessorTestData[] = [ + { + id: 'iterable-page-test-1', + name: 'iterable', + description: 'Page call with name and properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain page name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties, + type: 'page', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + userId: anonymousId, + dataFields: properties, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded page', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-page-test-2', + name: 'iterable', + description: 'Page call with name and properties and mapToSingleEvent config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain page name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, mapToSingleEvent: true }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: { ...properties, campaignId: '123456', templateId: '1213458' }, + type: 'page', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + campaignId: 123456, + templateId: 1213458, + userId: anonymousId, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'Loaded a Page', + dataFields: { ...properties, campaignId: '123456', templateId: '1213458' }, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-page-test-3', + name: 'iterable', + description: 'Page call with name and properties and trackNamedPages config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain page name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, trackNamedPages: true, trackAllPages: false }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties, + type: 'page', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + userId: anonymousId, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded page', + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-screen-test-1', + name: 'iterable', + description: 'Screen call with name and properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain screen name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, trackCategorisedPages: true, trackAllPages: false }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties, + type: 'screen', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + userId: anonymousId, + dataFields: properties, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded screen', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-screen-test-2', + name: 'iterable', + description: 'Screen call with name and properties and mapToSingleEvent config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain screen name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, mapToSingleEvent: true }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: { ...properties, campaignId: '123456', templateId: '1213458' }, + type: 'screen', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + campaignId: 123456, + templateId: 1213458, + userId: anonymousId, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'Loaded a Screen', + dataFields: { ...properties, campaignId: '123456', templateId: '1213458' }, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-screen-test-3', + name: 'iterable', + description: 'Page call with name and properties and trackNamedPages config set to true', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain page name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ...destination, + Config: { ...destination.Config, trackNamedPages: true, trackAllPages: false }, + }, + message: { + anonymousId, + name: 'ApplicationLoaded', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties, + type: 'screen', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: pageEndpoint, + JSON: { + userId: anonymousId, + email: 'sayan@gmail.com', + createdAt: 1598631966468, + eventName: 'ApplicationLoaded screen', + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/processor/trackTestData.ts b/test/integrations/destinations/iterable/processor/trackTestData.ts new file mode 100644 index 0000000000..296275ad77 --- /dev/null +++ b/test/integrations/destinations/iterable/processor/trackTestData.ts @@ -0,0 +1,717 @@ +import { + generateMetadata, + generateTrackPayload, + transformResultBuilder, +} from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + preferUserId: false, + trackAllPages: true, + trackNamedPages: false, + mapToSingleEvent: false, + trackCategorisedPages: false, + }, + Enabled: true, +}; + +const headers = { + api_key: 'testApiKey', + 'Content-Type': 'application/json', +}; + +const properties = { + subject: 'resume validate', + sendtime: '2020-01-01', + sendlocation: 'akashdeep@gmail.com', +}; + +const customEventProperties = { + campaignId: '1', + templateId: '0', + user_actual_id: 12345, + category: 'test-category', + email: 'ruchira@rudderlabs.com', + user_actual_role: 'system_admin, system_user', +}; + +const productInfo = { + price: 797, + variant: 'Oak', + quantity: 1, + quickship: true, + full_price: 1328, + product_id: 10606, + non_interaction: 1, + sku: 'JB24691400-W05', + name: 'Vira Console Cabinet', + cart_id: 'bd9b8dbf4ef8ee01d4206b04fe2ee6ae', +}; + +const orderCompletedProductInfo = { + price: 45, + quantity: 1, + total: '1000', + name: 'Shoes', + orderId: 10000, + product_id: 1234, + campaignId: '123456', + templateId: '1213458', +}; + +const products = [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: '19', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + quantity: '2', + }, + { + product_id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + price: '192', + quantity: 22, + position: '12', + category: 'Cars2', + url: 'https://www.example.com/product/path2', + image_url: 'https://www.example.com/product/path.jpg2', + }, +]; + +const items = [ + { + id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + categories: ['cars'], + price: 19, + quantity: 2, + imageUrl: 'https://www.example.com/product/path.jpg', + url: 'https://www.example.com/product/path', + }, + { + id: '507f1f77bcf86cd7994390112', + sku: '45790-322', + name: 'Monopoly: 3rd Edition2', + categories: ['Cars2'], + price: 192, + quantity: 22, + imageUrl: 'https://www.example.com/product/path.jpg2', + url: 'https://www.example.com/product/path2', + }, +]; + +const userId = 'userId'; +const anonymousId = 'anonId'; +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +const endpoint = 'https://api.iterable.com/api/events/track'; +const updateCartEndpoint = 'https://api.iterable.com/api/commerce/updateCart'; +const trackPurchaseEndpoint = 'https://api.iterable.com/api/commerce/trackPurchase'; + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'iterable-track-test-1', + name: 'iterable', + description: 'Track call to add event with user', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event properties and event name', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId, + event: 'Email Opened', + type: 'track', + context: {}, + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint, + JSON: { + userId: 'anonId', + createdAt: 1598631966468, + eventName: 'Email Opened', + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-2', + name: 'iterable', + description: 'Track call for product added event with all properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'product added', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: { + campaignId: '1', + templateId: '0', + orderId: 10000, + total: 1000, + products, + }, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateCartEndpoint, + JSON: { + user: { + email: 'sayan@gmail.com', + dataFields: { + email: 'sayan@gmail.com', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + items, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-3', + name: 'iterable', + description: 'Track call for order completed event with all properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'order completed', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: { + orderId: 10000, + total: '1000', + campaignId: '123456', + templateId: '1213458', + products, + }, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: trackPurchaseEndpoint, + JSON: { + dataFields: { + orderId: 10000, + total: '1000', + campaignId: '123456', + templateId: '1213458', + products, + }, + id: '10000', + createdAt: 1598631966468, + campaignId: 123456, + templateId: 1213458, + total: 1000, + user: { + email: 'sayan@gmail.com', + dataFields: { + email: 'sayan@gmail.com', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + items, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-4', + name: 'iterable', + description: 'Track call for custom event with all properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain custom event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'test track event GA3', + context: { + traits: { + email: 'sayan@gmail.com', + }, + }, + properties: customEventProperties, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint, + JSON: { + email: 'ruchira@rudderlabs.com', + dataFields: customEventProperties, + userId, + eventName: 'test track event GA3', + createdAt: 1598631966468, + campaignId: 1, + templateId: 0, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-5', + name: 'iterable', + description: 'Track call for product added event with product info as properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'product added', + context: { + traits: { + email: 'jessica@jlpdesign.net', + }, + }, + properties: productInfo, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateCartEndpoint, + JSON: { + user: { + email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + items: [ + { + id: productInfo.product_id, + sku: productInfo.sku, + name: productInfo.name, + price: productInfo.price, + quantity: productInfo.quantity, + }, + ], + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-6', + name: 'iterable', + description: 'Track call for product added event with product info as properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'product added', + context: { + traits: { + email: 'jessica@jlpdesign.net', + }, + }, + properties: { + campaignId: '1', + templateId: '0', + orderId: 10000, + total: 1000, + ...products[1], + }, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: updateCartEndpoint, + JSON: { + user: { + email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + items: [ + { + price: 192, + url: products[1].url, + sku: products[1].sku, + name: products[1].name, + id: products[1].product_id, + quantity: products[1].quantity, + imageUrl: products[1].image_url, + categories: [products[1].category], + }, + ], + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-7', + name: 'iterable', + description: 'Track call for order completed event with product info as properties', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event name and all properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateTrackPayload({ + userId, + anonymousId, + event: 'order completed', + context: { + traits: { + email: 'jessica@jlpdesign.net', + }, + }, + properties: orderCompletedProductInfo, + sentAt, + originalTimestamp, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint: trackPurchaseEndpoint, + JSON: { + dataFields: orderCompletedProductInfo, + user: { + email: 'jessica@jlpdesign.net', + dataFields: { + email: 'jessica@jlpdesign.net', + }, + userId, + preferUserId: false, + mergeNestedObjects: true, + }, + id: '10000', + total: 1000, + campaignId: 123456, + templateId: 1213458, + createdAt: 1598631966468, + items: [ + { + id: orderCompletedProductInfo.product_id, + name: orderCompletedProductInfo.name, + price: orderCompletedProductInfo.price, + quantity: orderCompletedProductInfo.quantity, + }, + ], + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-8', + name: 'iterable', + description: 'Track call without event name and userId', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + anonymousId, + type: 'track', + context: {}, + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint, + JSON: { + userId: anonymousId, + createdAt: 1598631966468, + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-track-test-8', + name: 'iterable', + description: 'Track call without event name', + scenario: 'Business', + successCriteria: + 'Response should contain status code 200 and it should contain event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId, + anonymousId, + type: 'track', + context: {}, + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + headers, + endpoint, + JSON: { + userId, + createdAt: 1598631966468, + dataFields: properties, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/iterable/processor/validationTestData.ts b/test/integrations/destinations/iterable/processor/validationTestData.ts new file mode 100644 index 0000000000..86728a868b --- /dev/null +++ b/test/integrations/destinations/iterable/processor/validationTestData.ts @@ -0,0 +1,258 @@ +import { generateMetadata } from './../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const destination: Destination = { + ID: '123', + Name: 'iterable', + DestinationDefinition: { + ID: '123', + Name: 'iterable', + DisplayName: 'Iterable', + Config: {}, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + apiKey: 'testApiKey', + mapToSingleEvent: false, + trackAllPages: false, + trackCategorisedPages: true, + trackNamedPages: false, + }, + Enabled: true, +}; + +const properties = { + url: 'https://dominos.com', + title: 'Pizza', + referrer: 'https://google.com', +}; + +const sentAt = '2020-08-28T16:26:16.473Z'; +const originalTimestamp = '2020-08-28T16:26:06.468Z'; + +const expectedStatTags = { + destType: 'ITERABLE', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'iterable-validation-test-1', + name: 'iterable', + description: "[Error]: Page call without it's required configuration", + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw configuration error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'sajal12', + anonymousId: 'abcdeeeeeeeexxxx102', + context: { + traits: { + email: 'abc@example.com', + }, + }, + properties, + type: 'page', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Invalid page call', + statTags: { ...expectedStatTags, errorType: 'configuration' }, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-validation-test-2', + name: 'iterable', + description: '[Error]: Identify call without userId and email', + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw instrumentation error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + context: {}, + type: 'identify', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'userId or email is mandatory for this request', + statTags: expectedStatTags, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-validation-test-3', + name: 'iterable', + description: '[Error]: Message type is not supported', + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw instrumentation error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + context: {}, + type: 'group', + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Message type group not supported', + statTags: expectedStatTags, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-validation-test-4', + name: 'iterable', + description: '[Error]: Missing required value for alias call', + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw instrumentation error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + context: {}, + type: 'alias', + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Missing required value from "previousId"', + statTags: expectedStatTags, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'iterable-validation-test-5', + name: 'iterable', + description: '[Error]: Missing userId value for alias call', + scenario: 'Framework', + successCriteria: + 'Response should contain status code 400 and it should throw instrumentation error with respective message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + context: {}, + type: 'alias', + previousId: 'old@email.com', + anonymousId: 'anonId', + properties, + sentAt, + originalTimestamp, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Missing required value from "userId"', + statTags: expectedStatTags, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/klaviyo_bulk_upload/processor/data.ts b/test/integrations/destinations/klaviyo_bulk_upload/processor/data.ts new file mode 100644 index 0000000000..0a97f9035d --- /dev/null +++ b/test/integrations/destinations/klaviyo_bulk_upload/processor/data.ts @@ -0,0 +1,490 @@ +export const data = [ + { + name: 'klaviyo_bulk_upload', + description: 'Successful identify event with location data', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'sources', + context: { + externalId: [ + { + id: 'user1', + identifierType: 'userId', + type: 'KLAVIYO_BULK_UPLOAD-userProfiles', + }, + ], + mappedToDestination: 'true', + sources: { + job_id: '2gif2bMzsX1Nt0rbV1vcbAE3cxC', + job_run_id: 'cp5p5ilq47pqg38v2nfg', + task_run_id: 'cp5p5ilq47pqg38v2ng0', + version: '2051/merge', + }, + }, + traits: { + address1: 'dallas street', + address2: 'oppenheimer market', + city: 'delhi', + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + country: 'India', + phone_number: '+919902330123', + ip: '213.5.6.41', + }, + type: 'identify', + userId: '1', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + privateApiKey: 'pk_dummy_123', + listId: 'list101', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: [ + { + type: 'profile', + attributes: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + location: { + address1: 'dallas street', + address2: 'oppenheimer market', + city: 'delhi', + country: 'India', + ip: '213.5.6.41', + }, + anonymous_id: 'user1', + jobIdentifier: 'user1:1', + }, + }, + ], + }, + }, + relationships: { + lists: { + data: [ + { + type: 'list', + id: 'list101', + }, + ], + }, + }, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + }, + { + name: 'klaviyo_bulk_upload', + description: 'Successful identify event without location data', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'sources', + context: { + externalId: [ + { + id: 'user1', + identifierType: 'userId', + type: 'KLAVIYO_BULK_UPLOAD-userProfiles', + }, + ], + mappedToDestination: 'true', + sources: { + job_id: '2gif2bMzsX1Nt0rbV1vcbAE3cxC', + job_run_id: 'cp5p5ilq47pqg38v2nfg', + task_run_id: 'cp5p5ilq47pqg38v2ng0', + version: '2051/merge', + }, + }, + traits: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + ip: '213.5.6.41', + }, + type: 'identify', + userId: '1', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + privateApiKey: 'pk_dummy_123', + listId: 'list101', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: [ + { + type: 'profile', + attributes: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + location: { + ip: '213.5.6.41', + }, + anonymous_id: 'user1', + jobIdentifier: 'user1:1', + }, + }, + ], + }, + }, + relationships: { + lists: { + data: [ + { + type: 'list', + id: 'list101', + }, + ], + }, + }, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + }, + { + name: 'klaviyo_bulk_upload', + description: 'Successful identify event without listId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'sources', + context: { + externalId: [ + { + id: 'user1', + identifierType: 'userId', + type: 'KLAVIYO_BULK_UPLOAD-userProfiles', + }, + ], + mappedToDestination: 'true', + sources: { + job_id: '2gif2bMzsX1Nt0rbV1vcbAE3cxC', + job_run_id: 'cp5p5ilq47pqg38v2nfg', + task_run_id: 'cp5p5ilq47pqg38v2ng0', + version: '2051/merge', + }, + }, + traits: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + ip: '213.5.6.41', + }, + type: 'identify', + userId: '1', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + privateApiKey: 'pk_dummy_123', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: [ + { + type: 'profile', + attributes: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + location: { + ip: '213.5.6.41', + }, + anonymous_id: 'user1', + jobIdentifier: 'user1:1', + }, + }, + ], + }, + }, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + }, + { + name: 'klaviyo_bulk_upload', + description: 'Successful identify event with custom properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + channel: 'sources', + context: { + externalId: [ + { + id: 'user1', + identifierType: 'userId', + type: 'KLAVIYO_BULK_UPLOAD-userProfiles', + }, + ], + mappedToDestination: 'true', + sources: { + job_id: '2gif2bMzsX1Nt0rbV1vcbAE3cxC', + job_run_id: 'cp5p5ilq47pqg38v2nfg', + task_run_id: 'cp5p5ilq47pqg38v2ng0', + version: '2051/merge', + }, + }, + traits: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + ip: '213.5.6.41', + last_visit_date: '2020-10-01T00:00:00Z', + lastVisitService: ['Brazilian'], + }, + type: 'identify', + userId: '1', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + privateApiKey: 'pk_dummy_123', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + data: { + type: 'profile-bulk-import-job', + attributes: { + profiles: { + data: [ + { + type: 'profile', + attributes: { + email: 'qwe22@mail.com', + first_name: 'Testqwe0022', + last_name: 'user', + phone_number: '+919902330123', + location: { + ip: '213.5.6.41', + }, + anonymous_id: 'user1', + jobIdentifier: 'user1:1', + properties: { + lastVisitService: ['Brazilian'], + last_visit_date: '2020-10-01T00:00:00Z', + }, + }, + }, + ], + }, + }, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + jobId: 1, + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koala/processor/data.ts b/test/integrations/destinations/koala/processor/data.ts new file mode 100644 index 0000000000..b866353066 --- /dev/null +++ b/test/integrations/destinations/koala/processor/data.ts @@ -0,0 +1,323 @@ +export const data = [ + { + name: 'koala', + description: 'Sucessful track event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Signed Up', + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + email: 'johndoe@somemail.com', + label: 'test', + value: 10, + }, + context: { + network: 'wifi', + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.22.33', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.22.33', + email: 'johndoe@somemail.com', + events: [ + { + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + email: 'johndoe@somemail.com', + label: 'test', + value: 10, + }, + context: { + network: 'wifi', + }, + }, + ], + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + userId: '', + headers: { + 'content-type': 'application/json', + }, + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Successful identify event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'identify', + traits: { + FirstName: 'John', + LastName: 'Doe', + address: { + city: 'San Francisco', + state: 'CA', + postalCode: '94107', + }, + email: 'johndoe@somemail.com', + ko_profile_id: 'xxxx-2222-xxxx-xxxx', + }, + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + email: 'johndoe@somemail.com', + profile_id: 'xxxx-2222-xxxx-xxxx', + identifies: [ + { + type: 'identify', + sent_at: '2024-01-23T08:35:17.342Z', + traits: { + FirstName: 'John', + LastName: 'Doe', + address: { + city: 'San Francisco', + state: 'CA', + postalCode: '94107', + }, + email: 'johndoe@somemail.com', + }, + }, + ], + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + userId: '', + headers: { + 'content-type': 'application/json', + }, + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Missing required email or ko_profile_id fields in traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'track', + traits: { + name: 'John Doe', + }, + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Neither email or ko_profile_id are present on traits. Aborting message: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Neither email or ko_profile_id are present on traits. Aborting message', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'KOALA', + module: 'destination', + implementation: 'cdkV2', + destinationId: 'destId', + workspaceId: 'wspId', + feature: 'processor', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'koala', + description: 'Invalid message type page', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + type: 'page', + groupId: 'group-uuid', + originalTimestamp: '2024-01-23T08:35:17.342Z', + sentAt: '2024-01-23T08:35:35.234Z', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'message type page is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type page is not supported', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'KOALA', + module: 'destination', + implementation: 'cdkV2', + destinationId: 'destId', + workspaceId: 'wspId', + feature: 'processor', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koala/router/data.ts b/test/integrations/destinations/koala/router/data.ts new file mode 100644 index 0000000000..f998f48dc4 --- /dev/null +++ b/test/integrations/destinations/koala/router/data.ts @@ -0,0 +1,204 @@ +export const data = [ + { + name: 'koala', + description: 'Router batch request', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Signed Up', + type: 'track', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + traits: { + email: 'johndoe@somemail.com', + }, + properties: { + label: 'test', + value: 10, + }, + context: { + network: 'wifi', + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.22.33', + }, + metadata: { + jobId: 1, + userId: 'u1', + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + { + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + message: { + userId: 'user-uuid', + annonymousId: 'annonymous-uuid', + event: 'User Deleted account', + type: 'track', + messageId: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', + traits: { + ko_profile_id: '123456', + }, + properties: { + attr1: 'foo', + attr2: 'bar', + }, + context: { + network: 'wifi', + }, + originalTimestamp: '2024-01-23T08:35:17.562Z', + sentAt: '2024-01-23T08:35:17.562Z', + request_ip: '192.11.55.1', + }, + metadata: { + jobId: 2, + userId: 'u1', + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + destType: 'koala', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.22.33', + email: 'johndoe@somemail.com', + events: [ + { + type: 'track', + event: 'User Signed Up', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '84e26acc-56a5-4835-8233-591137fca468', + properties: { + label: 'test', + value: 10, + }, + context: { + network: 'wifi', + }, + }, + ], + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + }, + batched: false, + metadata: [{ jobId: 1, userId: 'u1', workspaceId: 'wspId', destinationId: 'destId' }], + statusCode: 200, + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + }, + { + batchedRequest: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + ip: '192.11.55.1', + profile_id: '123456', + events: [ + { + type: 'track', + event: 'User Deleted account', + sent_at: '2024-01-23T08:35:17.562Z', + message_id: '8bc79b03-2a5c-4615-b2da-54c0aaaaaae8', + properties: { + attr1: 'foo', + attr2: 'bar', + }, + context: { + network: 'wifi', + }, + }, + ], + }, + }, + endpoint: 'https://api2.getkoala.com/web/projects/kkooaallaa321/batch', + files: {}, + params: {}, + type: 'REST', + version: '1', + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + }, + batched: false, + metadata: [{ jobId: 2, userId: 'u1', workspaceId: 'wspId', destinationId: 'destId' }], + statusCode: 200, + destination: { + Config: { + publicKey: 'kkooaallaa321', + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + }, + ], + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/common.ts b/test/integrations/destinations/koddi/common.ts new file mode 100644 index 0000000000..ec83a0f446 --- /dev/null +++ b/test/integrations/destinations/koddi/common.ts @@ -0,0 +1,78 @@ +import { Destination } from '../../../../src/types'; + +const destType = 'koddi'; +const destTypeInUpperCase = 'KODDI'; +const displayName = 'Koddi'; +const channel = 'web'; +const destination: Destination = { + Config: { + apiBaseUrl: 'https://www.test-client.com', + clientName: 'test-client', + }, + DestinationDefinition: { + DisplayName: displayName, + ID: '123', + Name: destTypeInUpperCase, + Config: { cdkV2Enabled: true }, + }, + Enabled: true, + ID: '123', + Name: destTypeInUpperCase, + Transformations: [], + WorkspaceID: 'test-workspace-id', +}; + +const processorInstrumentationErrorStatTags = { + destType: destTypeInUpperCase, + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +const RouterInstrumentationErrorStatTags = { + ...processorInstrumentationErrorStatTags, + feature: 'router', +}; + +const getHeader = { + accept: 'application/json', +}; + +const postHeader = { + ...getHeader, + 'content-type': 'application/json', +}; + +const bidders = [ + { + bidder: 'bidder1', + alternate_bidder: 'alternate1', + count: 1, + base_price: 100, + total_price: 227, + }, +]; + +const alternateBidders = [ + { + count: 1, + base_price: 100, + total_price: 227, + }, +]; + +export { + destType, + channel, + destination, + processorInstrumentationErrorStatTags, + RouterInstrumentationErrorStatTags, + getHeader, + postHeader, + bidders, + alternateBidders, +}; diff --git a/test/integrations/destinations/koddi/processor/clicks.ts b/test/integrations/destinations/koddi/processor/clicks.ts new file mode 100644 index 0000000000..6101e9bafe --- /dev/null +++ b/test/integrations/destinations/koddi/processor/clicks.ts @@ -0,0 +1,70 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, getHeader } from '../common'; + +export const clicks: ProcessorTestData[] = [ + { + id: 'Clicks-test', + name: destType, + description: 'Clicks call: Example Clicks Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventType: 'Clicks', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=click', + headers: getHeader, + userId: '', + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + userGuid: 'userId123', + clientName: destination.Config.clientName, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/processor/conversions.ts b/test/integrations/destinations/koddi/processor/conversions.ts new file mode 100644 index 0000000000..1647ffed7d --- /dev/null +++ b/test/integrations/destinations/koddi/processor/conversions.ts @@ -0,0 +1,142 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { + destType, + channel, + destination, + postHeader, + bidders, + alternateBidders, + processorInstrumentationErrorStatTags, +} from '../common'; + +export const conversions: ProcessorTestData[] = [ + { + id: 'Conversions-test-1', + name: destType, + description: 'Conversions call: Example Conversions Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + bidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventType: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: destination.Config.apiBaseUrl + '/conversion', + headers: postHeader, + userId: '', + JSON: { + client_name: destination.Config.clientName, + culture: 'en-US', + currency: 'USD', + transaction_id: 'ABC123', + unixtime: 1709566376, + user_guid: 'userId123', + user_ip: '127.0.0.1', + bidders: bidders, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'Conversions-test-2', + name: destType, + description: 'Conversions call: Example Conversions Event with missing required field', + scenario: 'Framework+Business', + successCriteria: 'Response should contain error and status code should be 400', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + alternateBidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventType: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Missing required value from "properties.bidders": Workflow: procWorkflow, Step: preparePayload, ChildStep: undefined, OriginalError: Missing required value from "properties.bidders"', + statusCode: 400, + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/processor/data.ts b/test/integrations/destinations/koddi/processor/data.ts new file mode 100644 index 0000000000..5c3d7da472 --- /dev/null +++ b/test/integrations/destinations/koddi/processor/data.ts @@ -0,0 +1,5 @@ +import { impressions } from './impressions'; +import { clicks } from './clicks'; +import { conversions } from './conversions'; + +export const data = [...impressions, ...clicks, ...conversions]; diff --git a/test/integrations/destinations/koddi/processor/impressions.ts b/test/integrations/destinations/koddi/processor/impressions.ts new file mode 100644 index 0000000000..840ed9139f --- /dev/null +++ b/test/integrations/destinations/koddi/processor/impressions.ts @@ -0,0 +1,70 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, getHeader } from '../common'; + +export const impressions: ProcessorTestData[] = [ + { + id: 'Impressions-test', + name: destType, + description: 'Impressions call: Example Impression Event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventType: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=impression', + headers: getHeader, + userId: '', + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + ts: '2024-03-04T15:32:56.409Z', + clientName: destination.Config.clientName, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/koddi/router/data.ts b/test/integrations/destinations/koddi/router/data.ts new file mode 100644 index 0000000000..1601a481e5 --- /dev/null +++ b/test/integrations/destinations/koddi/router/data.ts @@ -0,0 +1,262 @@ +import { RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; +import { + destType, + channel, + destination, + getHeader, + postHeader, + RouterInstrumentationErrorStatTags, + bidders, +} from '../common'; + +const routerRequest: RouterTransformationRequest = { + input: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventType: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Clicks Event', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventType: 'Clicks', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Conversions Event', + properties: { + currency: 'USD', + transaction_id: 'ABC123', + bidders, + }, + context: { + locale: 'en-US', + ip: '127.0.0.1', + }, + integrations: { + All: true, + koddi: { + eventType: 'Conversions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(3), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Example Impression Event', + properties: { + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventType: 'Impressions', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(4), + }, + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + properties: { + tracking_data: 'dummy-tracking-data', + rank: 1, + beacon_issued: '2024-03-04T15:32:56.409Z', + }, + integrations: { + All: true, + koddi: { + eventType: 'Unknown', + }, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(5), + }, + ], + destType, +}; + +export const data = [ + { + id: 'koddi-router-test', + name: destType, + description: 'Basic Router Test to test payloads and missing field error', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for missing fields and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=impression', + headers: getHeader, + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + ts: '2024-03-04T15:32:56.409Z', + clientName: destination.Config.clientName, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(1)], + statusCode: 200, + }, + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'GET', + endpoint: destination.Config.apiBaseUrl + '?action=click', + headers: getHeader, + params: { + trackingData: 'dummy-tracking-data', + rank: 1, + beaconIssued: '2024-03-04T15:32:56.409Z', + userGuid: 'userId123', + clientName: destination.Config.clientName, + }, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(2)], + statusCode: 200, + }, + { + batched: false, + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.apiBaseUrl + '/conversion', + headers: postHeader, + params: {}, + body: { + JSON: { + client_name: 'test-client', + culture: 'en-US', + currency: 'USD', + transaction_id: 'ABC123', + unixtime: 1709566376, + user_guid: 'userId123', + user_ip: '127.0.0.1', + bidders: bidders, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + destination, + metadata: [generateMetadata(3)], + statusCode: 200, + }, + { + batched: false, + error: 'Missing required value from "properties.tracking_data"', + destination, + metadata: [generateMetadata(4)], + statTags: RouterInstrumentationErrorStatTags, + statusCode: 400, + }, + { + batched: false, + error: 'event type unknown is not supported', + destination, + metadata: [generateMetadata(5)], + statTags: RouterInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/dataDelivery/business.ts b/test/integrations/destinations/linkedin_ads/dataDelivery/business.ts new file mode 100644 index 0000000000..ff4fa4455f --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/dataDelivery/business.ts @@ -0,0 +1,188 @@ +import { generateProxyV1Payload } from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +export const element = { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '0', + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + }, +}; + +export const wrongFormatElement = { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + userInfo: { + city: 'San Francisco', + }, + }, +}; + +export const testJSONData = { + elements: [{ ...element }], +}; + +export const wrongFormattedTestJSONData = { + elements: [{ ...wrongFormatElement }], +}; + +export const testJSONDataWithDifferentTypeConversion = { + elements: [ + { + ...element, + conversion: 'urn:li:partner:differentConversion', + }, + ], +}; + +export const statTags = { + destType: 'LINKEDIN_ADS', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const metadata = { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, +}; +export const headerBlockWithCorrectAccessToken = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; + +const commonRequestParametersWithWrongElemet = { + headers: headerBlockWithCorrectAccessToken, + JSON: wrongFormattedTestJSONData, +}; + +const commonRequestParametersWithDifferentConversion = { + headers: headerBlockWithCorrectAccessToken, + JSON: testJSONDataWithDifferentTypeConversion, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'linkedin_ads_v1_scenario_1', + name: 'linkedin_ads', + description: 'Event fails due to wrong process followed while creating a conversion', + successCriteria: 'Should return 400 and aborted', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParametersWithDifferentConversion, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + "LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. Incorrect conversions information provided. Conversion's method should be CONVERSIONS_API, indices [0] (0-indexed)", + response: [ + { + error: + '{"message":"Incorrect conversions information provided. Conversion\'s method should be CONVERSIONS_API, indices [0] (0-indexed)","status":400}', + statusCode: 400, + metadata, + }, + ], + statTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'linkedin_ads_v1_scenario_2', + name: 'linkedin_ads', + description: 'Event fails due to wrong format payload sent to linkedin', + successCriteria: 'Should return 400 with appropriate reason of failure', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParametersWithWrongElemet, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: { + message: + 'Index: 0, ERROR :: /conversionValue/amount :: field is required but not found and has no default value\nERROR :: /user/userInfo/firstName :: field is required but not found and has no default value\nERROR :: /user/userInfo/lastName :: field is required but not found and has no default value\n', + status: 422, + }, + status: 422, + }, + message: + '[LINKEDIN_CONVERSION_API Response V1 Handler] - Request Processed Successfully', + response: [ + { + error: + '/conversionValue/amount :: field is required but not found and has no default value', + statusCode: 400, + metadata, + }, + ], + status: 422, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/dataDelivery/data.ts b/test/integrations/destinations/linkedin_ads/dataDelivery/data.ts new file mode 100644 index 0000000000..5bb0a7ef6e --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/dataDelivery/data.ts @@ -0,0 +1,4 @@ +import { testScenariosForV1API, statTags as baseStatTags } from './business'; +import { oauthScenariosV1 } from './oauth'; + +export const data = [...testScenariosForV1API, ...oauthScenariosV1]; diff --git a/test/integrations/destinations/linkedin_ads/dataDelivery/oauth.ts b/test/integrations/destinations/linkedin_ads/dataDelivery/oauth.ts new file mode 100644 index 0000000000..5cc643d972 --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/dataDelivery/oauth.ts @@ -0,0 +1,207 @@ +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; +import { ProxyV1TestData } from '../../../testTypes'; + +export const testJSONData = { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '0', + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + }, + }, + ], +}; +export const statTags = { + destType: 'LINKEDIN_ADS', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const metadata = { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, +}; + +export const headerBlockWithCorrectAccessToken = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; + +const commonRequestParameters = { + headers: headerBlockWithCorrectAccessToken, + JSON: testJSONData, +}; +const commonRequestParametersWithInvalidAccess = { + headers: { ...headerBlockWithCorrectAccessToken, Authorization: 'Bearer invalidToken' }, + JSON: testJSONData, + accessToken: 'invalidToken', +}; + +const commonRequestParametersWithRevokedAccess = { + headers: { ...headerBlockWithCorrectAccessToken, Authorization: 'Bearer revokedToken' }, + JSON: testJSONData, + accessToken: 'revokedToken', +}; + +export const oauthScenariosV1: ProxyV1TestData[] = [ + { + id: 'linkedin_ads_v1_oauth_scenario_1', + name: 'linkedin_ads', + description: 'app event fails due to revoked access token error', + successCriteria: 'Should return 400 with revoked access token error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParametersWithRevokedAccess, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + response: [ + { + error: + '{"status":401,"serviceErrorCode":65601,"code":"REVOKED_ACCESS_TOKEN","message":"The token used in the request has been revoked by the user"}', + statusCode: 400, + metadata: { ...metadata, secret: { accessToken: 'revokedToken' } }, + }, + ], + statTags, + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + 'LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. Invalid or expired access token. Retrying', + status: 400, + }, + }, + }, + }, + }, + { + id: 'linkedin_ads_v1_oauth_scenario_2', + name: 'linkedin_ads', + description: 'app event fails due to invalid access token error', + successCriteria: 'Should return 500 with invalid access token error', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParametersWithInvalidAccess, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + response: [ + { + error: + '{"status":401,"serviceErrorCode":65600,"code":"INVALID_ACCESS_TOKEN","message":"Invalid access token"}', + statusCode: 500, + metadata: { ...metadata, secret: { accessToken: 'invalidToken' } }, + }, + ], + statTags: { ...statTags, errorType: 'retryable' }, + authErrorCategory: 'REFRESH_TOKEN', + message: + 'LinkedIn Conversion API: Error transformer proxy v1 during LinkedIn Conversion API response transformation. Invalid or expired access token. Retrying', + status: 500, + }, + }, + }, + }, + }, + { + id: 'linkedin_ads_v1_oauth_scenario_3', + name: 'linkedin_ads', + description: 'success case', + successCriteria: 'Should return 200 response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + ...commonRequestParameters, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[LINKEDIN_CONVERSION_API Response V1 Handler] - Request Processed Successfully', + destinationResponse: { + response: { + elements: [ + { + status: 201, + }, + { + status: 201, + }, + ], + }, + status: 200, + }, + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: 'success', + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/network.ts b/test/integrations/destinations/linkedin_ads/network.ts new file mode 100644 index 0000000000..890ad48589 --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/network.ts @@ -0,0 +1,186 @@ +export const headerBlockWithCorrectAccessToken = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; +export const element = { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '0', + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + }, +}; + +export const testJSONData = { + elements: [{ ...element }], +}; + +export const testJSONDataWithDifferentTypeConversion = { + elements: [ + { + ...element, + conversion: 'urn:li:partner:differentConversion', + }, + ], +}; + +export const wrongFormatElement = { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + userInfo: { + city: 'San Francisco', + }, + }, +}; + +export const wrongFormattedTestJSONData = { + elements: [{ ...wrongFormatElement }], +}; + +// MOCK DATA +const businessMockData = [ + { + description: 'Mock response from destination depicting request with a revoked access token', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: { ...headerBlockWithCorrectAccessToken, Authorization: 'Bearer revokedToken' }, + data: testJSONData, + }, + httpRes: { + data: { + status: 401, + serviceErrorCode: 65601, + code: 'REVOKED_ACCESS_TOKEN', + message: 'The token used in the request has been revoked by the user', + }, + status: 401, + statusText: 'OK', + }, + }, + { + description: 'Mock response from destination depicting request with an invalid access token', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: { ...headerBlockWithCorrectAccessToken, Authorization: 'Bearer invalidToken' }, + data: testJSONData, + }, + httpRes: { + data: { + status: 401, + serviceErrorCode: 65600, + code: 'INVALID_ACCESS_TOKEN', + message: 'Invalid access token', + }, + status: 401, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting a correct request with a valid access token', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: headerBlockWithCorrectAccessToken, + data: testJSONData, + }, + httpRes: { + data: { + elements: [ + { + status: 201, + }, + { + status: 201, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a conversion created differently than choosing direct API', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: headerBlockWithCorrectAccessToken, + data: testJSONDataWithDifferentTypeConversion, + }, + httpRes: { + data: { + message: + "Incorrect conversions information provided. Conversion's method should be CONVERSIONS_API, indices [0] (0-indexed)", + status: 400, + }, + status: 400, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a conversion created differently than choosing direct API', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: headerBlockWithCorrectAccessToken, + data: testJSONDataWithDifferentTypeConversion, + }, + httpRes: { + data: { + message: + "Incorrect conversions information provided. Conversion's method should be CONVERSIONS_API, indices [0] (0-indexed)", + status: 400, + }, + status: 400, + statusText: 'OK', + }, + }, + { + description: + 'Mock response from destination depicting request with a conversion created differently than choosing direct API', + httpReq: { + method: 'post', + url: 'https://api.linkedin.com/rest/conversionEvents', + headers: headerBlockWithCorrectAccessToken, + data: wrongFormattedTestJSONData, + }, + httpRes: { + data: { + message: + 'Index: 0, ERROR :: /conversionValue/amount :: field is required but not found and has no default value\nERROR :: /user/userInfo/firstName :: field is required but not found and has no default value\nERROR :: /user/userInfo/lastName :: field is required but not found and has no default value\n', + status: 422, + }, + status: 422, + statusText: 'OK', + }, + }, +]; + +export const networkCallsData = [...businessMockData]; diff --git a/test/integrations/destinations/linkedin_ads/processor/configLevelFeaturesTestData.ts b/test/integrations/destinations/linkedin_ads/processor/configLevelFeaturesTestData.ts new file mode 100644 index 0000000000..287e35e5a7 --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/processor/configLevelFeaturesTestData.ts @@ -0,0 +1,219 @@ +import { + generateMetadata, + generateTrackPayload, + overrideDestination, + transformResultBuilder, +} from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + hashData: true, + conversionMapping: [ + { + from: 'ABC Searched', + to: '1234567', + }, + { + from: 'spin_result', + to: '23456', + }, + { + from: 'ABC Searched', + to: '34567', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + price: 400, + additional_bet_index: 0, + eventId: '12345', +}; + +const commonTimestamp = new Date('2023-10-14'); + +const commonHeader = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; + +export const configLevelFeaturesTestData: ProcessorTestData[] = [ + { + id: 'linkedin_ads-config-test-1', + name: 'linkedin_ads', + description: 'Track call : hashData is set to false and no deduplication key is provided', + scenario: 'Business', + successCriteria: 'email provided will not be hashed and eventId will be mapped from messageId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { hashData: false }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: 'abc@gmail.com', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-config-test-2', + name: 'linkedin_ads', + description: 'Track call : hashData is set to true and deduplication key is provided', + scenario: 'Business', + successCriteria: + 'email provided will be hashed and eventId will be mapped from deduplication key properties.eventId', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + deduplicationKey: `properties.eventId`, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/processor/data.ts b/test/integrations/destinations/linkedin_ads/processor/data.ts new file mode 100644 index 0000000000..edd6d1f1b5 --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/processor/data.ts @@ -0,0 +1,11 @@ +import { trackTestData } from './trackTestData'; +import { validationTestData } from './validationTestData'; +import { configLevelFeaturesTestData } from './configLevelFeaturesTestData'; + +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-15')); +}; +export const data = [...trackTestData, ...validationTestData, ...configLevelFeaturesTestData].map( + (d) => ({ ...d, mockFns }), +); diff --git a/test/integrations/destinations/linkedin_ads/processor/trackTestData.ts b/test/integrations/destinations/linkedin_ads/processor/trackTestData.ts new file mode 100644 index 0000000000..f9dfc528db --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/processor/trackTestData.ts @@ -0,0 +1,718 @@ +import { generateMetadata, generateTrackPayload, transformResultBuilder } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + hashData: true, + deduplicationKey: 'properties.eventId', + conversionMapping: [ + { + from: 'ABC Searched', + to: '1234567', + }, + { + from: 'spin_result', + to: '23456', + }, + { + from: 'ABC Searched', + to: '34567', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + price: 400, + additional_bet_index: 0, + eventId: '12345', +}; + +const commonPropertiesWithProducts = { + revenue: 400, + additional_bet_index: 0, + eventId: '12345', + products: [ + { + product_id: '123', + name: 'abc', + category: 'def', + brand: 'xyz', + variant: 'pqr', + price: 100, + quantity: 2, + }, + { + product_id: '456', + name: 'def', + category: 'abc', + brand: 'pqr', + variant: 'xyz', + price: 200, + quantity: 3, + }, + ], +}; + +const commonPropertiesWithProductsPriceNotPresentInAll = { + revenue: 400, + additional_bet_index: 0, + eventId: '12345', + products: [ + { + product_id: '123', + name: 'abc', + category: 'def', + brand: 'xyz', + variant: 'pqr', + quantity: 2, + }, + { + product_id: '456', + name: 'def', + category: 'abc', + brand: 'pqr', + variant: 'xyz', + price: 200, + quantity: 3, + }, + ], +}; + +const commonTimestamp = new Date('2023-10-14'); + +const commonStatTags = { + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + implementation: 'cdkV2', + destType: 'LINKEDIN_ADS', + module: 'destination', + feature: 'processor', + workspaceId: 'default-workspaceId', +}; + +const commonHeader = { + Authorization: 'Bearer default-accessToken', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', +}; + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'linkedin_ads-track-test-1', + name: 'linkedin_ads', + description: 'Track call : particular track event mapped to a specific conversion rule', + scenario: 'Business', + successCriteria: + 'event will respect the UI mapping and create a conversion event with the mapped conversion rule', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-2', + name: 'linkedin_ads', + description: 'Track call : event is mapped with more than one conversion rules ', + scenario: 'Business', + successCriteria: + 'event will respect the UI mapping and create a conversion event with the mapped conversion rule and club the two conversions in a single elements array', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'ABC Searched', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:1234567', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + }, + }, + { + conversion: 'urn:lla:llaPartnerConversion:34567', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-3', + name: 'linkedin_ads', + description: 'Track call : track event containing multiple allowed user identifiqers', + scenario: 'Business', + successCriteria: + 'event will respect the UI mapping and create a conversion event with the mapped conversion rule', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + externalId: [ + { + id: 'test@rudderlabs.com', + type: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + }, + { + id: 'test@rudderlabs.com', + type: 'ACXIOM_ID', + }, + { + id: 'test@rudderlabs.com', + type: 'ORACLE_MOAT_ID', + }, + ], + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + { + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ACXIOM_ID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ORACLE_MOAT_ID', + idValue: 'test@rudderlabs.com', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-4', + name: 'linkedin_ads', + description: 'Track call : event not containing any of the allowed user identifiers', + scenario: 'Business', + successCriteria: + 'Error will be thrown as the event does not contain any of the allowed user identifiers', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'ABC Searched', + properties: commonUserProperties, + context: { + traits: { + firstName: 'John', + }, + }, + timestamp: commonTimestamp, + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '[LinkedIn Conversion API] no matching user id found. Please provide at least one of the following: email, linkedinFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId: Workflow: procWorkflow, Step: commonFields, ChildStep: undefined, OriginalError: [LinkedIn Conversion API] no matching user id found. Please provide at least one of the following: email, linkedinFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId', + metadata: generateMetadata(1), + statTags: commonStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-5', + name: 'linkedin_ads', + description: 'Track call : track event containing product array', + scenario: 'Business', + successCriteria: + 'the amount will be summation of product * quantity for all the products in the array', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonPropertiesWithProducts, + externalId: [ + { + id: 'test@rudderlabs.com', + type: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + }, + { + id: 'test@rudderlabs.com', + type: 'ACXIOM_ID', + }, + { + id: 'test@rudderlabs.com', + type: 'ORACLE_MOAT_ID', + }, + ], + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '800', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + { + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ACXIOM_ID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ORACLE_MOAT_ID', + idValue: 'test@rudderlabs.com', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-6', + name: 'linkedin_ads', + description: 'Track call : track event containing first name and last name in traits', + scenario: 'Business', + successCriteria: + 'output event will contain userInfo object only because first name and last name are present in traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonUserProperties, + context: { + traits: { ...commonUserTraits, firstName: 'John', lastName: 'Doe' }, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '400', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'John', + lastName: 'Doe', + }, + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-track-test-7', + name: 'linkedin_ads', + description: + 'Track call : track event containing product array where not all products contains price field', + scenario: 'Business', + successCriteria: + 'the amount will be summation of product * quantity for all the products in the array', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: commonPropertiesWithProductsPriceNotPresentInAll, + externalId: [ + { + id: 'test@rudderlabs.com', + type: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + }, + { + id: 'test@rudderlabs.com', + type: 'ACXIOM_ID', + }, + { + id: 'test@rudderlabs.com', + type: 'ORACLE_MOAT_ID', + }, + ], + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: commonDestination, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + version: '1', + type: 'REST', + method: 'POST', + endpoint: `https://api.linkedin.com/rest/conversionEvents`, + headers: commonHeader, + params: {}, + FORM: {}, + files: {}, + JSON: { + elements: [ + { + conversion: 'urn:lla:llaPartnerConversion:23456', + conversionHappenedAt: 1697241600000, + conversionValue: { + amount: '600', + currencyCode: 'USD', + }, + eventId: '12345', + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + { + idType: 'LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ACXIOM_ID', + idValue: 'test@rudderlabs.com', + }, + { + idType: 'ORACLE_MOAT_ID', + idValue: 'test@rudderlabs.com', + }, + ], + }, + }, + ], + }, + userId: '', + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/processor/validationTestData.ts b/test/integrations/destinations/linkedin_ads/processor/validationTestData.ts new file mode 100644 index 0000000000..4579cf68ee --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/processor/validationTestData.ts @@ -0,0 +1,323 @@ +import { generateMetadata, generateTrackPayload, overrideDestination } from '../../../testUtils'; +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; + +const commonDestination: Destination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: { + hashData: true, + conversionMapping: [ + { + from: 'ABC Searched', + to: '1234567', + }, + { + from: 'spin_result', + to: '23456', + }, + { + from: 'ABC Searched', + to: '34567', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], + }, + Enabled: true, +}; + +const commonUserTraits = { + email: 'abc@gmail.com', + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event_id: '12345', +}; + +const commonUserProperties = { + additional_bet_index: 0, + eventId: '12345', +}; + +const commonUserPropertiesWithProductWithoutPrice = { + additional_bet_index: 0, + eventId: '12345', + products: [ + { + productId: '12345', + }, + { + productId: '123456', + }, + ], +}; + +const commonStats = { + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + implementation: 'cdkV2', + destType: 'LINKEDIN_ADS', + module: 'destination', + feature: 'processor', + workspaceId: 'default-workspaceId', +}; + +const commonTimestamp = new Date('2023-10-14'); +const olderTimestamp = new Date('2023-07-13'); + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'linkedin_ads-validation-test-1', + name: 'linkedin_ads', + description: 'Track call : event is older than 90 days', + scenario: 'Business', + successCriteria: 'shoud throw error with status code 400 and error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'spin_result', + properties: { ...commonUserProperties, price: 400 }, + context: { + traits: commonUserTraits, + }, + timestamp: olderTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { hashData: false }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Events must be sent within ninety days of their occurrence.: Workflow: procWorkflow, Step: commonFields, ChildStep: undefined, OriginalError: Events must be sent within ninety days of their occurrence.', + metadata: generateMetadata(1), + statTags: { + destinationId: 'default-destinationId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + implementation: 'cdkV2', + destType: 'LINKEDIN_ADS', + module: 'destination', + feature: 'processor', + workspaceId: 'default-workspaceId', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-validation-test-2', + name: 'linkedin_ads', + description: 'Track call : event not mapped to conversion rule in UI', + scenario: 'Business', + successCriteria: + 'should throw error with status code 400 and error message no matching conversion rule found for random event. Please provide a conversion rule. Aborting', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'random event', + properties: { ...commonUserProperties, price: 400 }, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + deduplicationKey: `properties.eventId`, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '[LinkedIn Conversion API] no matching conversion rule found for random event. Please provide a conversion rule. Aborting: Workflow: procWorkflow, Step: deduceConversionEventRules, ChildStep: undefined, OriginalError: [LinkedIn Conversion API] no matching conversion rule found for random event. Please provide a conversion rule. Aborting', + metadata: generateMetadata(1), + statTags: { ...commonStats, errorType: 'configuration' }, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-validation-test-3', + name: 'linkedin_ads', + description: '[Error]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by linkedin_ads destination and the error message should be Event type random is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: commonDestination, + metadata: generateMetadata(1), + message: { + userId: 'user123', + type: 'random', + groupId: 'XUepkK', + traits: { + subscribe: true, + }, + context: { + traits: { + email: 'test@rudderstack.com', + phone: '+12 345 678 900', + consent: 'email', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type random is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type random is not supported', + metadata: generateMetadata(1), + statTags: commonStats, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-validation-test-4', + name: 'linkedin_ads', + description: 'Track call : properties without product array and no price', + scenario: 'Business', + successCriteria: + 'should throw error with status code 400 and error message regarding price is a mandatory field for linkedin conversions', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'random event', + properties: commonUserProperties, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + deduplicationKey: `properties.eventId`, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '[LinkedIn Conversion API]: Cannot map price for event random event. Aborting: Workflow: procWorkflow, Step: commonFields, ChildStep: undefined, OriginalError: [LinkedIn Conversion API]: Cannot map price for event random event. Aborting', + metadata: generateMetadata(1), + statTags: commonStats, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'linkedin_ads-validation-test-5', + name: 'linkedin_ads', + description: 'Track call : properties with product array and no price', + scenario: 'Business', + successCriteria: + 'should throw error with status code 400 and error message regarding price is a mandatory field for linkedin conversions', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: generateTrackPayload({ + event: 'random event', + properties: commonUserPropertiesWithProductWithoutPrice, + context: { + traits: commonUserTraits, + }, + timestamp: commonTimestamp, + messageId: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + }), + metadata: generateMetadata(1), + destination: overrideDestination(commonDestination, { + deduplicationKey: `properties.eventId`, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '[LinkedIn Conversion API]: Cannot map price for event random event. Aborting: Workflow: procWorkflow, Step: commonFields, ChildStep: undefined, OriginalError: [LinkedIn Conversion API]: Cannot map price for event random event. Aborting', + metadata: generateMetadata(1), + statTags: commonStats, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/linkedin_ads/router/data.ts b/test/integrations/destinations/linkedin_ads/router/data.ts new file mode 100644 index 0000000000..cf7defe6af --- /dev/null +++ b/test/integrations/destinations/linkedin_ads/router/data.ts @@ -0,0 +1,462 @@ +export const mockFns = (_) => { + // @ts-ignore + jest.useFakeTimers().setSystemTime(new Date('2023-10-15')); +}; + +const config = { + hashData: true, + deduplicationKey: 'properties.eventId', + conversionMapping: [ + { + from: 'ABC Searched', + to: '1234567', + }, + { + from: 'spin_result', + to: '23456', + }, + { + from: 'ABC Searched', + to: '34567', + }, + ], + oneTrustCookieCategories: [ + { + oneTrustCookieCategory: 'Marketing', + }, + ], +}; + +const commonDestination = { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: config, + Enabled: true, +}; + +export const data = [ + { + id: 'linkedin_ads-track-test-1', + name: 'linkedin_ads', + description: 'Track call : custom event calls with simple user properties and traits', + scenario: 'Business', + successCriteria: + 'event not respecting the internal mapping and as well as UI mapping should be considered as a custom event and should be sent as it is', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'track', + event: 'ABC Searched', + sentAt: '2020-08-14T05: 30: 30.118Z', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + email: 'abc@gmail.com', + phone: '+1234589947', + gender: 'non-binary', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2024-02-10T12:16:07.251Z', + properties: { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'USD', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + requestIP: '123.0.0.0', + optOutType: 'LDP', + clickId: 'dummy_clickId', + + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 1, + secret: { + accessToken: 'dummyToken', + }, + }, + destination: commonDestination, + }, + { + message: { + type: 'track', + event: 'ABC Searched', + sentAt: '2020-08-14T05: 30: 30.118Z', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + email: 'abc@gmail.com', + phone: '+1234589947', + gender: 'non-binary', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2024-02-10T12:16:07.251Z', + properties: { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'USD', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + requestIP: '123.0.0.0', + optOutType: 'LDP', + clickId: 'dummy_clickId', + + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 2, + secret: { + accessToken: 'dummyToken', + }, + }, + destination: commonDestination, + }, + { + message: { + type: 'track', + event: 'spin_result', + sentAt: '2020-08-14T05: 30: 30.118Z', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + phone: '+1234589947', + gender: 'non-binary', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2024-02-10T12:16:07.251Z', + properties: { + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'USD', + discount: 2.5, + order_id: '50314b8e9bcf000000000000', + requestIP: '123.0.0.0', + optOutType: 'LDP', + clickId: 'dummy_clickId', + + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 3, + secret: { + accessToken: 'dummyToken', + }, + }, + destination: commonDestination, + }, + ], + destType: 'linkedin_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 3, + secret: { + accessToken: 'dummyToken', + }, + }, + ], + destination: { + ID: '12335', + Name: 'sample-destination', + DestinationDefinition: { + ID: '123', + Name: 'linkedin_ads', + DisplayName: 'LinkedIn Ads', + Config: { + cdkV2Enabled: true, + }, + }, + WorkspaceID: '123', + Transformations: [], + Config: config, + Enabled: true, + }, + batched: false, + statusCode: 400, + error: + '[LinkedIn Conversion API] no matching user id found. Please provide at least one of the following: email, linkedinFirstPartyAdsTrackingUUID, acxiomId, oracleMoatId', + statTags: { + destType: 'LINKEDIN_ADS', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'router', + implementation: 'cdkV2', + module: 'destination', + }, + }, + { + batchedRequest: { + body: { + JSON: { + elements: [ + { + conversionHappenedAt: 1707567367251, + eventId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + conversionValue: { + currencyCode: 'USD', + amount: '50', + }, + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'Test', + lastName: 'Rudderlabs', + }, + }, + conversion: 'urn:lla:llaPartnerConversion:1234567', + }, + { + conversionHappenedAt: 1707567367251, + eventId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + conversionValue: { + currencyCode: 'USD', + amount: '50', + }, + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'Test', + lastName: 'Rudderlabs', + }, + }, + conversion: 'urn:lla:llaPartnerConversion:34567', + }, + { + conversionHappenedAt: 1707567367251, + eventId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + conversionValue: { + currencyCode: 'USD', + amount: '50', + }, + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'Test', + lastName: 'Rudderlabs', + }, + }, + conversion: 'urn:lla:llaPartnerConversion:1234567', + }, + { + conversionHappenedAt: 1707567367251, + eventId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + conversionValue: { + currencyCode: 'USD', + amount: '50', + }, + user: { + userIds: [ + { + idType: 'SHA256_EMAIL', + idValue: + '48ddb93f0b30c475423fe177832912c5bcdce3cc72872f8051627967ef278e08', + }, + ], + userInfo: { + firstName: 'Test', + lastName: 'Rudderlabs', + }, + }, + conversion: 'urn:lla:llaPartnerConversion:34567', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.linkedin.com/rest/conversionEvents', + headers: { + 'Content-Type': 'application/json', + 'X-RestLi-Method': 'BATCH_CREATE', + 'X-Restli-Protocol-Version': '2.0.0', + 'LinkedIn-Version': '202402', + Authorization: 'Bearer dummyToken', + }, + params: {}, + files: {}, + }, + metadata: [ + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 1, + secret: { + accessToken: 'dummyToken', + }, + }, + { + sourceType: '', + destinationType: '', + namespace: '', + jobId: 2, + secret: { + accessToken: 'dummyToken', + }, + }, + ], + batched: true, + statusCode: 200, + destination: commonDestination, + }, + ], + }, + }, + }, + }, +].map((d) => ({ ...d, mockFns })); diff --git a/test/integrations/destinations/mailjet/processor/data.ts b/test/integrations/destinations/mailjet/processor/data.ts index 71e06dc14e..03c3232e72 100644 --- a/test/integrations/destinations/mailjet/processor/data.ts +++ b/test/integrations/destinations/mailjet/processor/data.ts @@ -141,7 +141,7 @@ export const data = [ status: 200, body: [ { - error: 'Missing required value from "email"', + error: 'Missing required value from "emailOnly"', statTags: { destType: 'MAILJET', errorCategory: 'dataValidation', diff --git a/test/integrations/destinations/marketo/dataDelivery/business.ts b/test/integrations/destinations/marketo/dataDelivery/business.ts new file mode 100644 index 0000000000..ca4e05afa9 --- /dev/null +++ b/test/integrations/destinations/marketo/dataDelivery/business.ts @@ -0,0 +1,352 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + retryable: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + throttled: { + destType: 'MARKETO', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'throttled', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +export const reqMetadataArray = [proxyMetdata]; +const params = { + destination: 'marketo', +}; + +const commonRequestParameters = { + JSON: { + action: 'createOrUpdate', + input: [ + { + City: 'Tokyo', + Country: 'JP', + Email: 'gabi29@gmail.com', + PostalCode: '100-0001', + Title: 'Owner', + id: 1328328, + userId: 'gabi_userId_45', + }, + ], + lookupField: 'id', + }, + params, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'marketo_v1_scenario_1', + name: 'marketo', + description: '[Proxy v1 API] :: Test for a successful update request', + successCriteria: 'Should return a 200 status code with status updated and record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_1', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"664#17dae8c3d48","result":[{"id":1328328,"status":"updated"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_2', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Access token invalid scenario', + successCriteria: 'Should return a 500 status code with message Access token invalid', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_2', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: + 'Request Failed for marketo, Access token invalid (Retryable).during Marketo Response Handling', + response: [ + { + error: + '{"requestId":"a61c#17daea5968a","success":false,"errors":[{"code":"601","message":"Access token invalid"}]}', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_3', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Requested resource not found scenario', + successCriteria: 'Should return a 400 status code with message Requested resource not found', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_3', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + 'Request Failed for marketo, Requested resource not found (Aborted).during Marketo Response Handling', + response: [ + { + error: + '{"requestId":"a61c#17daea5968a","success":false,"errors":[{"code":"610","message":"Requested resource not found"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_4', + name: 'marketo', + description: '[Proxy v1 API] :: Test for Unknown error with empty response', + successCriteria: 'Should return a 500 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_4', + 'Content-Type': 'application/json', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: 'Request failed with status: 500', + response: [ + { + error: '""', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_5', + name: 'marketo', + description: '[Proxy v1 API] :: Test for missing content type header scenario', + successCriteria: 'Should return a 612 status code with Invalid Content Type ', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'invalid', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test_invalid_header', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: + 'Request Failed for marketo, Invalid Content Type (Aborted).during Marketo Response Handling', + response: [ + { + error: + '{"success":false,"errors":[{"code":"612","message":"Invalid Content Type"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_6', + name: 'marketo', + description: '[Proxy v1 API] :: Test for a passed field exceeding max length', + successCriteria: 'Should return a 1077 status code with Value for field exceeds max length', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test_exceeded_length', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: 'Request failed with status: 400', + response: [ + { + error: + '{"success":false,"errors":[{"code":"1077","message":"Value for field exceeds max length"}]}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo/dataDelivery/data.ts b/test/integrations/destinations/marketo/dataDelivery/data.ts index 47dd8e9236..db379c9e95 100644 --- a/test/integrations/destinations/marketo/dataDelivery/data.ts +++ b/test/integrations/destinations/marketo/dataDelivery/data.ts @@ -1,4 +1,7 @@ -export const data = [ +import { testScenariosForV1API } from './business'; +import { otheMarketoScenariosV1 } from './other'; + +const legacyTests = [ { name: 'marketo', description: 'Test 0', @@ -488,3 +491,5 @@ export const data = [ }, }, ]; + +export const data = [...legacyTests, ...testScenariosForV1API, ...otheMarketoScenariosV1]; diff --git a/test/integrations/destinations/marketo/dataDelivery/other.ts b/test/integrations/destinations/marketo/dataDelivery/other.ts new file mode 100644 index 0000000000..5d4e3b1f17 --- /dev/null +++ b/test/integrations/destinations/marketo/dataDelivery/other.ts @@ -0,0 +1,266 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + aborted: { + destType: 'MARKETO', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + retryable: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'MARKETO', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, +}; + +const metadata = { + jobId: 1, + secret: { + accessToken: 'default-accessToken', + }, + attemptNum: 1, + userId: 'default-userId', + sourceId: 'default-sourceId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + dontBatch: false, +}; + +export const otheMarketoScenariosV1: ProxyV1TestData[] = [ + { + id: 'marketo_v1_other_scenario_1', + name: 'marketo', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 503', + status: 503, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_2', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_3', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 504', + status: 504, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_4', + name: 'marketo', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with empty error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_other_scenario_5', + name: 'marketo', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with empty error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata, + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_v1_scenario_6', + name: 'marketo', + description: '[Proxy v1 API] :: Test for DNS lookup failed scenario', + successCriteria: 'Should return a 400 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/dns_lookup_failure', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + statTags: statTags.aborted, + message: 'Request failed with status: 400', + response: [ + { + error: '{}', + metadata, + statusCode: 400, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo/network.ts b/test/integrations/destinations/marketo/network.ts index 9c28a9aef1..1606e78c51 100644 --- a/test/integrations/destinations/marketo/network.ts +++ b/test/integrations/destinations/marketo/network.ts @@ -1,20 +1,26 @@ -export const networkCallsData = [ +const userObject = { + City: 'Tokyo', + Country: 'JP', + Email: 'gabi29@gmail.com', + PostalCode: '100-0001', + Title: 'Owner', + id: 1328328, + userId: 'gabi_userId_45', +}; + +const headerObject = { + Authorization: 'Bearer test_token_2', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', +}; + +const tfProxyMocksData = [ { httpReq: { url: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -44,24 +50,10 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, - headers: { - Authorization: 'Bearer test_token_2', - 'Content-Type': 'application/json', - 'User-Agent': 'RudderLabs', - }, + headers: headerObject, method: 'POST', }, httpRes: { @@ -84,17 +76,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -124,17 +106,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -151,17 +123,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test5', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -178,17 +140,7 @@ export const networkCallsData = [ url: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', data: { action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], + input: [userObject], lookupField: 'id', }, headers: { @@ -970,3 +922,1007 @@ export const networkCallsData = [ }, }, ]; + +const businessMockData = [ + { + description: 'Mock response for a successful update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test1', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_1', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: '664#17dae8c3d48', + result: [ + { + id: 1328328, + status: 'updated', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed update request due to invalid access token', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test2', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: headerObject, + method: 'POST', + }, + httpRes: { + data: { + requestId: 'a61c#17daea5968a', + success: false, + errors: [ + { + code: '601', + message: 'Access token invalid', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed update request due to requested resource not found', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test3', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_3', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: 'a61c#17daea5968a', + success: false, + errors: [ + { + code: '610', + message: 'Requested resource not found', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful create/update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test4', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_4', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: {}, + }, + { + description: 'Mock response for a successful create/update request', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test5', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_5', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: '', + }, + { + description: 'Mock response for a failed create/update request due to DNS lookup failure', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + code: '[ENOTFOUND] :: DNS lookup failed', + status: 400, + }, + }, + { + description: + 'Mock response for a failed create/update request due to unhandled exception in proxy request', + httpReq: { + url: 'https://unhandled_exception_in_proxy_req.mktorest.com/rest/v1/leads.json', + data: { + action: 'createOrUpdate', + input: [userObject], + lookupField: 'id', + }, + headers: { + Authorization: 'Bearer access_token_success', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: 'random_marketo_code', + message: 'problem', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful access token request', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to expired token', + httpReq: { + url: 'https://marketo_acct_id_token_failure.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_expired', + expires_in: 0, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json', + method: 'get', + }, + httpRes: { + data: { + requestId: '7ab2#17672a46a99', + result: [ + { + id: 4, + status: 'created', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request with filterType=email with no results', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=arnab.compsc%40gmail.com', + method: 'GET', + }, + httpRes: { + data: { + requestId: '107#17672aeadba', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request with filterType=userId with results', + httpReq: { + url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/leads.json?filterType=userId&filterValues=test-user-6j55yr', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [ + { + createdAt: '2020-12-17T21:39:07Z', + email: null, + firstName: null, + id: 4, + lastName: null, + updatedAt: '2020-12-17T21:39:07Z', + userId: 'test-user-6j55yr', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to expired token', + httpReq: { + url: 'https://marketo_acct_id_failed.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '601', + message: 'Access Token Expired', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful access token request', + httpReq: { + url: 'https://munchkinId.mktorest.com/identity/oauth/token?client_id=b&client_secret=clientSecret&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_acess', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed access token request due to invalid client id', + httpReq: { + url: 'https://munchkinId.mktorest.com/identity/oauth/token?client_id=wrongClientId&client_secret=clientSecret&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://munchkinId.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with queued status', + httpReq: { + url: 'https://munchkinId.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '5bdd#17fd1ff88cd', + result: [ + { + batchId: 2977, + importId: '2977', + status: 'Queued', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for bulk request', + httpReq: { + url: 'https://a.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for bulk request for throttling error', + httpReq: { + url: 'https://a.mktorest.com/identity/oauth/token?client_id=b&client_secret=forThrottle&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://a.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful oauth token request', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 400 error', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/bulk/v1/leads/batch/1234.json', + method: 'GET', + }, + httpRes: { + data: { + errors: [ + { + message: 'Any 400 error', + code: 1000, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful oauth token request', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful oauth token request', + httpReq: { + url: 'https://testMunchkin500.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'test_access_token', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 500 error', + httpReq: { + url: 'https://testMunchkin500.mktorest.com/bulk/v1/leads/batch/1234.json', + method: 'GET', + }, + httpRes: { + data: { + errors: [ + { + message: 'Any 500 error', + code: 502, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with warnings', + httpReq: { + url: 'https://a.mktorest.com/bulk/v1/leads/batch/12345/warnings.json', + method: 'GET', + }, + httpRes: { + data: 'data \n data', + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bulk request with failures', + httpReq: { + url: 'https://a.mktorest.com/bulk/v1/leads/batch/12345/failures.json', + method: 'GET', + }, + httpRes: { + data: 'data \n data', + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed bulk request with 603 error code', + httpReq: { + url: 'https://testMunchkin1.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: 603, + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/identity/oauth/token?client_id=b&client_secret=c&grant_type=client_credentials', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful get request to get list of fields', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with pending import error', + httpReq: { + url: 'https://testMunchkin2.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'There are 10 imports currently being processed. Please try again later', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful leads query request', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with empty file error', + httpReq: { + url: 'https://testMunchkin3.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'Empty file', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful leads query request', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/rest/v1/leads/describe2.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7fa1#17fd1da66fe', + result: [ + { + name: 'API Lead', + searchableFields: [['email']], + fields: [ + { + name: 'Email', + displayName: 'Email Address', + dataType: 'email', + length: 255, + updateable: true, + crmManaged: false, + }, + ], + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed leads query request with any other error', + httpReq: { + url: 'https://testMunchkin4.mktorest.com/bulk/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + success: false, + errors: [ + { + message: 'Any other error', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a succesful bearer token request', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with no results', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed get request due to missing lookup field', + httpReq: { + url: 'https://valid_account_broken_event.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: '1006', + message: "Lookup field 'userId' not found", + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with no results', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed get request due to random marketo error code', + httpReq: { + url: 'https://unhandled_status_code.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '142e4#1835b117b76', + success: false, + errors: [ + { + code: 'random_marketo_code', + message: 'some other problem', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a successful bearer token request', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/identity/oauth/token', + method: 'GET', + }, + httpRes: { + data: { + access_token: 'access_token_success', + expires_in: 3599, + scope: 'integrations@rudderstack.com', + token_type: 'bearer', + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with no filterType and filterValues specified', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/rest/v1/leads.json', + method: 'GET', + }, + httpRes: { + data: { + requestId: '7ab2#17672a46a99', + result: [ + { + id: 4, + status: 'created', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: + 'Mock response for a successful get request with filterType=email and filterValues specified with results', + httpReq: { + url: 'https://successful_identify_transformation.mktorest.com/rest/v1/leads.json?filterType=email&filterValues=0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + method: 'GET', + }, + httpRes: { + data: { + requestId: '12093#17672aeaee6', + result: [ + { + createdAt: '2022-09-17T21:39:07Z', + email: '0c7b8b80-9c43-4f8e-b2d2-5e2448a25040@j.mail', + firstName: 'random_first', + id: 4, + lastName: 'random_last', + updatedAt: '2022-09-20T21:48:07Z', + userId: 'test-user-957ue', + }, + ], + success: true, + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed lead request due to invalid header', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test_invalid_header', + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'invalid', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '612', + message: 'Invalid Content Type', + }, + ], + }, + status: 200, + statusText: 'OK', + }, + }, + { + description: 'Mock response for a failed lead request due to length exceeded', + httpReq: { + url: 'https://mktId.mktorest.com/rest/v1/leads.json/test_exceeded_length', + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + success: false, + errors: [ + { + code: '1077', + message: 'Value for field exceeds max length', + }, + ], + }, + status: 400, + statusText: 'OK', + }, + }, +]; + +export const networkCallsData = [...businessMockData, ...tfProxyMocksData]; diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts new file mode 100644 index 0000000000..08be877ba8 --- /dev/null +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/business.ts @@ -0,0 +1,255 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +export const statTags = { + aborted: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + retryable: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', + }, + throttled: { + destType: 'MARKETO_STATIC_LIST', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'throttled', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + secret: {}, + dontBatch: false, +}; + +export const reqMetadataArray = [proxyMetdata]; +const params = { + destination: 'marketo_static_list', +}; + +const commonRequestParameters = { + params, + userId: '', + body: { + FORM: {}, + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'msl_v1_scenario_1', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for a partial successful request with multiple ids', + successCriteria: 'Should return a 200 status code with respective status for each record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer Incorrect_token', + 'Content-Type': 'application/json', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=110&id=111&id=112', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"b6d1#18a8d2c10e7","result":[{"id":110,"status":"skipped","reasons":[{"code":"1015","message":"Lead not in list"}]},{"id":111,"status":"removed"},{"id":112,"status":"removed"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_2', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for Access token invalid scenario', + successCriteria: 'Should return a 500 status code with message Access token invalid', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer Incorrect_token', + 'Content-Type': 'application/json', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2&id=3', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: + 'Request Failed for Marketo Static List, Access token invalid (Retryable).during Marketo Static List Response Handling', + response: [ + { + error: + '{"requestId":"68d8#1846058ee27","success":false,"errors":[{"code":"601","message":"Access token invalid"}]}', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_3', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for a complete successful request with multiple ids', + successCriteria: + 'Should return a 200 status code with respective added status for each record id', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer token', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: + 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + error: + '{"requestId":"12d3c#1846057dce2","result":[{"id":1,"status":"added"},{"id":2,"status":"added"}],"success":true}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'msl_v1_scenario_4', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Test for DNS lookup failed scenario', + successCriteria: 'Should return a 400 status code with empty response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + headers: { + Authorization: 'Bearer test_token_6', + 'Content-Type': 'application/json', + 'User-Agent': 'RudderLabs', + }, + endpoint: 'https://mktId.mktorest.com/rest/v1/leads.json/test6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + statTags: statTags.retryable, + message: 'Request failed with status: 500', + response: [ + { + error: '""', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts index f0275e329e..c0398f6d2b 100644 --- a/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/data.ts @@ -1,4 +1,7 @@ -export const data = [ +import { testScenariosForV1API } from './business'; +import { otherScenariosV1 } from './other'; + +const legacyTests = [ { name: 'marketo_static_list', description: 'Test 0', @@ -158,21 +161,7 @@ export const data = [ }, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -234,30 +223,7 @@ export const data = [ params: {}, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -311,30 +277,7 @@ export const data = [ params: {}, body: { FORM: {}, - JSON: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, + JSON: {}, JSON_ARRAY: {}, XML: {}, }, @@ -373,3 +316,5 @@ export const data = [ }, }, ]; + +export const data = [...legacyTests, ...testScenariosForV1API, ...otherScenariosV1]; diff --git a/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts b/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts new file mode 100644 index 0000000000..b1f3403fa6 --- /dev/null +++ b/test/integrations/destinations/marketo_static_list/dataDelivery/other.ts @@ -0,0 +1,194 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { reqMetadataArray, statTags } from './business'; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'marketo_static_list_v1_other_scenario_1', + name: 'marketo_static_list', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 503', + status: 503, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_2', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_3', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 504', + status: 504, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_4', + name: 'marketo_static_list', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, + { + id: 'marketo_static_list_v1_other_scenario_5', + name: 'marketo_static_list', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: statTags.retryable, + message: 'Request failed with status: 500', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/marketo_static_list/network.ts b/test/integrations/destinations/marketo_static_list/network.ts index 5c13273859..f165291c15 100644 --- a/test/integrations/destinations/marketo_static_list/network.ts +++ b/test/integrations/destinations/marketo_static_list/network.ts @@ -46,21 +46,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=1&id=2', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - ], - lookupField: 'id', - }, params: { destination: 'marketo_static_list' }, headers: { Authorization: 'Bearer token', @@ -84,30 +69,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=3&id=4', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, params: {}, headers: { Authorization: 'Bearer token', @@ -131,30 +92,6 @@ const deliveryCallsData = [ { httpReq: { url: 'https://marketo_acct_id_success.mktorest.com/rest/v1/lists/1234/leads.json?id=5&id=6', - data: { - action: 'createOrUpdate', - input: [ - { - City: 'Tokyo', - Country: 'JP', - Email: 'gabi29@gmail.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328328, - userId: 'gabi_userId_45', - }, - { - City: 'Tokyo', - Country: 'JP', - Email: 'b@s.com', - PostalCode: '100-0001', - Title: 'Owner', - id: 1328329, - userId: 'ben_userId_45', - }, - ], - lookupField: 'id', - }, params: {}, headers: { Authorization: 'Bearer token', diff --git a/test/integrations/destinations/monday/dataDelivery/data.ts b/test/integrations/destinations/monday/dataDelivery/data.ts new file mode 100644 index 0000000000..16d2047095 --- /dev/null +++ b/test/integrations/destinations/monday/dataDelivery/data.ts @@ -0,0 +1,189 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'authToken', +}; + +const commonRequestParameters = { + headers: commonHeaders, +}; + +export const data: ProxyV1TestData[] = [ + { + id: 'monday_v1_scenario_1', + name: 'monday', + description: 'Sucess reponse from monday', + feature: 'dataDelivery', + successCriteria: 'Should return 200 with no error with destination response', + module: 'destination', + scenario: 'Business', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: 'https://api.monday.com/v2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[MONDAY Response V1 Handler] - Request Processed Successfully', + response: [ + { + error: 'success', + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + }, + }, + { + id: 'monday_v1_scenario_2', + name: 'monday', + description: 'Error response with 200 status', + feature: 'dataDelivery', + successCriteria: 'Should return 200 with no error with destination response', + module: 'destination', + scenario: 'Business', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + headers: { + Authorization: 'errorAuth', + }, + endpoint: 'https://api.monday.com/v2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: '[MONDAY Response V1 Handler] - Request Processed Successfully', + response: [ + { + error: "Field 'region' doesn't exist on type 'User'", + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + }, + }, + { + id: 'monday_v1_scenario_3', + name: 'monday', + description: 'Rate limit exceeded request', + feature: 'dataDelivery', + successCriteria: 'Should return throlled with correct status code', + module: 'destination', + scenario: 'Business', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + headers: { + Authorization: 'rateLimitAuthToken', + }, + endpoint: 'https://api.monday.com/v2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '{"error_message":"Rate Limit Exceeded.","status_code":429}', + statusCode: 429, + metadata: generateMetadata(1), + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'throttled', + destType: 'MONDAY', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: + 'MONDAY: Error encountered in transformer proxy V1 with error: Rate Limit Exceeded.', + status: 429, + }, + }, + }, + }, + }, + { + id: 'monday_v1_scenario_4', + name: 'monday', + description: 'Invalid request with bad query data', + feature: 'dataDelivery', + successCriteria: 'Should return 400 with error message', + module: 'destination', + scenario: 'Business', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + headers: { + Authorization: 'internalServerAuthToken', + }, + endpoint: 'https://api.monday.com/v2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '{"error_message":"Internal server error","status_code":500}', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: { + errorCategory: 'network', + errorType: 'retryable', + destType: 'MONDAY', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + }, + message: + 'MONDAY: Error encountered in transformer proxy V1 with error: Internal server error', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/monday/network.ts b/test/integrations/destinations/monday/network.ts index f23b9061f8..f91952069a 100644 --- a/test/integrations/destinations/monday/network.ts +++ b/test/integrations/destinations/monday/network.ts @@ -1,4 +1,287 @@ export const networkCallsData = [ + { + httpReq: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: { + Authorization: 'authToken', + }, + }, + httpRes: { + data: { + data: { + boards: [ + { + name: 'Planning', + columns: [ + { + id: 'name', + title: 'Name', + type: 'name', + description: null, + settings_str: '{}', + }, + { + id: 'subitems', + title: 'Subitems', + type: 'subtasks', + description: null, + settings_str: + '{"allowMultipleItems":true,"itemTypeName":"column.subtasks.title","displayType":"BOARD_INLINE","boardIds":[3160974974]}', + }, + { + id: 'person', + title: 'Person', + type: 'multiple-person', + description: null, + settings_str: '{}', + }, + { + id: 'status', + title: 'Status', + type: 'color', + description: null, + settings_str: + '{"labels":{"0":"Working on it","1":"Done","2":"Stuck"},"labels_positions_v2":{"0":0,"1":2,"2":1,"5":3},"labels_colors":{"0":{"color":"#fdab3d","border":"#E99729","var_name":"orange"},"1":{"color":"#00c875","border":"#00B461","var_name":"green-shadow"},"2":{"color":"#e2445c","border":"#CE3048","var_name":"red-shadow"}}}', + }, + { + id: 'date4', + title: 'Date', + type: 'date', + description: null, + settings_str: '{}', + }, + { + id: 'checkbox', + title: 'Checkbox', + type: 'boolean', + description: null, + settings_str: '{}', + }, + { + id: 'connect_boards', + title: 'Connect boards', + type: 'board-relation', + description: null, + settings_str: '{"allowCreateReflectionColumn":false}', + }, + { + id: 'status_1', + title: 'Other', + type: 'color', + description: null, + settings_str: + '{"labels":{"0":"Working on it","1":"Done","2":"Stuck"},"labels_colors":{"0":{"color":"#fdab3d","border":"#E99729","var_name":"orange"},"1":{"color":"#00c875","border":"#00B461","var_name":"green-shadow"},"2":{"color":"#e2445c","border":"#CE3048","var_name":"red-shadow"}}}', + }, + { + id: 'date_1', + title: 'Date 1', + type: 'date', + description: null, + settings_str: '{"hide_footer":false}', + }, + { + id: 'status_12', + title: 'new status', + type: 'color', + description: null, + settings_str: + '{"labels":{"0":"Working on it","1":"Done","2":"Stuck"},"labels_colors":{"0":{"color":"#fdab3d","border":"#E99729","var_name":"orange"},"1":{"color":"#00c875","border":"#00B461","var_name":"green-shadow"},"2":{"color":"#e2445c","border":"#CE3048","var_name":"red-shadow"}}}', + }, + { + id: 'numbers', + title: 'Numbers', + type: 'numeric', + description: null, + settings_str: '{}', + }, + { + id: 'text', + title: 'Name', + type: 'text', + description: null, + settings_str: '{}', + }, + { + id: 'country', + title: 'Country', + type: 'country', + description: null, + settings_str: '{}', + }, + { + id: 'dropdown', + title: 'Dropdown', + type: 'dropdown', + description: null, + settings_str: + '{"hide_footer":false,"labels":[{"id":1,"name":"dropdown"},{"id":2,"name":"dropup"}]}', + }, + { + id: 'email', + title: 'Email', + type: 'email', + description: null, + settings_str: '{}', + }, + { + id: 'location', + title: 'Location', + type: 'location', + description: null, + settings_str: '{}', + }, + { + id: 'phone', + title: 'Phone', + type: 'phone', + description: null, + settings_str: '{}', + }, + { + id: 'rating', + title: 'Rating', + type: 'rating', + description: null, + settings_str: '{}', + }, + { + id: 'timeline', + title: 'Timeline', + type: 'timerange', + description: null, + settings_str: '{"hide_footer":false}', + }, + { + id: 'dependent_on', + title: 'Dependent On', + type: 'dependency', + description: + 'Choose the item your task will be dependent on. If the “dependent on” item’s date is changing, the other dates will adjust automatically', + settings_str: + '{"boardIds":[3142482015],"dependencyNewInfra":true,"allowMultipleItems":true}', + }, + { + id: 'long_text', + title: 'Long Text', + type: 'long-text', + description: null, + settings_str: '{}', + }, + { + id: 'link', + title: 'Link', + type: 'link', + description: null, + settings_str: '{}', + }, + { + id: 'tags', + title: 'Tags', + type: 'tag', + description: null, + settings_str: '{"hide_footer":false}', + }, + { + id: 'label', + title: 'Label', + type: 'color', + description: '', + settings_str: + '{"done_colors":[1],"labels":{"3":"Label 2","105":"Label 1","156":"Label 3"},"labels_positions_v2":{"3":1,"5":3,"105":0,"156":2},"labels_colors":{"3":{"color":"#0086c0","border":"#3DB0DF","var_name":"blue-links"},"105":{"color":"#9AADBD","border":"#9AADBD","var_name":"winter"},"156":{"color":"#9D99B9","border":"#9D99B9","var_name":"purple_gray"}}}', + }, + { + id: 'world_clock', + title: 'World Clock', + type: 'timezone', + description: null, + settings_str: '{}', + }, + { + id: 'week', + title: 'Week', + type: 'week', + description: null, + settings_str: '{}', + }, + ], + groups: [ + { + id: 'topics', + title: 'This month', + }, + { + id: 'group_title', + title: 'Next month', + }, + ], + }, + ], + }, + account_id: 13215538, + }, + status: 200, + }, + }, + { + httpReq: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: { + Authorization: 'errorAuth', + }, + }, + httpRes: { + data: { + errors: [ + { + message: "Field 'region' doesn't exist on type 'User'", + locations: [ + { + line: 322, + column: 5, + }, + ], + fields: ['query', 'me', 'region'], + }, + ], + account_id: 123456789, + }, + status: 200, + }, + }, + { + httpReq: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: { + Authorization: 'rateLimitAuthToken', + }, + }, + httpRes: { + data: { + error_message: 'Rate Limit Exceeded.', + status_code: 429, + }, + status: 200, + }, + }, + { + httpReq: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: { + Authorization: 'internalServerAuthToken', + }, + }, + httpRes: { + data: { + error_message: 'Internal server error', + status_code: 500, + }, + status: 500, + }, + }, { httpReq: { url: 'https://api.monday.com/v2', diff --git a/test/integrations/destinations/monday/processor/data.ts b/test/integrations/destinations/monday/processor/data.ts index 4e5280efcb..082ff822fd 100644 --- a/test/integrations/destinations/monday/processor/data.ts +++ b/test/integrations/destinations/monday/processor/data.ts @@ -74,7 +74,7 @@ export const data = [ FORM: {}, JSON: { query: - 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -172,7 +172,7 @@ export const data = [ FORM: {}, JSON: { query: - 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -716,7 +716,7 @@ export const data = [ body: { JSON: { query: - 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -827,7 +827,7 @@ export const data = [ body: { JSON: { query: - 'mutation { create_item (board_id: 339283933, group_id: group_title item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}", group_id: "group_title") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -1188,7 +1188,7 @@ export const data = [ body: { JSON: { query: - 'mutation { create_item (board_id: 339283933, group_id: group_title item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"},\\"checkbox\\":{\\"checked\\":true},\\"numbers\\":\\"45\\",\\"text\\":\\"texting\\",\\"country\\":{\\"countryName\\":\\"Unites States\\",\\"countryCode\\":\\"US\\"},\\"location\\":{\\"address\\":\\"New York\\",\\"lat\\":\\"51.23\\",\\"lng\\":\\"35.3\\"},\\"phone\\":{\\"phone\\":\\"2626277272\\",\\"countryShortName\\":\\"US\\"},\\"rating\\":3,\\"link\\":{\\"url\\":\\"demo.com\\",\\"text\\":\\"websiteLink\\"},\\"long_text\\":{\\"text\\":\\"property description\\"},\\"world_clock\\":{\\"timezone\\":\\"America/New_York\\"}}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"},\\"checkbox\\":{\\"checked\\":true},\\"numbers\\":\\"45\\",\\"text\\":\\"texting\\",\\"country\\":{\\"countryName\\":\\"Unites States\\",\\"countryCode\\":\\"US\\"},\\"location\\":{\\"address\\":\\"New York\\",\\"lat\\":\\"51.23\\",\\"lng\\":\\"35.3\\"},\\"phone\\":{\\"phone\\":\\"2626277272\\",\\"countryShortName\\":\\"US\\"},\\"rating\\":3,\\"link\\":{\\"url\\":\\"demo.com\\",\\"text\\":\\"websiteLink\\"},\\"long_text\\":{\\"text\\":\\"property description\\"},\\"world_clock\\":{\\"timezone\\":\\"America/New_York\\"}}", group_id: "group_title") {id}}', }, JSON_ARRAY: {}, XML: {}, diff --git a/test/integrations/destinations/monday/router/data.ts b/test/integrations/destinations/monday/router/data.ts index 3be8b129c5..abd649d805 100644 --- a/test/integrations/destinations/monday/router/data.ts +++ b/test/integrations/destinations/monday/router/data.ts @@ -113,7 +113,7 @@ export const data = [ FORM: {}, JSON: { query: - 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{}") {id}}', }, JSON_ARRAY: {}, XML: {}, @@ -159,7 +159,7 @@ export const data = [ body: { JSON: { query: - 'mutation { create_item (board_id: 339283933, group_id: group_title item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}") {id}}', + 'mutation { create_item (board_id: 339283933, item_name: "Task 1", column_values: "{\\"status\\":{\\"label\\":\\"Done\\"},\\"email\\":{\\"email\\":\\"abc@email.com\\",\\"text\\":\\"emailId\\"}}", group_id: "group_title") {id}}', }, JSON_ARRAY: {}, XML: {}, diff --git a/test/integrations/destinations/movable_ink/common.ts b/test/integrations/destinations/movable_ink/common.ts new file mode 100644 index 0000000000..29fe76852c --- /dev/null +++ b/test/integrations/destinations/movable_ink/common.ts @@ -0,0 +1,308 @@ +import { Destination } from '../../../../src/types'; + +const destType = 'movable_ink'; +const destTypeInUpperCase = 'MOVABLE_INK'; +const displayName = 'Movable Ink'; +const channel = 'web'; +const destination: Destination = { + Config: { + endpoint: 'https://collector.movableink-dmz.com/behavioral/abc123', + accessKey: 'test-access-key', + accessSecret: 'test_access_secret', + }, + DestinationDefinition: { + DisplayName: displayName, + ID: '123', + Name: destTypeInUpperCase, + Config: { cdkV2Enabled: true }, + }, + Enabled: true, + ID: '123', + Name: destTypeInUpperCase, + Transformations: [], + WorkspaceID: 'test-workspace-id', +}; + +const processorInstrumentationErrorStatTags = { + destType: destTypeInUpperCase, + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +const RouterInstrumentationErrorStatTags = { + ...processorInstrumentationErrorStatTags, + feature: 'router', +}; + +const traits = { + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + phone: '1234567890', +}; + +const headers = { + 'Content-Type': 'application/json', + Authorization: 'Basic dGVzdC1hY2Nlc3Mta2V5OnRlc3RfYWNjZXNzX3NlY3JldA==', +}; + +const commonProperties = { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + categories: [ + { url: 'https://example1', id: '1' }, + { url: 'https://example2', id: '2' }, + ], + name: 'Cones of Dunshire', + brand: 'Wyatt Games', + variant: 'expansion pack', + price: 49.99, + quantity: 5, + coupon: 'PREORDER15', + position: 1, + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.webp', +}; + +const customProperties = { + key1: 'value1', + key2: true, + key3: ['value3'], + key4: { key5: { key6: 'value6' } }, +}; + +const trackTestProperties = { + 'Product Added': { ...commonProperties, ...customProperties }, + 'Product Viewed': { ...commonProperties, ...customProperties }, + 'Order Completed': { + checkout_id: '70324a1f0eaf000000000000', + order_id: '40684e8f0eaf000000000000', + affiliation: 'Vandelay Games', + total: 52, + subtotal: 45, + revenue: 50, + shipping: 4, + tax: 3, + discount: 5, + coupon: 'NEWCUST5', + currency: 'USD', + products: [ + { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + name: 'Cones of Dunshire', + price: 40, + position: 1, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: '577c6f5d5cf86a4c7735ba03', + sku: '3309-483-2201', + name: 'Five Crowns', + price: 5, + position: 2, + category: 'Games', + }, + { + product_id: '122c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Ticket to Ride', + price: 20, + position: 3, + category: 'Games', + }, + { + product_id: '222c6f5d5cf86a4c77358033', + sku: '9472-998-0112', + name: 'Catan', + price: 30, + position: 4, + category: 'Games', + }, + { + product_id: '322c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Pandemic', + price: 25, + position: 5, + category: 'Games', + }, + { + product_id: '422c6f5d5cf86a4c77358033', + sku: '8472-998-0113', + name: 'Exploding Kittens', + price: 15, + position: 6, + category: 'Games', + }, + { + product_id: '522c6f5d5cf86a4c77358033', + sku: '8472-998-0114', + name: 'Codenames', + price: 18, + position: 7, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358034', + sku: '8472-998-0115', + name: 'Scythe', + price: 35, + position: 8, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + name: 'Cones of Dunshire', + price: 40, + position: 1, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: '577c6f5d5cf86a4c7735ba03', + sku: '3309-483-2201', + name: 'Five Crowns', + price: 5, + position: 2, + category: 'Games', + }, + { + product_id: '122c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Ticket to Ride', + price: 20, + position: 3, + category: 'Games', + }, + { + product_id: '222c6f5d5cf86a4c77358033', + sku: '9472-998-0112', + name: 'Catan', + price: 30, + position: 4, + category: 'Games', + }, + { + product_id: '322c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Pandemic', + price: 25, + position: 5, + category: 'Games', + }, + { + product_id: '422c6f5d5cf86a4c77358033', + sku: '8472-998-0113', + name: 'Exploding Kittens', + price: 15, + position: 6, + category: 'Games', + }, + { + product_id: '522c6f5d5cf86a4c77358033', + sku: '8472-998-0114', + name: 'Codenames', + price: 18, + position: 7, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358034', + sku: '8472-998-0115', + name: 'Scythe', + price: 35, + position: 8, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358033', + sku: '8472-998-0112', + name: 'Cones of Dunshire', + price: 40, + position: 1, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: '577c6f5d5cf86a4c7735ba03', + sku: '3309-483-2201', + name: 'Five Crowns', + price: 5, + position: 2, + category: 'Games', + }, + { + product_id: '122c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Ticket to Ride', + price: 20, + position: 3, + category: 'Games', + }, + { + product_id: '222c6f5d5cf86a4c77358033', + sku: '9472-998-0112', + name: 'Catan', + price: 30, + position: 4, + category: 'Games', + }, + { + product_id: '322c6f5d5cf86a4c77358033', + sku: '7472-998-0112', + name: 'Pandemic', + price: 25, + position: 5, + category: 'Games', + }, + { + product_id: '422c6f5d5cf86a4c77358033', + sku: '8472-998-0113', + name: 'Exploding Kittens', + price: 15, + position: 6, + category: 'Games', + }, + { + product_id: '522c6f5d5cf86a4c77358033', + sku: '8472-998-0114', + name: 'Codenames', + price: 18, + position: 7, + category: 'Games', + }, + { + product_id: '622c6f5d5cf86a4c77358034', + sku: '8472-998-0115', + name: 'Scythe', + price: 35, + position: 8, + category: 'Games', + }, + ], + }, + 'Products Searched': { query: 'HDMI cable', url: 'https://www.website.com/product/path' }, + 'Custom event': { ...commonProperties, key1: 'value1', key2: true }, +}; + +export { + destType, + channel, + destination, + processorInstrumentationErrorStatTags, + RouterInstrumentationErrorStatTags, + traits, + headers, + trackTestProperties, +}; diff --git a/test/integrations/destinations/movable_ink/mocks.ts b/test/integrations/destinations/movable_ink/mocks.ts new file mode 100644 index 0000000000..2468f51315 --- /dev/null +++ b/test/integrations/destinations/movable_ink/mocks.ts @@ -0,0 +1,6 @@ +import config from '../../../../src/cdk/v2/destinations/movable_ink/config'; + +export const defaultMockFns = () => { + jest.replaceProperty(config, 'MAX_REQUEST_SIZE_IN_BYTES', 5000); + jest.replaceProperty(config, 'MAX_BATCH_SIZE', 2); +}; diff --git a/test/integrations/destinations/movable_ink/processor/data.ts b/test/integrations/destinations/movable_ink/processor/data.ts new file mode 100644 index 0000000000..45453c74cd --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/data.ts @@ -0,0 +1,4 @@ +import { validation } from './validation'; +import { identify } from './identify'; +import { track } from './track'; +export const data = [...identify, ...track, ...validation]; diff --git a/test/integrations/destinations/movable_ink/processor/identify.ts b/test/integrations/destinations/movable_ink/processor/identify.ts new file mode 100644 index 0000000000..0fe806df4a --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/identify.ts @@ -0,0 +1,65 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, destination, traits, headers } from '../common'; + +export const identify: ProcessorTestData[] = [ + { + id: 'MovableInk-identify-test-1', + name: destType, + description: 'Identify call with traits and anonymousId', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: '', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'identify', + userId: traits.email, + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/processor/track.ts b/test/integrations/destinations/movable_ink/processor/track.ts new file mode 100644 index 0000000000..890de11a0c --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/track.ts @@ -0,0 +1,195 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata, transformResultBuilder } from '../../../testUtils'; +import { destType, channel, destination, headers, trackTestProperties } from '../common'; + +export const track: ProcessorTestData[] = [ + { + id: 'MovableInk-track-test-1', + name: destType, + description: 'Track call: Product Added event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Product Added', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Product Added', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'MovableInk-track-test-2', + name: destType, + description: 'Track call: Order Completed event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Order Completed', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Order Completed', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'MovableInk-track-test-3', + name: destType, + description: 'Track call: Custom event', + scenario: 'Framework+Business', + successCriteria: + 'Response should contain the input payload with few additional mappings configured in transformer and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Custom Event', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: destination.Config.endpoint, + headers, + JSON: { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Custom Event', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/processor/validation.ts b/test/integrations/destinations/movable_ink/processor/validation.ts new file mode 100644 index 0000000000..6aafb5e2c0 --- /dev/null +++ b/test/integrations/destinations/movable_ink/processor/validation.ts @@ -0,0 +1,261 @@ +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; +import { destType, destination, processorInstrumentationErrorStatTags } from '../common'; + +export const validation: ProcessorTestData[] = [ + { + id: 'MovableInk-validation-test-1', + name: destType, + description: 'All of the required fields — userId, email, and anonymousId — are missing.', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Either one of userId or email or anonymousId is required. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Either one of userId or email or anonymousId is required. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-2', + name: destType, + description: 'Unsupported message type -> group', + scenario: 'Framework', + successCriteria: 'Instrumentation Error for Unsupported message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'group', + userId: 'userId123', + channel: 'mobile', + anonymousId: 'anon_123', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'message type group is not supported: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: message type group is not supported', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-3', + name: destType, + description: 'Missing required field -> timestamp', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + All: true, + }, + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Timestamp is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Timestamp is not present. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-4', + name: destType, + description: "Products Searched event - Missing 'query' property", + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'user123', + integrations: { + All: true, + }, + event: 'Products Searched', + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "Missing 'query' property in properties. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Missing 'query' property in properties. Aborting", + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-5', + name: destType, + description: "Products Added event - Missing 'product_id' property", + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'user123', + integrations: { + All: true, + }, + event: 'Product Added', + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + "Missing 'product_id' property in properties. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Missing 'product_id' property in properties. Aborting", + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, + { + id: 'MovableInk-validation-test-6', + name: destType, + description: 'Missing event name', + scenario: 'Framework', + successCriteria: 'Instrumentation Error', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + anonymousId: 'anonId123', + userId: 'userId123', + properties: {}, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + 'Event name is not present. Aborting: Workflow: procWorkflow, Step: validateInput, ChildStep: undefined, OriginalError: Event name is not present. Aborting', + metadata: generateMetadata(1), + statTags: processorInstrumentationErrorStatTags, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/movable_ink/router/data.ts b/test/integrations/destinations/movable_ink/router/data.ts new file mode 100644 index 0000000000..afadfec56e --- /dev/null +++ b/test/integrations/destinations/movable_ink/router/data.ts @@ -0,0 +1,355 @@ +import { RouterTestData } from '../../../testTypes'; +import { RouterTransformationRequest } from '../../../../../src/types'; +import { generateMetadata } from '../../../testUtils'; +import { defaultMockFns } from '../mocks'; +import { + destType, + channel, + destination, + traits, + headers, + trackTestProperties, + RouterInstrumentationErrorStatTags, +} from '../common'; + +const routerRequest: RouterTransformationRequest = { + input: [ + { + message: { + type: 'identify', + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + destination, + }, + { + message: { + type: 'identify', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Product Added', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(3), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Custom Event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(4), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Custom Event', + properties: trackTestProperties['Custom Event'], + integrations: { + All: true, + }, + }, + metadata: generateMetadata(5), + destination, + }, + ], + destType, +}; + +// >5KB payload +const routerRequest2: RouterTransformationRequest = { + input: [ + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Order Completed', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(1), + destination, + }, + { + message: { + type: 'track', + channel, + anonymousId: 'anonId123', + userId: 'userId123', + event: 'Custom Event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + }, + metadata: generateMetadata(2), + destination, + }, + ], + destType, +}; + +export const data = [ + { + id: 'MovableInk-router-test-1', + name: destType, + description: 'Basic Router Test to test multiple payloads', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for missing fields and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'identify', + userId: traits.email, + anonymousId: 'anonId123', + traits, + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Product Added', + properties: trackTestProperties['Product Added'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(1), generateMetadata(3)], + batched: true, + statusCode: 200, + destination, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Custom Event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(4)], + batched: true, + statusCode: 200, + destination, + }, + { + metadata: [generateMetadata(2)], + batched: false, + statusCode: 400, + error: 'Either one of userId or email or anonymousId is required. Aborting', + statTags: RouterInstrumentationErrorStatTags, + destination, + }, + { + metadata: [generateMetadata(5)], + batched: false, + statusCode: 400, + error: 'Timestamp is not present. Aborting', + statTags: RouterInstrumentationErrorStatTags, + destination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + id: 'MovableInk-router-test-2', + name: destType, + description: 'Basic Router Test to test Max Request Size', + scenario: 'Framework', + successCriteria: + 'Some events should be transformed successfully and some should fail for missing fields and status code should be 200', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: routerRequest2, + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Order Completed', + properties: trackTestProperties['Order Completed'], + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(1)], + batched: true, + statusCode: 200, + destination, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: destination.Config.endpoint, + headers, + params: {}, + body: { + JSON: { + events: [ + { + type: 'track', + channel, + userId: 'userId123', + anonymousId: 'anonId123', + event: 'Custom Event', + integrations: { + All: true, + }, + originalTimestamp: '2024-03-04T15:32:56.409Z', + timestamp: 1709566376409, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [generateMetadata(2)], + batched: true, + statusCode: 200, + destination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, +]; diff --git a/test/integrations/destinations/mp/common.ts b/test/integrations/destinations/mp/common.ts index 82f0e3202b..d40afa0c02 100644 --- a/test/integrations/destinations/mp/common.ts +++ b/test/integrations/destinations/mp/common.ts @@ -7,7 +7,7 @@ const defaultMockFns = () => { const sampleDestination: Destination = { Config: { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', prefixProperties: true, useNativeSDK: false, }, diff --git a/test/integrations/destinations/mp/processor/data.ts b/test/integrations/destinations/mp/processor/data.ts index 5b2d0fbfff..db5bc840c2 100644 --- a/test/integrations/destinations/mp/processor/data.ts +++ b/test/integrations/destinations/mp/processor/data.ts @@ -12,7 +12,7 @@ export const data = [ request: { body: [ { - destination: overrideDestination(sampleDestination, { token: 'dummyApiKey' }), + destination: overrideDestination(sampleDestination, { token: 'test_api_token' }), message: { anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', channel: 'web', @@ -87,14 +87,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","campaign_id":"test_name","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","campaign_id":"test_name","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -191,14 +194,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Viewed a Contact Us page","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us","category":"Contact","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Viewed a Contact Us page","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us","category":"Contact","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -272,14 +278,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"category":"communication","ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', + '[{"event":"Loaded a Screen","properties":{"category":"communication","ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', }, XML: {}, FORM: {}, @@ -360,14 +369,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","ip":"0.0.0.0","$user_id":"hjiklmk","$screen_dpi":2,"mp_lib":"RudderLabs Android SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjiklmk","time":1579847342402,"name":"Contact Us","category":"Contact"}}]', + '[{"event":"Loaded a Screen","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","ip":"0.0.0.0","$user_id":"hjiklmk","$screen_dpi":2,"mp_lib":"RudderLabs Android SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjiklmk","time":1579847342402,"name":"Contact Us","category":"Contact"}}]', }, XML: {}, FORM: {}, @@ -440,14 +452,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Screen","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', + '[{"event":"Loaded a Screen","properties":{"ip":"0.0.0.0","$user_id":"hjikl","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"hjikl","time":1579847342402,"name":"Contact Us"}}]', }, XML: {}, FORM: {}, @@ -550,7 +565,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -664,7 +679,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":45.89}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":45.89}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -686,7 +701,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$add":{"counter":1,"item_purchased":"2"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$add":{"counter":1,"item_purchased":"2"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -701,14 +716,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":45.89,"counter":1,"item_purchased":"2","number_of_logins":"","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","campaign_id":"test_name","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342403,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":45.89,"counter":1,"item_purchased":"2","number_of_logins":"","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","campaign_id":"test_name","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342403,"utm_campaign":"test_name","utm_source":"rudder","utm_medium":"test_medium","utm_term":"test_tem","utm_content":"test_content","utm_test":"test","utm_keyword":"test_keyword","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -796,14 +814,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"dummyApiKey"}}]', + '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -933,7 +954,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -948,14 +969,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1089,7 +1113,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":34}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":34}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -1104,14 +1128,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","revenue":34,"key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","first_name":"Mickey","lastName":"Mouse","name":"Mickey Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","revenue":34,"key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","first_name":"Mickey","lastName":"Mouse","name":"Mickey Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1235,14 +1262,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":" new Order Completed totally","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":" new Order Completed totally","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1366,14 +1396,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":" Order Completed ","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"Billing Amount":"77","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":" Order Completed ","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"total":23,"order_id":"50314b8e9bcf000000000000","key_1":{"child_key1":"child_value1","child_key2":{"child_key21":"child_value21","child_key22":"child_value22"}},"products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"shipping":3,"subtotal":22.5,"tax":2,"Billing Amount":"77","city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -1541,7 +1574,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -1628,7 +1661,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1650,7 +1683,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -1737,7 +1770,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp","testComp1"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp","testComp1"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1759,7 +1792,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":["testComp","testComp1"]}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":["testComp","testComp1"]}}]', }, XML: {}, FORM: {}, @@ -1781,7 +1814,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp1","$set":{"company":["testComp","testComp1"]}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp1","$set":{"company":["testComp","testComp1"]}}]', }, XML: {}, FORM: {}, @@ -1869,7 +1902,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -1891,7 +1924,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -2019,7 +2052,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.402Z","$amount":25}},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca"}]', }, XML: {}, FORM: {}, @@ -2034,14 +2067,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api-eu.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api-eu.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstname":"Mickey","lastname":"Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"KM Order Completed","properties":{"affiliation":"Google Store","checkout_id":"fksdjfsdjfisjf9sdfjsd9f","coupon":"hasbros","currency":"USD","discount":2.5,"order_id":"50314b8e9bcf000000000000","products":[{"category":"Games","image_url":"https:///www.example.com/product/path.jpg","name":"Monopoly: 3rd Edition","price":19,"product_id":"507f1f77bcf86cd799439011","quantity":1,"sku":"45790-32","url":"https://www.example.com/product/path"},{"category":"Games","name":"Uno Card Game","price":3,"product_id":"505bd76785ebb509fc183733","quantity":2,"sku":"46493-32"}],"revenue":25,"shipping":3,"subtotal":22.5,"tax":2,"total":27.5,"city":"Disney","country":"USA","email":"mickey@disney.com","firstname":"Mickey","lastname":"Mouse","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"aa5f5e44-8756-40ad-ad1e-b0d3b9fa710a","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -2129,7 +2165,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","$android_devices":["test_device_token"],"$os":"Android","$android_model":"Android SDK built for x86","$android_os_version":"8.1.0","$android_manufacturer":"Google","$android_app_version":"1.0","$android_app_version_code":"1.0","$android_brand":"Google"},"$token":"dummyApiKey","$distinct_id":"5094f5704b9cf2b3","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","$android_devices":["test_device_token"],"$os":"Android","$android_model":"Android SDK built for x86","$android_os_version":"8.1.0","$android_manufacturer":"Google","$android_app_version":"1.0","$android_app_version_code":"1.0","$android_brand":"Google"},"$token":"test_api_token","$distinct_id":"5094f5704b9cf2b3","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2216,7 +2252,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2233,7 +2269,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -2241,7 +2277,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2328,14 +2364,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","category":"communication","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"dummyApiKey","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"path":"/tests/html/index2.html","referrer":"","search":"","title":"","url":"http://localhost/tests/html/index2.html","category":"communication","ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1579847342402,"name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -2423,14 +2462,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"dummyApiKey"}}]', + '[{"event":"$create_alias","properties":{"distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","alias":"1234abc","token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2523,7 +2565,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","createdat":"2020-01-23T08:54:02.362Z","$ios_devices":["test_device_token"],"$ios_device_model":"Android SDK built for x86","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","createdat":"2020-01-23T08:54:02.362Z","$ios_devices":["test_device_token"],"$ios_device_model":"Android SDK built for x86","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -2540,7 +2582,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -2548,7 +2590,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -2641,7 +2683,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2733,7 +2775,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2826,7 +2868,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -2924,7 +2966,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3020,7 +3062,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3115,7 +3157,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3209,7 +3251,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3304,7 +3346,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$name":"Mickey Mouse","$country_code":"USA","$city":"Disney","$region":"US","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3405,7 +3447,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -3502,17 +3544,29 @@ export const data = [ status: 200, body: [ { - error: - 'Event timestamp is older than 5 days and no API secret or service account credentials (i.e. username, secret and projectId) are provided in destination configuration', - statTags: { - destType: 'MP', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, + body: { + JSON: {}, + JSON_ARRAY: { + batch: + '[{"event":"FirstTrackCall12","properties":{"foo":"bar","$deviceId":"nkasdnkasd","anonymousId":"ea776ad0-3136-44fb-9216-5b1578609a2b","userId":"as09sufa09usaf09as0f9uasf","id":"as09sufa09usaf09as0f9uasf","firstName":"Bob","lastName":"Marley","name":"Bob Marley","age":43,"email":"bob@marleymail.com","phone":"+447748544123","birthday":"1987-01-01T20:08:59+0000","createdAt":"2022-01-21T14:10:12+0000","address":"51,B.L.T road, Kolkata-700060","description":"I am great","gender":"male","title":"Founder","username":"bobm","website":"https://bobm.com","randomProperty":"randomValue","$user_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$current_url":"http://127.0.0.1:7307/Testing/App_for_testingTool/","$referrer":"http://127.0.0.1:7307/Testing/","$screen_height":900,"$screen_width":1440,"$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.1.18","$insert_id":"0d5c1a4a-27e4-41da-a246-4d01f44e74bd","token":"test_api_token","distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","time":1632986123523,"$browser":"Chrome","$browser_version":"93.0.4577.82"}}]', + }, + XML: {}, + FORM: {}, + }, + files: {}, + userId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', }, - statusCode: 400, + statusCode: 200, }, ], }, @@ -3686,7 +3740,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -3694,7 +3748,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"MainActivity","properties":{"name":"MainActivity","automatic":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$user_id":"test_user_id","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"dummyApiKey","distinct_id":"test_user_id","time":1520845503421}}]', + '[{"event":"MainActivity","properties":{"name":"MainActivity","automatic":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$user_id":"test_user_id","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"test_api_token","distinct_id":"test_user_id","time":1520845503421}}]', }, XML: {}, FORM: {}, @@ -3965,7 +4019,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402,"$ignore_time":true}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402,"$ignore_time":true}]', }, XML: {}, FORM: {}, @@ -4071,7 +4125,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"e6ab2c5e-2cda-44a9-a962-e2f67df78bca","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4276,7 +4330,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"dummyApiKey","$distinct_id":"user1234","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$first_name":"Mickey","$last_name":"Mouse","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"user1234","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4294,15 +4348,14 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: - 'Basic cnVkZGVyLmQyYTNmMS5tcC1zZXJ2aWNlLWFjY291bnQ6amF0cFF4Y2pNaDhlZXRrMXhySDNLalFJYnp5NGlYOGI=', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, - params: { project_id: '123456', strict: 0 }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["user1234","e6ab2c5e-2cda-44a9-a962-e2f67df78bca"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["user1234","e6ab2c5e-2cda-44a9-a962-e2f67df78bca"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, @@ -4387,7 +4440,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -4395,7 +4448,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -4477,7 +4530,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -4485,7 +4538,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Opened","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Opened","properties":{"build":4,"version":"1.0","anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -4573,7 +4626,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$distinct_id":"hjikl","$set":{"groupId":["testGroupId"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"hjikl","$set":{"groupId":["testGroupId"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -4595,7 +4648,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"dummyApiKey","$group_key":"groupId","$group_id":"testGroupId","$set":{"company":"testComp","groupId":"groupIdInTraits"}}]', + '[{"$token":"test_api_token","$group_key":"groupId","$group_id":"testGroupId","$set":{"company":"testComp","groupId":"groupIdInTraits"}}]', }, XML: {}, FORM: {}, @@ -4622,7 +4675,7 @@ export const data = [ description: 'Track: set device id and user id when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4678,14 +4731,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Product Viewed","properties":{"name":"T-Shirt","$user_id":"userId01","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"apiToken123","distinct_id":"userId01","time":1579847342402,"$device_id":"anonId01"}}]', + '[{"event":"Product Viewed","properties":{"name":"T-Shirt","$user_id":"userId01","$os":"iOS","$screen_height":1794,"$screen_width":1080,"$screen_dpi":420,"$carrier":"Android","$os_version":"8.1.0","$device":"generic_x86","$manufacturer":"Google","$model":"Android SDK built for x86","mp_device_model":"Android SDK built for x86","$wifi":true,"$bluetooth_enabled":false,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"1","$app_version_string":"1.0","$insert_id":"id2","token":"test_api_token","distinct_id":"userId01","time":1579847342402,"$device_id":"anonId01"}}]', }, XML: {}, FORM: {}, @@ -4714,7 +4770,7 @@ export const data = [ { description: 'Identify: skip merge event when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4795,7 +4851,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"apiToken123","$distinct_id":"userId01","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"userId01","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4823,7 +4879,7 @@ export const data = [ 'Identify: append $device: to deviceId while creating the user when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -4903,7 +4959,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"apiToken123","$distinct_id":"$device:anonId01","$ip":"0.0.0.0","$time":1579847342402}]', + '[{"$set":{"$created":"2020-01-23T08:54:02.362Z","$email":"mickey@disney.com","$country_code":"USA","$city":"Disney","$initial_referrer":"https://docs.rudderstack.com","$initial_referring_domain":"docs.rudderstack.com","$name":"Mickey Mouse","$firstName":"Mickey","$lastName":"Mouse","$browser":"Chrome","$browser_version":"79.0.3945.117"},"$token":"test_api_token","$distinct_id":"$device:anonId01","$ip":"0.0.0.0","$time":1579847342402}]', }, XML: {}, FORM: {}, @@ -4930,7 +4986,7 @@ export const data = [ description: 'Unsupported alias call when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5019,7 +5075,7 @@ export const data = [ 'Track revenue event: set device id and user id when simplified id merge api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5090,7 +5146,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":18.9}},"$token":"apiToken123","$distinct_id":"userId01"}]', + '[{"$append":{"$transactions":{"$time":"2020-01-24T06:29:02.403Z","$amount":18.9}},"$token":"test_api_token","$distinct_id":"userId01"}]', }, XML: {}, FORM: {}, @@ -5105,14 +5161,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":18.9,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$user_id":"userId01","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"apiToken123","distinct_id":"userId01","time":1579847342403,"$device_id":"anonId01","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"test revenue MIXPANEL","properties":{"currency":"USD","revenue":18.9,"city":"Disney","country":"USA","email":"mickey@disney.com","firstName":"Mickey","ip":"0.0.0.0","$user_id":"userId01","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"a6a0ad5a-bd26-4f19-8f75-38484e580fc7","token":"test_api_token","distinct_id":"userId01","time":1579847342403,"$device_id":"anonId01","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -5142,7 +5201,7 @@ export const data = [ description: 'Page with anonymous user when simplified api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', }), message: { @@ -5209,14 +5268,17 @@ export const data = [ version: '1', type: 'REST', method: 'POST', - endpoint: 'https://api.mixpanel.com/track/', - headers: {}, - params: {}, + endpoint: 'https://api.mixpanel.com/import/', + headers: { + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', + 'Content-Type': 'application/json', + }, + params: { strict: 0 }, body: { JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"apiToken123","distinct_id":"$device:anonId01","time":1579847342402,"$device_id":"anonId01","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', + '[{"event":"Loaded a Page","properties":{"ip":"0.0.0.0","$current_url":"https://docs.rudderstack.com/destinations/mixpanel","$screen_dpi":2,"mp_lib":"RudderLabs JavaScript SDK","$app_build_number":"1.0.0","$app_version_string":"1.0.5","$insert_id":"dd266c67-9199-4a52-ba32-f46ddde67312","token":"test_api_token","distinct_id":"$device:anonId01","time":1579847342402,"$device_id":"anonId01","name":"Contact Us","$browser":"Chrome","$browser_version":"79.0.3945.117"}}]', }, XML: {}, FORM: {}, @@ -5246,7 +5308,7 @@ export const data = [ description: 'Group call with anonymous user when simplified api is selected', destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', groupKeySettings: [{ groupKey: 'company' }], }), @@ -5309,7 +5371,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"apiToken123","$distinct_id":"$device:anonId01","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', + '[{"$token":"test_api_token","$distinct_id":"$device:anonId01","$set":{"company":["testComp"]},"$ip":"0.0.0.0"}]', }, XML: {}, FORM: {}, @@ -5331,7 +5393,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$token":"apiToken123","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', + '[{"$token":"test_api_token","$group_key":"company","$group_id":"testComp","$set":{"company":"testComp"}}]', }, XML: {}, FORM: {}, @@ -5357,7 +5419,7 @@ export const data = [ { destination: overrideDestination(sampleDestination, { apiKey: 'apiKey123', - token: 'apiToken123', + token: 'test_api_token', identityMergeApi: 'simplified', groupKeySettings: [{ groupKey: 'company' }], }), @@ -5476,7 +5538,7 @@ export const data = [ }, destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'dummyApiKey', useNewMapping: true, }), @@ -5502,7 +5564,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":12.13}},"$token":"dummyApiKey","$distinct_id":"39da706ec83d0e90"}]', + '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":12.13}},"$token":"test_api_token","$distinct_id":"39da706ec83d0e90"}]', }, XML: {}, FORM: {}, @@ -5519,7 +5581,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -5527,7 +5589,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":12.13,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":12.13,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":1662363980290}}]', }, XML: {}, FORM: {}, @@ -5591,7 +5653,7 @@ export const data = [ }, destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'dummyApiKey', useNewMapping: true, }), @@ -5617,7 +5679,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":23.45}},"$token":"dummyApiKey","$distinct_id":"39da706ec83d0e90"}]', + '[{"$append":{"$transactions":{"$time":"2022-09-05T07:46:20.290Z","$amount":23.45}},"$token":"test_api_token","$distinct_id":"39da706ec83d0e90"}]', }, XML: {}, FORM: {}, @@ -5634,7 +5696,7 @@ export const data = [ method: 'POST', endpoint: 'https://api.mixpanel.com/import/', headers: { - Authorization: 'Basic ZHVtbXlBcGlLZXk6', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 0 }, @@ -5642,7 +5704,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":23.45,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"dummyApiKey","distinct_id":"39da706ec83d0e90","time":null}}]', + '[{"event":"Application Installed","properties":{"build":4,"version":"1.0","revenue":23.45,"anonymousId":"39da706ec83d0e90","$os":"Android","$screen_height":2984,"$screen_width":1440,"$screen_dpi":560,"$carrier":"T-Mobile","$os_version":"12","$device":"emu64a","$manufacturer":"Google","$model":"sdk_gphone64_arm64","mp_device_model":"sdk_gphone64_arm64","$wifi":true,"$bluetooth_enabled":true,"mp_lib":"com.rudderstack.android.sdk.core","$app_build_number":"4","$app_version_string":"1.0","$insert_id":"168cf720-6227-4b56-a98e-c49bdc7279e9","$session_id":"1662363980","token":"test_api_token","distinct_id":"39da706ec83d0e90","time":null}}]', }, XML: {}, FORM: {}, @@ -5669,7 +5731,7 @@ export const data = [ description: 'Track: with strict mode enabled', destination: overrideDestination(sampleDestination, { apiKey: 'dummyApiKey', - token: 'dummyApiKey', + token: 'test_api_token', apiSecret: 'some_api_secret', dataResidency: 'eu', strictMode: true, @@ -5733,7 +5795,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"dummyApiKey","$distinct_id":"test_user_id","$time":1584003903421}]', + '[{"$set":{"$carrier":"Android","$manufacturer":"Google","$model":"Android SDK built for x86","$screen_height":1794,"$screen_width":1080,"$wifi":true,"anonymousId":"5094f5704b9cf2b3","userId":"test_user_id","$ios_devices":["test_device_token"],"$os":"iOS","$ios_device_model":"Android SDK built for x86","$ios_version":"8.1.0","$ios_app_release":"1","$ios_app_version":"1.0"},"$token":"test_api_token","$distinct_id":"test_user_id","$time":1584003903421}]', }, XML: {}, FORM: {}, @@ -5750,7 +5812,7 @@ export const data = [ method: 'POST', endpoint: 'https://api-eu.mixpanel.com/import/', headers: { - Authorization: 'Basic c29tZV9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', 'Content-Type': 'application/json', }, params: { strict: 1 }, @@ -5758,7 +5820,7 @@ export const data = [ JSON: {}, JSON_ARRAY: { batch: - '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"dummyApiKey"}}]', + '[{"event":"$merge","properties":{"$distinct_ids":["test_user_id","5094f5704b9cf2b3"],"token":"test_api_token"}}]', }, XML: {}, FORM: {}, diff --git a/test/integrations/destinations/mp/router/data.ts b/test/integrations/destinations/mp/router/data.ts index 059e222e92..8716c9daa0 100644 --- a/test/integrations/destinations/mp/router/data.ts +++ b/test/integrations/destinations/mp/router/data.ts @@ -442,7 +442,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -509,7 +509,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -577,7 +577,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1166,7 +1166,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1232,7 +1232,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { @@ -1299,7 +1299,7 @@ export const data = [ endpoint: 'https://api.mixpanel.com/import/', headers: { 'Content-Type': 'application/json', - Authorization: 'Basic dGVzdF9hcGlfc2VjcmV0Og==', + Authorization: 'Basic dGVzdF9hcGlfdG9rZW46', }, params: { strict: 1 }, body: { diff --git a/test/integrations/destinations/ninetailed/commonConfig.ts b/test/integrations/destinations/ninetailed/commonConfig.ts index 3b5d4149f2..99df88dd66 100644 --- a/test/integrations/destinations/ninetailed/commonConfig.ts +++ b/test/integrations/destinations/ninetailed/commonConfig.ts @@ -71,7 +71,40 @@ export const context = { timezone: 'America/Los_Angeles', }, }; - +export const contextWithNoLocation = { + app: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + campaign: { + name: 'campign_123', + source: 'social marketing', + medium: 'facebook', + term: '1 year', + }, + library: { + name: 'RudderstackSDK', + version: 'Ruddderstack SDK version', + }, + locale: 'en-US', + page: { + path: '/signup', + referrer: 'https://rudderstack.medium.com/', + search: '?type=freetrial', + url: 'https://app.rudderstack.com/signup?type=freetrial', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', +}; +export const commonInputWithNoLocationAndChannel = { + anonymousId: 'anon_123', + messageId: 'dummy_msg_id', + context: contextWithNoLocation, + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', +}; export const commonInput = { anonymousId: 'anon_123', messageId: 'dummy_msg_id', diff --git a/test/integrations/destinations/ninetailed/processor/data.ts b/test/integrations/destinations/ninetailed/processor/data.ts index 4e5fa72365..9d3cd217cd 100644 --- a/test/integrations/destinations/ninetailed/processor/data.ts +++ b/test/integrations/destinations/ninetailed/processor/data.ts @@ -1,5 +1,4 @@ import { validationFailures } from './validation'; import { track } from './track'; -import { page } from './page'; import { identify } from './identify'; -export const data = [...identify, ...page, ...track, ...validationFailures]; +export const data = [...identify, ...track, ...validationFailures]; diff --git a/test/integrations/destinations/ninetailed/processor/identify.ts b/test/integrations/destinations/ninetailed/processor/identify.ts index fbd7379e19..b580e17ed0 100644 --- a/test/integrations/destinations/ninetailed/processor/identify.ts +++ b/test/integrations/destinations/ninetailed/processor/identify.ts @@ -2,8 +2,8 @@ import { destination, traits, commonInput, + commonInputWithNoLocationAndChannel, metadata, - processInstrumentationErrorStatTags, } from '../commonConfig'; import { transformResultBuilder } from '../../../testUtils'; export const identify = [ @@ -109,12 +109,12 @@ export const identify = [ }, }, { - id: 'ninetailed-test-identify-failure-1', + id: 'ninetailed-test-identify-success-2', name: 'ninetailed', description: 'identify call with no userId available', scenario: 'Framework', successCriteria: - 'Error should be thrown for required field userId not present and status code should be 200', + 'No Error should be thrown for field userId not present but default empty string should be provided to userId and status code should be 200', feature: 'processor', module: 'destination', version: 'v0', @@ -124,9 +124,8 @@ export const identify = [ { destination, message: { - ...commonInput, + ...commonInputWithNoLocationAndChannel, type: 'identify', - channel: 'mobile', messageId: 'dummy_msg_id', traits: traits, }, @@ -140,13 +139,143 @@ export const identify = [ status: 200, body: [ { - error: - 'Missing required value from "userIdOnly": Workflow: procWorkflow, Step: preparePayload, ChildStep: undefined, OriginalError: Missing required value from "userIdOnly"', metadata: { destinationId: 'dummyDestId', }, - statTags: processInstrumentationErrorStatTags, - statusCode: 400, + output: transformResultBuilder({ + method: 'POST', + endpoint: + 'https://experience.ninetailed.co/v2/organizations/dummyOrganisationId/environments/main/events', + JSON: { + events: [ + { + context: { + app: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + campaign: { + name: 'campign_123', + source: 'social marketing', + medium: 'facebook', + term: '1 year', + }, + library: { + name: 'RudderstackSDK', + version: 'Ruddderstack SDK version', + }, + locale: 'en-US', + page: { + path: '/signup', + referrer: 'https://rudderstack.medium.com/', + search: '?type=freetrial', + url: 'https://app.rudderstack.com/signup?type=freetrial', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + location: {}, + }, + type: 'identify', + userId: '', + channel: 'server', + messageId: 'dummy_msg_id', + traits: traits, + anonymousId: 'anon_123', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + ], + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'ninetailed-test-identify-success-3', + name: 'ninetailed', + description: 'identify call with no context.location present and {} is used as default', + scenario: 'Framework+Buisness', + successCriteria: 'Response should contain context.location as {} and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + ...commonInputWithNoLocationAndChannel, + userId: 'sajal12', + traits: traits, + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + metadata, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + destinationId: 'dummyDestId', + }, + output: transformResultBuilder({ + method: 'POST', + endpoint: + 'https://experience.ninetailed.co/v2/organizations/dummyOrganisationId/environments/main/events', + JSON: { + events: [ + { + context: { + app: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + campaign: { + name: 'campign_123', + source: 'social marketing', + medium: 'facebook', + term: '1 year', + }, + library: { + name: 'RudderstackSDK', + version: 'Ruddderstack SDK version', + }, + locale: 'en-US', + page: { + path: '/signup', + referrer: 'https://rudderstack.medium.com/', + search: '?type=freetrial', + url: 'https://app.rudderstack.com/signup?type=freetrial', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + location: {}, + }, + type: 'identify', + channel: 'server', + userId: 'sajal12', + messageId: 'dummy_msg_id', + traits: traits, + anonymousId: 'anon_123', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + ], + }, + userId: '', + }), + statusCode: 200, }, ], }, diff --git a/test/integrations/destinations/ninetailed/processor/page.ts b/test/integrations/destinations/ninetailed/processor/page.ts deleted file mode 100644 index 93a086ceea..0000000000 --- a/test/integrations/destinations/ninetailed/processor/page.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { destination, context, commonProperties, metadata } from '../commonConfig'; -import { transformResultBuilder } from '../../../testUtils'; -export const page = [ - { - id: 'ninetailed-test-page-success-1', - name: 'ninetailed', - description: 'page call with all mappings available', - scenario: 'Framework+Buisness', - successCriteria: 'Response should contain all the mappings and status code should be 200', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination, - message: { - context, - type: 'page', - event: 'product purchased', - userId: 'sajal12', - channel: 'mobile', - messageId: 'dummy_msg_id', - properties: commonProperties, - anonymousId: 'anon_123', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - metadata, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - metadata: { - destinationId: 'dummyDestId', - }, - output: transformResultBuilder({ - method: 'POST', - endpoint: - 'https://experience.ninetailed.co/v2/organizations/dummyOrganisationId/environments/main/events', - JSON: { - events: [ - { - context: { - app: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, - campaign: { - name: 'campign_123', - source: 'social marketing', - medium: 'facebook', - term: '1 year', - }, - library: { - name: 'RudderstackSDK', - version: 'Ruddderstack SDK version', - }, - locale: 'en-US', - page: { - path: '/signup', - referrer: 'https://rudderstack.medium.com/', - search: '?type=freetrial', - url: 'https://app.rudderstack.com/signup?type=freetrial', - }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - location: { - coordinates: { - latitude: 40.7128, - longitude: -74.006, - }, - city: 'San Francisco', - postalCode: '94107', - region: 'CA', - regionCode: 'CA', - country: ' United States', - countryCode: 'United States of America', - continent: 'North America', - timezone: 'America/Los_Angeles', - }, - }, - type: 'page', - channel: 'mobile', - messageId: 'dummy_msg_id', - properties: commonProperties, - anonymousId: 'anon_123', - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - ], - }, - userId: '', - }), - statusCode: 200, - }, - ], - }, - }, - }, -]; diff --git a/test/integrations/destinations/ninetailed/router/data.ts b/test/integrations/destinations/ninetailed/router/data.ts index 05105f4aed..14ac046127 100644 --- a/test/integrations/destinations/ninetailed/router/data.ts +++ b/test/integrations/destinations/ninetailed/router/data.ts @@ -3,6 +3,7 @@ import { destination, commonOutput, routerInstrumentationErrorStatTags, + context, } from '../commonConfig'; import { trackProperties, pageProperties, traits } from './basicProperties'; import { defaultMockFns } from '../mocks'; @@ -31,15 +32,6 @@ export const data = [ metadata: { jobId: 1, userId: 'u1' }, destination, }, - { - message: { - ...commonInput, - type: 'page', - properties: pageProperties, - }, - metadata: { jobId: 2, userId: 'u1' }, - destination, - }, { message: { type: 'identify', @@ -80,11 +72,6 @@ export const data = [ event: 'product list viewed', properties: trackProperties, }, - { - ...commonOutput, - type: 'page', - properties: pageProperties, - }, { type: 'identify', ...commonOutput, @@ -103,7 +90,6 @@ export const data = [ }, metadata: [ { jobId: 1, userId: 'u1' }, - { jobId: 2, userId: 'u1' }, { jobId: 3, userId: 'u1' }, ], batched: true, @@ -142,21 +128,9 @@ export const data = [ { message: { ...commonInput, - type: 'page', - properties: { - title: 'Sample Page', - url: 'https://example.com/?utm_campaign=example_campaign&utm_content=example_content', - path: '/', - hash: '', - search: '?utm_campaign=example_campaign&utm_content=example_content', - width: '1920', - height: '1080', - query: { - utm_campaign: 'example_campaign', - utm_content: 'example_content', - }, - referrer: '', - }, + type: 'track', + event: 'product added', + properties: trackProperties, }, metadata: { jobId: 2, userId: 'u1' }, destination, @@ -164,9 +138,14 @@ export const data = [ { message: { type: 'identify', - ...commonInput, + messageId: 'dummy_msg_id', + context, + channel: 'web', + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', traits, - integrations: { All: true }, }, metadata: { jobId: 3, userId: 'u1' }, destination, @@ -185,7 +164,7 @@ export const data = [ { batched: false, destination, - error: 'Missing required value from "userIdOnly"', + error: 'Missing required value from "anonymousId"', metadata: [{ jobId: 3, userId: 'u1' }], statTags: routerInstrumentationErrorStatTags, statusCode: 400, @@ -210,8 +189,9 @@ export const data = [ }, { ...commonOutput, - type: 'page', - properties: pageProperties, + type: 'track', + event: 'product added', + properties: trackProperties, }, ], }, @@ -264,8 +244,9 @@ export const data = [ { message: { ...commonInput, - type: 'page', - properties: pageProperties, + type: 'track', + event: 'product added', + properties: trackProperties, }, metadata: { jobId: 2, userId: 'u1' }, destination, @@ -284,9 +265,14 @@ export const data = [ { message: { type: 'identify', - ...commonInput, + messageId: 'dummy_msg_id', + context, + channel: 'web', + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', traits, - integrations: { All: true }, }, metadata: { jobId: 4, userId: 'u1' }, destination, @@ -305,7 +291,7 @@ export const data = [ { batched: false, destination, - error: 'Missing required value from "userIdOnly"', + error: 'Missing required value from "anonymousId"', metadata: [{ jobId: 4, userId: 'u1' }], statTags: routerInstrumentationErrorStatTags, statusCode: 400, @@ -330,8 +316,9 @@ export const data = [ }, { ...commonOutput, - type: 'page', - properties: pageProperties, + type: 'track', + event: 'product added', + properties: trackProperties, }, ], }, diff --git a/test/integrations/destinations/one_signal/processor/commonConfig.ts b/test/integrations/destinations/one_signal/processor/commonConfig.ts new file mode 100644 index 0000000000..cdef3dbfb4 --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/commonConfig.ts @@ -0,0 +1,45 @@ +export const destination = { + Config: { + appId: 'random-818c-4a28-b98e-6cd8a994eb22', + emailDeviceType: true, + smsDeviceType: true, + eventAsTags: false, + allowedProperties: [ + { propertyName: 'brand' }, + { propertyName: 'firstName' }, + { propertyName: 'price' }, + ], + version: 'V2', + }, +}; + +export const endpoint = 'https://api.onesignal.com/apps/random-818c-4a28-b98e-6cd8a994eb22/users'; + +export const headers = { Accept: 'application/json', 'Content-Type': 'application/json' }; + +export const commonTraits = { + brand: 'John Players', + price: '15000', + firstName: 'Test', +}; +export const commonTags = { + brand: 'John Players', + price: '15000', + firstName: 'Test', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', +}; + +export const commonProperties = { + products: [ + { + sku: 3, + iso: 'iso', + quantity: 2, + amount: 100, + }, + ], + brand: 'John Players', + price: '15000', + firstName: 'Test', + customKey: 'customVal', +}; diff --git a/test/integrations/destinations/one_signal/processor/data.ts b/test/integrations/destinations/one_signal/processor/data.ts index 4171157aef..13aa6043f1 100644 --- a/test/integrations/destinations/one_signal/processor/data.ts +++ b/test/integrations/destinations/one_signal/processor/data.ts @@ -1,3 +1,4 @@ +import { data_v2 } from './data_v2'; export const data = [ { name: 'one_signal', @@ -1542,4 +1543,5 @@ export const data = [ }, }, }, + ...data_v2, ]; diff --git a/test/integrations/destinations/one_signal/processor/data_v2.ts b/test/integrations/destinations/one_signal/processor/data_v2.ts new file mode 100644 index 0000000000..325dca336d --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/data_v2.ts @@ -0,0 +1,5 @@ +import { identifyTests } from './identify'; +import { trackTests } from './track'; +import { validations } from './validations'; +import { groupTests } from './group'; +export const data_v2 = [...identifyTests, ...trackTests, ...validations, ...groupTests]; diff --git a/test/integrations/destinations/one_signal/processor/group.ts b/test/integrations/destinations/one_signal/processor/group.ts new file mode 100644 index 0000000000..e16d1e4fe6 --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/group.ts @@ -0,0 +1,121 @@ +import { destination, endpoint, headers } from './commonConfig'; +export const groupTests = [ + { + name: 'one_signal', + id: 'One Signal V2-test-group-success-1', + description: + 'Group call for adding a tag groupId with value as group id with no userId available', + module: 'destination', + successCriteria: 'Request gets passed with 200 Status Code with userId mapped to external_id', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + integrations: { + one_signal: { + aliases: { custom_alias: 'custom_alias_identifier' }, + }, + }, + type: 'group', + channel: 'server', + groupId: 'group@27', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { + tags: { + groupId: 'group@27', + }, + }, + identity: { + custom_alias: 'custom_alias_identifier', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-group-failure-1', + description: 'V2-> No Group Id Passes', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no group id available', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'group', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'groupId is required for group events', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/one_signal/processor/identify.ts b/test/integrations/destinations/one_signal/processor/identify.ts new file mode 100644 index 0000000000..175c18a8fa --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/identify.ts @@ -0,0 +1,341 @@ +import { commonTags, commonTraits, destination, endpoint, headers } from './commonConfig'; + +export const identifyTests = [ + { + name: 'one_signal', + id: 'One Signal V2-test-identify-success-1', + description: + 'V2-> Identify call for creating new user with userId only available and no subscriptions available', + module: 'destination', + successCriteria: 'Request gets passed with 200 Status Code with userId mapped to external_id', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: 'user@27', + channel: 'server', + context: { + app: { + version: '1.1.11', + }, + traits: commonTraits, + locale: 'en-US', + screen: { density: 2 }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { + tags: commonTags, + laguage: 'en-US', + created_at: 1609693373, + last_active: 1609693373, + }, + identity: { + external_id: 'user@27', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-identify-success-2', + description: + 'V2-> Identify call for creating new user with userId and one device subscription available', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code with userId mapped to external_id and one subscription for device where identifier is mapped from anonymousId', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + userId: 'user@27', + channel: 'web', + context: { + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, + traits: commonTraits, + locale: 'en-US', + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { + tags: commonTags, + laguage: 'en-US', + created_at: 1609693373, + last_active: 1609693373, + }, + subscriptions: [ + { token: '97c46c81-3140-456d-b2a9-690d70aaca35', type: 'FirefoxPush' }, + ], + identity: { + external_id: 'user@27', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-identify-success-3', + description: + 'V2-> Identify call for creating new user with userId and three device subscription available', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code with userId mapped to external_id and three subscription for device where one is mapped from anonymousId, one from email and one from phone', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + integrations: { + one_signal: { + aliases: { custom_alias: 'custom_alias_identifier' }, + }, + }, + channel: 'web', + context: { + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, + device: { + model: 'dummy model', + }, + os: { version: '1.0.0' }, + traits: { + ...commonTraits, + email: 'example@abc.com', + phone: '12345678', + subscriptions: { + email: { + enabled: true, + notification_types: 'SMS', + session_time: 123456, + session_count: 22, + app_version: '1.0.0', + test_type: 'dev', + }, + }, + }, + locale: 'en-US', + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { + tags: { ...commonTags, email: 'example@abc.com', phone: '12345678' }, + laguage: 'en-US', + created_at: 1609693373, + last_active: 1609693373, + }, + subscriptions: [ + { + device_model: 'dummy model', + device_os: '1.0.0', + token: '97c46c81-3140-456d-b2a9-690d70aaca35', + type: 'FirefoxPush', + }, + { + app_version: '1.0.0', + device_model: 'dummy model', + device_os: '1.0.0', + enabled: true, + notification_types: 'SMS', + session_count: 22, + session_time: 123456, + test_type: 'dev', + token: 'example@abc.com', + type: 'Email', + }, + { + device_model: 'dummy model', + device_os: '1.0.0', + token: '12345678', + type: 'SMS', + }, + ], + identity: { custom_alias: 'custom_alias_identifier' }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-identify-failure-1', + description: 'V2-> Identify call without any aliases', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no aliases present', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + channel: 'server', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'userId or any other alias is required for identify', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/one_signal/processor/track.ts b/test/integrations/destinations/one_signal/processor/track.ts new file mode 100644 index 0000000000..41735fc7a2 --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/track.ts @@ -0,0 +1,249 @@ +import { commonProperties, destination, endpoint, headers } from './commonConfig'; + +const commonTrackTags = { + brand: 'John Players', + price: '15000', + firstName: 'Test', +}; + +const purchases = [ + { + sku: 3, + iso: 'iso', + count: 2, + amount: 100, + }, +]; + +export const trackTests = [ + { + name: 'one_signal', + id: 'One Signal V2-test-track-success-1', + description: + 'V2-> Track call for updating user tags with userId available and products details available', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code with userId mapped to external_id and properties mapped to tags', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'Order Completed', + userId: 'user@27', + channel: 'server', + properties: commonProperties, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { purchases, tags: { ...commonTrackTags, 'Order Completed': true } }, + + identity: { + external_id: 'user@27', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-track-success-2', + description: 'V2-> Track call for products details available in properties directly', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code with userId mapped to external_id and purchases mapped from proeprties mapped to tags', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'Order Completed', + userId: 'user@27', + channel: 'server', + properties: {}, + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint, + headers, + params: {}, + body: { + FORM: {}, + JSON: { + properties: { purchases: [], tags: { 'Order Completed': true } }, + identity: { + external_id: 'user@27', + }, + }, + JSON_ARRAY: {}, + XML: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-track-failure-1', + description: 'V2-> Track call without event name', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due instrumentation error', + feature: 'processor', + scenario: 'Framework+Buisness', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + userId: 'user@27', + channel: 'server', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event is not present in the input payloads', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-track-failure-2', + description: 'V2-> Track call without any aliases', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no aliases present', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'dummy event', + channel: 'server', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'userId or any other alias is required for track and group', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/one_signal/processor/validations.ts b/test/integrations/destinations/one_signal/processor/validations.ts new file mode 100644 index 0000000000..7cfa158eee --- /dev/null +++ b/test/integrations/destinations/one_signal/processor/validations.ts @@ -0,0 +1,149 @@ +import { destination } from './commonConfig'; +export const validations = [ + { + name: 'one_signal', + id: 'One Signal V2-test-validation-failure-1', + description: 'V2-> No Message type passed', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no message type present', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'user@27', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event type is required', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-validation-failure-2', + description: 'V2-> invalid Message type passed', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due invalid message type present', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'invalid', + userId: 'user@27', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Message type invalid is not supported', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'one_signal', + id: 'One Signal V2-test-validation-failure-3', + description: 'V2-> No App Id Present in destination Config', + module: 'destination', + successCriteria: + 'Request gets passed with 200 Status Code and failure happened due no Configuration Error', + feature: 'processor', + scenario: 'Framework', + version: 'v0', + input: { + request: { + body: [ + { + destination: { Config: {} }, + message: { + type: 'invalid', + userId: 'user@27', + rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', + messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', + anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', + originalTimestamp: '2021-01-03T17:02:53.193Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + error: 'appId is a required field', + statTags: { + destType: 'ONE_SIGNAL', + errorCategory: 'dataValidation', + errorType: 'configuration', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/ortto/processor/data.ts b/test/integrations/destinations/ortto/processor/data.ts index 715262e447..ff84f5dbbd 100644 --- a/test/integrations/destinations/ortto/processor/data.ts +++ b/test/integrations/destinations/ortto/processor/data.ts @@ -158,6 +158,8 @@ export const data = [ people: [ { fields: { + 'bol::p': true, + 'bol::sp': true, 'str::first': 'John', 'str::last': 'Sparrow', 'str::email': 'identify@test.com', @@ -172,9 +174,6 @@ export const data = [ 'phn::phone': { n: '9112340345', }, - 'bol::gdpr': true, - 'bol::p': true, - 'bol::sp': true, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, @@ -1036,8 +1035,6 @@ export const data = [ n: '9112340345', }, 'bol::gdpr': false, - 'bol::p': false, - 'bol::sp': false, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, @@ -1250,8 +1247,6 @@ export const data = [ 'str::language': 'en-US', 'str::ei': 'sample_user_id', 'bol::gdpr': false, - 'bol::p': false, - 'bol::sp': false, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, @@ -1452,4 +1447,206 @@ export const data = [ }, }, }, + { + name: 'ortto', + description: 'Track call for testing multiple event mappings filter logic', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ORTTO', + Config: { + privateApiKey: 'pvKey', + instanceRegion: 'other', + orttoEventsMapping: [ + { + rsEventName: 'player.set.loved', + orttoEventName: 'Loved a set', + eventProperties: [ + { + rudderProperty: 'set.id', + orttoProperty: 'Set ID', + type: 'text', + }, + { + rudderProperty: 'set.title', + orttoProperty: 'Set title', + type: 'text', + }, + { + rudderProperty: 'set.artist', + orttoProperty: 'Artist name', + type: 'text', + }, + ], + }, + { + rsEventName: 'player.set.liked', + orttoEventName: 'Liked a set', + eventProperties: [ + { + rudderProperty: 'set.id', + orttoProperty: 'Set ID', + type: 'text', + }, + { + rudderProperty: 'set.title', + orttoProperty: 'Set title', + type: 'text', + }, + { + rudderProperty: 'set.artist', + orttoProperty: 'Artist name', + type: 'text', + }, + ], + }, + { + rsEventName: 'player.set.played', + orttoEventName: 'Played a set', + eventProperties: [ + { + rudderProperty: 'set.id', + orttoProperty: 'Set ID', + type: 'text', + }, + { + rudderProperty: 'set.title', + orttoProperty: 'Set title', + type: 'text', + }, + { + rudderProperty: 'set.artist', + orttoProperty: 'Artist name', + type: 'text', + }, + ], + }, + ], + orttoPersonAttributes: [ + { + rudderTraits: 'id', + orttoAttribute: 'External ID', + type: 'text', + }, + ], + }, + Enabled: true, + Transformations: [], + }, + metadata: { destintionId: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', jobId: 2 }, + message: { + type: 'track', + event: 'player.set.liked', + sentAt: '2024-04-29T06:45:59.576Z', + userId: 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', + channel: 'mobile', + context: { + locale: 'en-MU', + traits: { + email: 'dummy-test@email.oop', + userId: 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', + anonymousId: 'dd14b53d-bfb8-48a9-a46f-e3d1351d1dd8', + }, + library: { + name: 'rudder-ios-library', + version: '1.26.3', + }, + network: { + wifi: true, + cellular: false, + }, + timezone: 'Africa/Johannesburg', + }, + rudderId: '83cb2d47-9c08-4ce3-86cc-44d8486b337a', + messageId: '2d9525a7-2a55-4d31-9c34-78788a5cbb59', + timestamp: '2024-04-29T06:45:52.611Z', + properties: { + email: 'dummy-test@email.oop', + 'set.id': '537d8682-4b37-4f12-8452-3f57091c2a54', + userId: 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', + 'set.title': 'Burn Man_Master', + 'set.artist': 'John Doe', + environment: 'test', + 'set.artists.0.name': 'John Doe', + }, + receivedAt: '2024-04-29T06:45:59.779Z', + request_ip: '206.0.75.23', + anonymousId: 'dd14b53d-bfb8-48a9-a46f-e3d1351d1dd8', + integrations: { + All: true, + }, + originalTimestamp: '2024-04-29T06:45:52.408Z', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + destintionId: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + jobId: 2, + }, + output: { + body: { + FORM: {}, + JSON_ARRAY: {}, + XML: {}, + JSON: { + activities: [ + { + fields: { + 'str::email': 'dummy-test@email.oop', + 'geo::city': {}, + 'geo::country': {}, + 'geo::region': {}, + 'str::ei': 'XxXxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', + 'str::language': 'en-MU', + }, + activity_id: 'act:cm:liked-a-set', + attributes: { + 'str:cm:set-id': '537d8682-4b37-4f12-8452-3f57091c2a54', + 'str:cm:set-title': 'Burn Man_Master', + 'str:cm:artist-name': 'John Doe', + }, + location: {}, + }, + ], + merge_by: ['str::ei', 'str::email'], + }, + }, + endpoint: 'https://api.ap3api.com/v1/activities/create', + files: {}, + headers: { + 'Content-Type': 'application/json', + 'X-Api-Key': 'pvKey', + }, + method: 'POST', + params: {}, + type: 'REST', + userId: '', + version: '1', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/ortto/router/data.ts b/test/integrations/destinations/ortto/router/data.ts index cf5731be80..8999637a66 100644 --- a/test/integrations/destinations/ortto/router/data.ts +++ b/test/integrations/destinations/ortto/router/data.ts @@ -298,9 +298,6 @@ export const data = [ 'phn::phone': { n: '9112340345', }, - 'bol::gdpr': true, - 'bol::p': false, - 'bol::sp': false, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, @@ -385,6 +382,7 @@ export const data = [ activities: [ { fields: { + 'bol::gdpr': false, 'str::first': 'John', 'str::last': 'Sparrow', 'str::email': 'identify@test.com', @@ -404,9 +402,6 @@ export const data = [ 'phn::phone': { n: '9112340345', }, - 'bol::gdpr': false, - 'bol::p': false, - 'bol::sp': false, 'str:cm:ortto-attirbute0': 'abc', 'str:cm:ortto-attirbute1': 'def', }, diff --git a/test/integrations/destinations/pardot/dataDelivery/business.ts b/test/integrations/destinations/pardot/dataDelivery/business.ts new file mode 100644 index 0000000000..f6baefbc8f --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/business.ts @@ -0,0 +1,372 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; +import { commonRequestParameters, retryStatTags } from './constant'; + +const pardotResponseRogerEmail = { + '@attributes': { stat: 'ok', version: 1 }, + prospect: { + id: 123435, + campaign_id: 42213, + salutation: null, + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@federer.io', + password: null, + company: null, + website: 'https://rudderstack.com', + job_title: null, + department: null, + country: 'AU', + address_one: null, + address_two: null, + city: null, + state: null, + territory: null, + zip: null, + phone: null, + fax: null, + source: null, + annual_revenue: null, + employees: null, + industry: null, + years_in_business: null, + comments: null, + notes: null, + score: 14, + grade: null, + last_activity_at: null, + recent_interaction: 'Never active.', + crm_lead_fid: '00Q6r000002LKhTPVR', + crm_contact_fid: null, + crm_owner_fid: '00G2v000004WYXaEAO', + crm_account_fid: null, + salesforce_fid: '00Q6r000002LKhTPVR', + crm_last_sync: '2022-01-21 18:47:37', + crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', + is_do_not_email: null, + is_do_not_call: null, + opted_out: null, + is_reviewed: 1, + is_starred: null, + created_at: '2022-01-21 18:21:46', + updated_at: '2022-01-21 18:48:41', + campaign: { id: 42113, name: 'Test', crm_fid: '7012y000000MNOCLL4' }, + assigned_to: { + user: { + id: 38443703, + email: 'test_rudderstack@testcompany.com', + first_name: 'Rudderstack', + last_name: 'User', + job_title: null, + role: 'Administrator', + account: 489853, + created_at: '2021-02-26 06:25:17', + updated_at: '2021-02-26 06:25:17', + }, + }, + Are_you_shipping_large_fragile_or_bulky_items: false, + Calendly: false, + Country_Code: 'AU', + Currency: 'AUD', + Inventory_or_Warehouse_Management_System: false, + Lead_Status: 'New', + Marketing_Stage: 'SAL', + Record_Type_ID: 'TestCompany Lead', + profile: { + id: 304, + name: 'Default', + profile_criteria: [ + { id: 1500, name: 'Shipping Volume', matches: 'Unknown' }, + { id: 1502, name: 'Industry', matches: 'Unknown' }, + { id: 1506, name: 'Job Title', matches: 'Unknown' }, + { id: 1508, name: 'Department', matches: 'Unknown' }, + ], + }, + visitors: null, + visitor_activities: null, + lists: null, + }, +}; + +const pardotResponseWalterEmail = { + '@attributes': { stat: 'ok', version: 1 }, + prospect: { + id: 123435, + campaign_id: 42213, + salutation: null, + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@waltair.io', + password: null, + company: null, + website: 'https://rudderstack.com', + job_title: null, + department: null, + country: 'AU', + address_one: null, + address_two: null, + city: null, + state: null, + territory: null, + zip: null, + phone: null, + fax: null, + source: null, + annual_revenue: null, + employees: null, + industry: null, + years_in_business: null, + comments: null, + notes: null, + score: 14, + grade: null, + last_activity_at: null, + recent_interaction: 'Never active.', + crm_lead_fid: null, + crm_contact_fid: null, + crm_owner_fid: '00G2v000004WYXaEAO', + crm_account_fid: null, + salesforce_fid: null, + crm_last_sync: null, + crm_url: null, + is_do_not_email: null, + is_do_not_call: null, + opted_out: null, + is_reviewed: 1, + is_starred: null, + created_at: '2022-01-21 18:21:46', + updated_at: '2022-01-21 18:48:41', + campaign: { id: 42113, name: 'Test', crm_fid: '7012y000000MNOCLL4' }, + assigned_to: { + user: { + id: 38443703, + email: 'test_rudderstack@testcompany.com', + first_name: 'Rudderstack', + last_name: 'User', + job_title: null, + role: 'Administrator', + account: 489853, + created_at: '2021-02-26 06:25:17', + updated_at: '2021-02-26 06:25:17', + }, + }, + Are_you_shipping_large_fragile_or_bulky_items: false, + Calendly: false, + Country_Code: 'AU', + Currency: 'AUD', + Inventory_or_Warehouse_Management_System: false, + Lead_Status: 'New', + Marketing_Stage: 'SAL', + Record_Type_ID: 'TestCompany Lead', + profile: { + id: 304, + name: 'Default', + profile_criteria: [ + { id: 1500, name: 'Shipping Volume', matches: 'Unknown' }, + { id: 1502, name: 'Industry', matches: 'Unknown' }, + { id: 1506, name: 'Job Title', matches: 'Unknown' }, + { id: 1508, name: 'Department', matches: 'Unknown' }, + ], + }, + visitors: null, + visitor_activities: null, + lists: null, + }, +}; + +export const businessV0TestScenarios = [ + { + id: 'pardot_v0_bussiness_scenario_1', + name: 'pardot', + description: '[Proxy v0 API] :: pardot email upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 201, + message: 'Request Processed Successfully', + destinationResponse: { + response: pardotResponseWalterEmail, + status: 201, + }, + }, + }, + }, + }, + }, + { + id: 'pardot_v0_bussiness_scenario_2', + name: 'pardot', + description: '[Proxy v0 API] :: pardot fid type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + destinationResponse: { + response: pardotResponseRogerEmail, + status: 200, + }, + }, + }, + }, + }, + }, +]; + +export const businessV1TestScenarios: ProxyV1TestData[] = [ + { + id: 'pardot_v1_bussiness_scenario_1', + name: 'pardot', + description: '[Proxy v1 API] :: pardot email type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 201, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 201, + metadata: generateMetadata(1), + error: JSON.stringify(pardotResponseWalterEmail), + }, + ], + }, + }, + }, + }, + }, + { + id: 'pardot_v1_bussiness_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: pardot fid type upsert', + successCriteria: 'Proper response from destination is received', + scenario: 'business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: JSON.stringify(pardotResponseRogerEmail), + }, + ], + }, + }, + }, + }, + }, + { + id: 'pardot_v1_Business_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: Response with other retryable codes', + successCriteria: 'the proxy should return 500 with retryable tag', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@test.com', + params: { + destination: 'pardot', + }, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + 'Unable to verify Salesforce connector during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: retryStatTags, + message: 'Unable to verify Salesforce connector during Pardot response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/constant.ts b/test/integrations/destinations/pardot/dataDelivery/constant.ts new file mode 100644 index 0000000000..97e456998e --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/constant.ts @@ -0,0 +1,27 @@ +export const retryStatTags = { + destType: 'PARDOT', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; +const commonHeaders = { + Authorization: 'Bearer myToken', + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', + 'User-Agent': 'RudderLabs', +}; + +export const commonRequestParameters = { + headers: commonHeaders, + FORM: { + first_name: 'Roger12', + last_name: 'Federer12', + website: 'https://rudderstack.com', + score: 14, + campaign_id: 42213, + }, +}; diff --git a/test/integrations/destinations/pardot/dataDelivery/data.ts b/test/integrations/destinations/pardot/dataDelivery/data.ts new file mode 100644 index 0000000000..752ef22cb1 --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/data.ts @@ -0,0 +1,10 @@ +import { businessV0TestScenarios, businessV1TestScenarios } from './business'; +import { v1OauthScenarios } from './oauth'; +import { otherScenariosV1 } from './other'; + +export const data = [ + ...v1OauthScenarios, + ...businessV1TestScenarios, + ...businessV0TestScenarios, + ...otherScenariosV1, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/oauth.ts b/test/integrations/destinations/pardot/dataDelivery/oauth.ts new file mode 100644 index 0000000000..4a0116a951 --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/oauth.ts @@ -0,0 +1,53 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; +import { commonRequestParameters, retryStatTags } from './constant'; + +export const v1OauthScenarios: ProxyV1TestData[] = [ + { + id: 'pardot_v1_oauth_scenario_1', + name: 'pardot', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: + 'Since the error from the destination is 401 - the proxy should return 500 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + ...commonRequestParameters, + endpoint: + 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', + params: { + destination: 'pardot', + }, + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + response: [ + { + error: + 'access_token is invalid, unknown, or malformed: Inactive token during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: retryStatTags, + authErrorCategory: 'REFRESH_TOKEN', + message: + 'access_token is invalid, unknown, or malformed: Inactive token during Pardot response transformation', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/dataDelivery/other.ts b/test/integrations/destinations/pardot/dataDelivery/other.ts new file mode 100644 index 0000000000..b7454e691c --- /dev/null +++ b/test/integrations/destinations/pardot/dataDelivery/other.ts @@ -0,0 +1,205 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateMetadata, generateProxyV1Payload } from '../../../testUtils'; + +const expectedStatTags = { + destType: 'PARDOT', + errorCategory: 'network', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', +}; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'pardot_v1_other_scenario_1', + name: 'pardot', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}} during Pardot response transformation', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 503, + message: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}} during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_2', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"Internal Server Error" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_3', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout" during Pardot response transformation', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 504, + message: '"Gateway Timeout" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_4', + name: 'pardot', + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_response', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"" during Pardot response transformation', + }, + }, + }, + }, + }, + { + id: 'pardot_v1_other_scenario_5', + name: 'pardot', + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"" during Pardot response transformation', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: expectedStatTags, + status: 500, + message: '"" during Pardot response transformation', + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/pardot/network.ts b/test/integrations/destinations/pardot/network.ts index bbbe0d70f9..9493aab01f 100644 --- a/test/integrations/destinations/pardot/network.ts +++ b/test/integrations/destinations/pardot/network.ts @@ -1,25 +1,9 @@ -import { enhanceRequestOptions, getFormData } from '../../../../src/adapters/network'; +import { getFormData } from '../../../../src/adapters/network'; export const networkCallsData = [ - // 2nd proxy test-case { httpReq: { url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/Roger_12@waltair.io', - data: getFormData({ - first_name: 'Roger_12', - last_name: 'Federer_12', - website: 'https://rudderstack.com', - score: 14, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, method: 'POST', }, httpRes: { @@ -135,59 +119,9 @@ export const networkCallsData = [ statusText: 'Created', }, }, - // 4th proxy test-case - { - httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', - data: getFormData({ - first_name: 'Rolex', - last_name: 'Waltair', - website: 'https://rudderstack.com', - score: 15, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myExpiredToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, - method: 'POST', - }, - httpRes: { - data: { - '@attributes': { - stat: 'fail', - version: 1, - err_code: 184, - }, - err: 'access_token is invalid, unknown, or malformed: Inactive token', - }, - status: 401, - statusText: 'Unauthorized', - }, - }, - // 1st proxy test-case { httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/id/123435', - data: getFormData({ - first_name: 'Roger12', - last_name: 'Federer12', - website: 'https://rudderstack.com', - score: 14, - campaign_id: 42213, - format: 'json', - }).toString(), - params: { destination: 'pardot' }, - headers: { - Authorization: 'Bearer myToken', - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - 'Pardot-Business-Unit-Id': '0Uv2v000000k9tHCAQ', - 'User-Agent': 'RudderLabs', - }, + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', method: 'POST', }, httpRes: { @@ -200,9 +134,9 @@ export const networkCallsData = [ id: 123435, campaign_id: 42213, salutation: null, - first_name: 'Roger12', - last_name: 'Federer12', - email: 'Roger12@waltair.io', + first_name: 'Roger_12', + last_name: 'Federer_12', + email: 'Roger_12@federer.io', password: null, company: null, website: 'https://rudderstack.com', @@ -228,13 +162,13 @@ export const networkCallsData = [ grade: null, last_activity_at: null, recent_interaction: 'Never active.', - crm_lead_fid: null, + crm_lead_fid: '00Q6r000002LKhTPVR', crm_contact_fid: null, crm_owner_fid: '00G2v000004WYXaEAO', crm_account_fid: null, - salesforce_fid: null, - crm_last_sync: null, - crm_url: null, + salesforce_fid: '00Q6r000002LKhTPVR', + crm_last_sync: '2022-01-21 18:47:37', + crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', is_do_not_email: null, is_do_not_call: null, opted_out: null, @@ -303,15 +237,32 @@ export const networkCallsData = [ statusText: 'OK', }, }, - // 3rd proxy test-case { httpReq: { - url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/fid/00Q6r000002LKhTPVR', + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@mywebsite.io', + method: 'POST', + }, + httpRes: { + data: { + '@attributes': { + stat: 'fail', + version: 1, + err_code: 184, + }, + err: 'access_token is invalid, unknown, or malformed: Inactive token', + }, + status: 401, + statusText: 'Unauthorized', + }, + }, + { + httpReq: { + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/id/123435', data: getFormData({ - first_name: 'Nick', - last_name: 'Kyrgios', + first_name: 'Roger12', + last_name: 'Federer12', website: 'https://rudderstack.com', - score: 12, + score: 14, campaign_id: 42213, format: 'json', }).toString(), @@ -334,9 +285,9 @@ export const networkCallsData = [ id: 123435, campaign_id: 42213, salutation: null, - first_name: 'Roger_12', - last_name: 'Federer_12', - email: 'Roger_12@federer.io', + first_name: 'Roger12', + last_name: 'Federer12', + email: 'Roger12@waltair.io', password: null, company: null, website: 'https://rudderstack.com', @@ -362,13 +313,13 @@ export const networkCallsData = [ grade: null, last_activity_at: null, recent_interaction: 'Never active.', - crm_lead_fid: '00Q6r000002LKhTPVR', + crm_lead_fid: null, crm_contact_fid: null, crm_owner_fid: '00G2v000004WYXaEAO', crm_account_fid: null, - salesforce_fid: '00Q6r000002LKhTPVR', - crm_last_sync: '2022-01-21 18:47:37', - crm_url: 'https://testcompany.my.salesforce.com/00Q6r000002LKhTPVR', + salesforce_fid: null, + crm_last_sync: null, + crm_url: null, is_do_not_email: null, is_do_not_call: null, opted_out: null, @@ -437,4 +388,22 @@ export const networkCallsData = [ statusText: 'OK', }, }, + { + httpReq: { + url: 'https://pi.pardot.com/api/prospect/version/4/do/upsert/email/rolex_waltair@test.com', + method: 'POST', + }, + httpRes: { + data: { + '@attributes': { + stat: 'fail', + version: 1, + err_code: 120, + }, + err: 'Unable to verify Salesforce connector', + }, + status: 500, + statusText: 'Invalid action', + }, + }, ]; diff --git a/test/integrations/destinations/pinterest_tag/processor/data.ts b/test/integrations/destinations/pinterest_tag/processor/data.ts index 65b33e7740..48b624645f 100644 --- a/test/integrations/destinations/pinterest_tag/processor/data.ts +++ b/test/integrations/destinations/pinterest_tag/processor/data.ts @@ -19,8 +19,8 @@ export const data = [ userAgent: 'chrome', traits: { anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', - email: 'abc@gmail.com', - phone: '+1234589947', + email: ' abc@gmail.com', + phone: '+1234589947 ', gender: 'non-binary', db: '19950715', lastname: 'Rudderlabs', diff --git a/test/integrations/destinations/rakuten/processor/commonConfig.ts b/test/integrations/destinations/rakuten/processor/commonConfig.ts index e7e2af7fbd..96bc6669c2 100644 --- a/test/integrations/destinations/rakuten/processor/commonConfig.ts +++ b/test/integrations/destinations/rakuten/processor/commonConfig.ts @@ -63,3 +63,27 @@ export const commonProperties = { storeId: '12345', storecat: 'Electronics', }; +export const commonPropertiesWithoutRansiteID = { + orderId: 'SampleOrderId', + landTime: '20240129_1200', + date: '20240129_1300', + altord: 'SampleAlternateOrderId', + currency: 'INR', + creditCardType: 'Visa', + commReason: 'SampleCommReason', + isComm: 'Y', + consumed: '20240129_1400', + coupon: 'SampleCoupon', + custId: 'SampleCustomerId', + custScore: 'A', + custStatus: 'New', + dId: 'SampleDeviceId', + disamt: '50.00', + ordStatus: 'Pending', + segment: 'SampleSegment', + shipcountry: 'USA', + shipped: '20240129_1500', + sitename: 'SampleSiteName', + storeId: '12345', + storecat: 'Electronics', +}; diff --git a/test/integrations/destinations/rakuten/processor/track.ts b/test/integrations/destinations/rakuten/processor/track.ts index 49b26e4658..74d09b8d4c 100644 --- a/test/integrations/destinations/rakuten/processor/track.ts +++ b/test/integrations/destinations/rakuten/processor/track.ts @@ -4,6 +4,7 @@ import { commonProperties, endpoint, singleProductWithAllProperties, + commonPropertiesWithoutRansiteID, } from './commonConfig'; import { transformResultBuilder } from '../../../testUtils'; export const trackSuccess = [ @@ -449,4 +450,129 @@ export const trackSuccess = [ }, }, }, + { + id: 'rakuten-test-track-success-5', + name: 'rakuten', + description: 'Track call no ranSiteId in input', + scenario: 'Business', + successCriteria: + 'Response should contain only properties with tr as empty string (ransiteId) and product payload and status code should be 200', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'product purchased', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + channel: 'mobile', + rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', + messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', + properties: { + ...commonPropertiesWithoutRansiteID, + products: [ + { ...singleProductWithAllProperties }, + { + sku: 'custom sku 1', + quantity: 5, + amount: 25, + name: 'name_1', + }, + { + sku: 'custom sku 2', + name: 'SampleProduct', + quantity: 1, + amount: 30, + coupon: 'SALE50', + }, + ], + }, + anonymousId: '9c6bd77ea9da3e68', + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + metadata: { + destinationId: 'dummyDestId', + jobId: '1', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + destinationId: 'dummyDestId', + jobId: '1', + }, + output: transformResultBuilder({ + method: 'GET', + endpoint, + headers: commonOutputHeaders, + params: { + mid: 'dummyMarketingId', + xml: 1, + source: 'rudderstack', + amtlist: '2000|2500|3000', + brandlist: 'SampleBrand||', + catidlist: '12345||', + catlist: 'Electronics||', + couponlist: 'SALE20||SALE50', + disamtlist: '10.5||', + distypelist: 'Percentage||', + ismarketplacelist: 'N||', + sequencelist: '123||', + shipbylist: 'Express||', + shipidlist: 'SHIP123||', + qlist: '5|5|1', + marginlist: '0.15||', + markdownlist: '5||', + taxexemptlist: 'N||', + namelist: 'SampleProduct|name_1|SampleProduct', + skulist: 'ABC123|custom sku 1|custom sku 2', + issalelist: 'Y||', + itmstatuslist: 'In Stock||', + isclearancelist: 'Y||', + ord: 'SampleOrderId', + tr: ' ', + land: '20240129_1200', + date: '20240129_1300', + altord: 'SampleAlternateOrderId', + cur: 'INR', + cc: 'Visa', + commreason: 'SampleCommReason', + iscomm: 'Y', + consumed: '20240129_1400', + coupon: 'SampleCoupon', + custid: 'SampleCustomerId', + custscore: 'A', + custstatus: 'New', + did: 'SampleDeviceId', + disamt: '50.00', + ordstatus: 'Pending', + segment: 'SampleSegment', + shipcountry: 'USA', + shipped: '20240129_1500', + sitename: 'SampleSiteName', + storeid: '12345', + storecat: 'Electronics', + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/rakuten/processor/transformationFailure.ts b/test/integrations/destinations/rakuten/processor/transformationFailure.ts index e35ab26b69..5485efae03 100644 --- a/test/integrations/destinations/rakuten/processor/transformationFailure.ts +++ b/test/integrations/destinations/rakuten/processor/transformationFailure.ts @@ -202,9 +202,9 @@ export const transformationFailures = [ { id: 'rakuten-test-5', name: 'rakuten', - description: 'No eligible property available for required field tr present', + description: 'No eligible property available for required field land present', scenario: 'Framework', - successCriteria: 'Transformationn Error for required field tr not present', + successCriteria: 'Transformationn Error for required field land not present', feature: 'processor', module: 'destination', version: 'v0', @@ -245,7 +245,7 @@ export const transformationFailures = [ body: [ { error: - 'Missing required value from ["properties.tr","properties.ran_site_id","properties.ranSiteID"]: Workflow: procWorkflow, Step: prepareTrackPayload, ChildStep: undefined, OriginalError: Missing required value from ["properties.tr","properties.ran_site_id","properties.ranSiteID"]', + 'Missing required value from ["properties.land","properties.land_time","properties.landTime"]: Workflow: procWorkflow, Step: prepareTrackPayload, ChildStep: undefined, OriginalError: Missing required value from ["properties.land","properties.land_time","properties.landTime"]', metadata: { destinationId: 'dummyDestId', jobId: '1', diff --git a/test/integrations/destinations/reddit/processor/data.ts b/test/integrations/destinations/reddit/processor/data.ts index 49e0cd2baa..e7b36f56ff 100644 --- a/test/integrations/destinations/reddit/processor/data.ts +++ b/test/integrations/destinations/reddit/processor/data.ts @@ -12,13 +12,13 @@ export const data = [ message: { context: { traits: { - email: 'testone@gmail.com', + email: 'testone@gmail.com ', }, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - ip: '54.100.200.255', + ip: ' 54.100.200.255', device: { - advertisingId: 'asfds7fdsihf734b34j43f', + advertisingId: ' asfds7fdsihf734b34j43f', }, os: { name: 'android', @@ -29,7 +29,7 @@ export const data = [ originalTimestamp: '2019-10-14T09:03:17.562Z', anonymousId: '123456', event: 'Order Completed', - userId: 'testuserId1', + userId: ' testuserId1', properties: { checkout_id: '12345', order_id: '1234', @@ -443,6 +443,8 @@ export const data = [ }, event_metadata: { item_count: 0, + value: 2600, + value_decimal: 26, products: [ { id: '017c6f5d5cf86a4b22432066', @@ -581,6 +583,8 @@ export const data = [ }, event_metadata: { item_count: 5, + value: 24995, + value_decimal: 249.95, products: [ { id: '622c6f5d5cf86a4c77358033', @@ -847,6 +851,8 @@ export const data = [ }, event_metadata: { item_count: 5, + value: 24995, + value_decimal: 249.95, products: [ { id: '622c6f5d5cf86a4c77358033', diff --git a/test/integrations/destinations/reddit/router/data.ts b/test/integrations/destinations/reddit/router/data.ts index 723afff374..b5bed48ae7 100644 --- a/test/integrations/destinations/reddit/router/data.ts +++ b/test/integrations/destinations/reddit/router/data.ts @@ -90,6 +90,8 @@ export const data = [ properties: { list_id: 'list1', category: "What's New", + value: 2600, + value_decimal: 26, products: [ { product_id: '017c6f5d5cf86a4b22432066', @@ -230,6 +232,8 @@ export const data = [ }, event_metadata: { item_count: 0, + value: 2600, + value_decimal: 26, products: [ { id: '017c6f5d5cf86a4b22432066', @@ -259,6 +263,8 @@ export const data = [ }, event_metadata: { item_count: 5, + value: 24995, + value_decimal: 249.95, products: [ { id: '622c6f5d5cf86a4c77358033', @@ -349,6 +355,8 @@ export const data = [ properties: { list_id: 'list1', category: "What's New", + value: 2600, + value_decimal: 26, products: [ { product_id: '017c6f5d5cf86a4b22432066', diff --git a/test/integrations/destinations/redis/processor/data.ts b/test/integrations/destinations/redis/processor/data.ts index 72c78a1ad4..999a954b59 100644 --- a/test/integrations/destinations/redis/processor/data.ts +++ b/test/integrations/destinations/redis/processor/data.ts @@ -1,3 +1,207 @@ +import { RedisTc, SupportedEventTypes, TraitsLocation } from '../types'; + +const getRedisTestCase = (tc: RedisTc) => { + const context = { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.5', + }, + ip: '0.0.0.0', + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.5', + }, + locale: 'en-GB', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + traits: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36', + }; + return { + name: 'redis', + description: tc.description, + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + metadata: { + workspaceId: tc.workspaceId || 'wspId', + }, + destination: { + Config: { + address: 'localhost:6379', + database: 'test', + prefix: ' ', + ...tc.destinationConfig, + }, + DestinationDefinition: { + DisplayName: 'Redis', + ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie', + Name: 'REDIS', + }, + Enabled: true, + ID: tc.destId || '1WhcOCGgj9asZu850HvugU2C3Aq', + Name: 'Redis', + + Transformations: [], + }, + message: { + anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca', + channel: 'web', + ...(tc.traitsLocation === TraitsLocation.Traits ? { traits: tc.traits } : {}), + context: { + ...context, + ...(tc.traitsLocation === TraitsLocation.ContextTraits + ? { traits: tc.traits } + : {}), + ...(tc.profilesContext ?? {}), + }, + integrations: { + All: true, + }, + messageId: '2536eda4-d638-4c93-8014-8ffe3f083214', + originalTimestamp: '2020-01-24T06:29:02.362Z', + receivedAt: '2020-01-24T11:59:02.403+05:30', + request_ip: '[::1]:53708', + sentAt: '2020-01-24T06:29:02.363Z', + timestamp: '2020-01-24T11:59:02.402+05:30', + type: tc.eventType, + userId: tc.userId || 'myUserId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + workspaceId: tc.workspaceId || 'wspId', + }, + output: tc.expectedResponse, + statusCode: tc.expectedStatusCode, + }, + ], + }, + }, + }; +}; + +const jsonTestCases: RedisTc[] = [ + { + description: + 'Test 6: [Event-stream] when useJSONModule is enabled with traits in context.traits, should transform successfully', + destinationConfig: { + useJSONModule: true, + }, + traitsLocation: TraitsLocation.ContextTraits, + traits: { + prop1: 'k', + prop2: 2, + prop3: { + p4: 'a', + }, + }, + eventType: SupportedEventTypes.Identify, + userId: 'user-1', + expectedResponse: { + message: { + key: 'user:user-1', + value: { + prop1: 'k', + prop2: 2, + prop3: { + p4: 'a', + }, + }, + }, + userId: 'user-1', + }, + expectedStatusCode: 200, + }, + { + description: + 'Test 7: [Event-stream] when useJSONModule is enabled with traits in traits, should transform successfully', + destinationConfig: { + useJSONModule: true, + }, + traitsLocation: TraitsLocation.Traits, + traits: { + prop1: 'k', + prop2: 2, + prop3: { + p4: 'a', + }, + }, + eventType: SupportedEventTypes.Identify, + userId: 'user-1', + expectedResponse: { + message: { + key: 'user:user-1', + value: { + prop1: 'k', + prop2: 2, + prop3: { + p4: 'a', + }, + }, + }, + userId: 'user-1', + }, + expectedStatusCode: 200, + }, + { + description: + 'Test 8: [Profiles] when useJSONModule is enabled with traits in traits, should transform successfully', + destinationConfig: { + useJSONModule: true, + }, + destId: 'd1', + workspaceId: 'w1', + traitsLocation: TraitsLocation.Traits, + traits: { + field: 'k', + model_name: 'analytics_app', + }, + profilesContext: { + sources: { + profiles_entity: 'e1', + profiles_model: 'model1', + profiles_id_type: 'id-1', + }, + }, + eventType: SupportedEventTypes.Identify, + userId: 'user-2', + expectedResponse: { + message: { + key: 'w1:d1:e1:id-1:user-2', + path: 'model1', + value: { + field: 'k', + model_name: 'analytics_app', + }, + }, + userId: 'user-2', + }, + expectedStatusCode: 200, + }, +]; + +const jsonTcs = jsonTestCases.map((tc) => getRedisTestCase(tc)); + export const data = [ { name: 'redis', @@ -658,4 +862,5 @@ export const data = [ }, }, }, + ...jsonTcs, ]; diff --git a/test/integrations/destinations/redis/types.ts b/test/integrations/destinations/redis/types.ts new file mode 100644 index 0000000000..f4970b3afa --- /dev/null +++ b/test/integrations/destinations/redis/types.ts @@ -0,0 +1,39 @@ +export type RedisConfig = Partial<{ + address: string; + database: string; + prefix: string; + useJSONModule: boolean; +}>; + +type RedisJSONOutput = { + message: Record; + userId: string; +}; + +export enum TraitsLocation { + ContextTraits = 'context.traits', + Traits = 'traits', +} +export enum SupportedEventTypes { + Identify = 'identify', +} + +export type RedisTc = { + description: string; + destId?: string; + workspaceId?: string; + destinationConfig: RedisConfig; + traitsLocation: TraitsLocation; + traits: Record; + eventType: SupportedEventTypes; + userId?: string; + expectedResponse: RedisJSONOutput; + expectedStatusCode: number; + profilesContext?: { + sources: { + profiles_entity: string; + profiles_id_type: string; + profiles_model: string; + }; + }; +}; diff --git a/test/integrations/destinations/refiner/processor/data.ts b/test/integrations/destinations/refiner/processor/data.ts index c851e9d7ce..b09ea234db 100644 --- a/test/integrations/destinations/refiner/processor/data.ts +++ b/test/integrations/destinations/refiner/processor/data.ts @@ -1,668 +1,13 @@ +import { identifyTestData } from './identifyTestData'; +import { trackTestData } from './trackTestData'; +import { pageTestData } from './pageTestData'; +import { groupTestData } from './groupTestData'; +import { validationTestData } from './validationTestData'; + export const data = [ - { - name: 'refiner', - description: 'No Message type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - sentAt: '2022-10-11T13:10:54.877+05:30', - userId: 'user@45', - context: { - traits: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - address: { city: 'ahmedabad', state: 'india' }, - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: 'b6ce7f31-5d76-4240-94d2-3eea020ef791', - timestamp: '2022-10-11T13:10:52.137+05:30', - receivedAt: '2022-10-11T13:10:52.138+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:10:54.877+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665474171943, - eventFilteringOption: 'disable', - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event type is required', - statTags: { - destType: 'REFINER', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'Unsupported Event type ', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'alias', - sentAt: '2022-10-11T13:10:54.877+05:30', - userId: 'user@45', - context: { - traits: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - address: { city: 'ahmedabad', state: 'india' }, - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: 'b6ce7f31-5d76-4240-94d2-3eea020ef791', - timestamp: '2022-10-11T13:10:52.137+05:30', - receivedAt: '2022-10-11T13:10:52.138+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:10:54.877+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665474171943, - eventFilteringOption: 'disable', - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event type "alias" is not supported', - statTags: { - destType: 'REFINER', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'userId and email is not present', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'identify', - sentAt: '2022-10-11T13:10:54.877+05:30', - context: { - traits: { - age: '30', - city: 'Banglore', - phone: '9876543210', - address: { city: 'ahmedabad', state: 'india' }, - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: 'b6ce7f31-5d76-4240-94d2-3eea020ef791', - timestamp: '2022-10-11T13:10:52.137+05:30', - receivedAt: '2022-10-11T13:10:52.138+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:10:54.877+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665474171943, - eventFilteringOption: 'disable', - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'At least one of `userId` or `email` is required', - statTags: { - destType: 'REFINER', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'event name is not present', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.1.2', - }, - traits: { - age: '30', - email: 'test@user.com', - phone: '9876543210', - city: 'Banglore', - userCountry: 'india', - lastName: 'user', - username: 'testUser', - firstName: 'test', - }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.1.2' }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36', - locale: 'en-GB', - os: { name: '', version: '' }, - screen: { density: 2 }, - page: { - path: '/tests/html/ecomm_test.html', - referrer: 'http://0.0.0.0:1112/tests/html/', - search: '', - title: 'Fb Offline Conversion Ecommerce Test', - url: 'http://0.0.0.0:1112/tests/html/ecomm_test.html', - }, - }, - type: 'track', - messageId: '9116b734-7e6b-4497-ab51-c16744d4487e', - userId: 'user@45', - properties: { - order_id: '5241735', - coupon: 'APPARELSALE', - currency: 'IND', - products: [ - { id: 'product-bacon-jam', category: 'Merch', brand: '' }, - { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, - { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, - ], - }, - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665474171943, - eventFilteringOption: 'disable', - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event name is required', - statTags: { - destType: 'REFINER', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'successful identify call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'identify', - sentAt: '2022-10-11T13:10:54.877+05:30', - userId: 'user@45', - context: { - traits: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - address: { city: 'ahmedabad', state: 'india' }, - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: 'b6ce7f31-5d76-4240-94d2-3eea020ef791', - timestamp: '2022-10-11T13:10:52.137+05:30', - receivedAt: '2022-10-11T13:10:52.138+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:10:54.877+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665475307930, - eventFilteringOption: 'disable', - userAttributesMapping: [{ from: 'address', to: 'userAddress' }], - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.refiner.io/v1/identify-user', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: 'Bearer dummyApiKey', - }, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - userId: 'user@45', - lastName: 'user', - username: 'testUser', - firstName: 'test', - userAddress: { city: 'ahmedabad', state: 'india' }, - userCountry: 'india', - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'successful track call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'track', - event: 'Product Searched', - sentAt: '2022-10-11T13:38:31.827+05:30', - userId: 'user@45', - channel: 'web', - context: { - os: { name: '', version: '' }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.2', - namespace: 'com.rudderlabs.javascript', - }, - page: { - url: 'http://0.0.0.0:1112/tests/html/ecomm_test.html', - path: '/tests/html/ecomm_test.html', - title: 'Fb Offline Conversion Ecommerce Test', - search: '', - referrer: 'http://0.0.0.0:1112/tests/html/', - }, - locale: 'en-GB', - screen: { density: 2 }, - traits: { - age: '30', - city: 'Banglore', - email: 'test@user.com', - phone: '9876543210', - lastName: 'user', - username: 'testUser', - firstName: 'test', - userCountry: 'india', - }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.1.2' }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36', - }, - rudderId: 'caae04c5-959f-467b-a293-86f6c62d59e6', - messageId: '9116b734-7e6b-4497-ab51-c16744d4487e', - timestamp: '2022-10-11T13:38:29.177+05:30', - properties: { - coupon: 'APPARELSALE', - currency: 'IND', - order_id: '5241735', - products: [ - { id: 'product-bacon-jam', category: 'Merch', brand: '' }, - { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, - { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, - ], - }, - receivedAt: '2022-10-11T13:38:29.178+05:30', - request_ip: '[::1]', - originalTimestamp: '2022-10-11T13:38:31.827+05:30', - }, - destination: { - Config: { - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665475307930, - eventFilteringOption: 'disable', - userAttributesMapping: [{ from: 'address', to: 'userAddress' }], - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: { id: 'user@45', email: 'test@user.com', event: 'Product Searched' }, - JSON: {}, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: 'Bearer dummyApiKey', - }, - version: '1', - endpoint: 'https://api.refiner.io/v1/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'successful group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - type: 'group', - sentAt: '2015-02-23T22:28:55.111Z', - traits: { name: 'rudder ventures', email: 'business@rudderstack.com' }, - userId: 'test@12', - channel: 'browser', - context: { - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36', - }, - groupId: 'group@123', - rudderId: 'd944be7a-c870-41ba-9fa5-f3c9dbf5f7e0', - messageId: '022bb90c-bbac-11e4-8dfc-aa07a5b093db', - request_ip: '[::1]', - integrations: { All: true }, - originalTimestamp: '2022-10-11T13:51:00.906+05:30', - }, - destination: { - Config: { - accountAttributesMapping: [{ from: 'email', to: 'businessEmail' }], - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665476456112, - eventFilteringOption: 'disable', - userAttributesMapping: [{ from: 'address', to: 'userAddress' }], - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.refiner.io/v1/identify-user', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: 'Bearer dummyApiKey', - }, - params: {}, - body: { - JSON: {}, - JSON_ARRAY: {}, - XML: {}, - FORM: { - id: 'test@12', - 'account[businessEmail]': 'business@rudderstack.com', - 'account[id]': 'group@123', - 'account[name]': 'rudder ventures', - }, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'refiner', - description: 'Refiner page call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - message: { - channel: 'web', - context: { - app: { - build: '1.0.0', - name: 'RudderLabs JavaScript SDK', - namespace: 'com.rudderlabs.javascript', - version: '1.0.0', - }, - library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', - locale: 'en-US', - ip: '0.0.0.0', - os: { name: '', version: '' }, - screen: { density: 2 }, - }, - type: 'page', - messageId: '5e10d13a-bf9a-44bf-b884-43a9e591ea71', - timestamp: '2019-09-01T15:46:51.693229+05:30', - anonymousId: '00000000000000000000000000', - userId: '12345', - properties: { - path: '/abc', - referrer: 'xyz', - search: 'def', - title: 'ghi', - url: 'jkl', - }, - integrations: { All: true }, - name: 'pageviewed', - sentAt: '2019-10-14T11:15:53.296Z', - }, - destination: { - Config: { - accountAttributesMapping: [{ from: 'email', to: 'businessEmail' }], - apiKey: 'dummyApiKey', - blacklistedEvents: [{ eventName: '' }], - eventDelivery: true, - eventDeliveryTS: 1665476456112, - eventFilteringOption: 'disable', - userAttributesMapping: [{ from: 'address', to: 'userAddress' }], - whitelistedEvents: [{ eventName: '' }], - }, - }, - }, - ], - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - XML: {}, - FORM: { id: '12345', event: 'Viewed pageviewed Page' }, - JSON: {}, - JSON_ARRAY: {}, - }, - type: 'REST', - files: {}, - method: 'POST', - params: {}, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: 'Bearer dummyApiKey', - }, - version: '1', - endpoint: 'https://api.refiner.io/v1/track', - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyTestData, + ...trackTestData, + ...pageTestData, + ...groupTestData, + ...validationTestData, ]; diff --git a/test/integrations/destinations/refiner/processor/groupTestData.ts b/test/integrations/destinations/refiner/processor/groupTestData.ts new file mode 100644 index 0000000000..e3a05357f5 --- /dev/null +++ b/test/integrations/destinations/refiner/processor/groupTestData.ts @@ -0,0 +1,91 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedGroupPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const headers = { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/x-www-form-urlencoded', +}; + +const endpoint = 'https://api.refiner.io/v1/identify-user'; + +export const groupTestData: ProcessorTestData[] = [ + { + id: 'refiner-group-test-1', + name: 'refiner', + description: 'Successful group call to create account and add user to account', + scenario: 'Framework', + successCriteria: 'Response status code should be 200 and it should contain all account traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedGroupPayload({ + userId: 'test@12', + groupId: 'group@123', + traits: { name: 'rudder ventures', email: 'business@rudderstack.com' }, + context: { + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36', + }, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + FORM: { + id: 'test@12', + 'account[email]': 'business@rudderstack.com', + 'account[id]': 'group@123', + 'account[name]': 'rudder ventures', + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/refiner/processor/identifyTestData.ts b/test/integrations/destinations/refiner/processor/identifyTestData.ts new file mode 100644 index 0000000000..cc3704bb45 --- /dev/null +++ b/test/integrations/destinations/refiner/processor/identifyTestData.ts @@ -0,0 +1,114 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + generateSimplifiedIdentifyPayload, + transformResultBuilder, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const userTraits = { + age: '30', + city: 'Banglore', + email: 'test@user.com', + phone: '9876543210', + address: { city: 'ahmedabad', state: 'india' }, + lastName: 'user', + username: 'testUser', + firstName: 'test', + userCountry: 'india', +}; + +const expectedOutputUserTraits = { + address: { + city: 'ahmedabad', + state: 'india', + }, + age: '30', + city: 'Banglore', + email: 'test@user.com', + firstName: 'test', + lastName: 'user', + phone: '9876543210', + userCountry: 'india', + userId: 'user@45', + username: 'testUser', +}; + +const headers = { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/x-www-form-urlencoded', +}; + +const endpoint = 'https://api.refiner.io/v1/identify-user'; + +export const identifyTestData: ProcessorTestData[] = [ + { + id: 'refiner-identify-test-1', + name: 'refiner', + description: 'Successful identify call to create user', + scenario: 'Framework', + successCriteria: + 'Response status code should be 200 and it should contain all user standard and custom traits', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedIdentifyPayload({ + userId: 'user@45', + context: { + traits: userTraits, + }, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + FORM: { + ...expectedOutputUserTraits, + }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/refiner/processor/pageTestData.ts b/test/integrations/destinations/refiner/processor/pageTestData.ts new file mode 100644 index 0000000000..b6c782202e --- /dev/null +++ b/test/integrations/destinations/refiner/processor/pageTestData.ts @@ -0,0 +1,99 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedPageOrScreenPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + userAttributesMapping: [{ from: 'address', to: 'userAddress' }], + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const properties = { + order_id: '5241735', + coupon: 'APPARELSALE', + currency: 'IND', + products: [ + { id: 'product-bacon-jam', category: 'Merch', brand: '' }, + { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, + { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, + ], +}; + +const headers = { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/x-www-form-urlencoded', +}; + +const endpoint = 'https://api.refiner.io/v1/track'; + +export const pageTestData: ProcessorTestData[] = [ + { + id: 'refiner-track-test-1', + name: 'refiner', + description: 'Successful page call', + scenario: 'Framework', + successCriteria: + 'Response status code should be 200 and response should contain respective page name', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedPageOrScreenPayload({ + type: 'page', + userId: '12345', + context: { + traits: {}, + }, + name: 'pageviewed', + properties, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + FORM: { id: '12345', event: 'Viewed pageviewed Page' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/refiner/processor/trackTestData.ts b/test/integrations/destinations/refiner/processor/trackTestData.ts new file mode 100644 index 0000000000..e10ac6f0f5 --- /dev/null +++ b/test/integrations/destinations/refiner/processor/trackTestData.ts @@ -0,0 +1,109 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { + generateMetadata, + transformResultBuilder, + generateSimplifiedTrackPayload, +} from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + userAttributesMapping: [{ from: 'address', to: 'userAddress' }], + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const userTraits = { + age: '30', + city: 'Banglore', + email: 'test@user.com', + phone: '9876543210', + address: { city: 'ahmedabad', state: 'india' }, + lastName: 'user', + username: 'testUser', + firstName: 'test', + userCountry: 'india', +}; + +const properties = { + order_id: '5241735', + coupon: 'APPARELSALE', + currency: 'IND', + products: [ + { id: 'product-bacon-jam', category: 'Merch', brand: '' }, + { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, + { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, + ], +}; + +const headers = { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/x-www-form-urlencoded', +}; + +const endpoint = 'https://api.refiner.io/v1/track'; + +export const trackTestData: ProcessorTestData[] = [ + { + id: 'refiner-track-test-1', + name: 'refiner', + description: 'Track event call for Product Searched event', + scenario: 'Framework', + successCriteria: 'Response status code should be 200 and event name should be Product Searched', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + userId: 'user@45', + event: 'Product Searched', + context: { + traits: userTraits, + }, + properties, + }), + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint, + headers, + FORM: { id: 'user@45', email: 'test@user.com', event: 'Product Searched' }, + }), + statusCode: 200, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/refiner/processor/validationTestData.ts b/test/integrations/destinations/refiner/processor/validationTestData.ts new file mode 100644 index 0000000000..20a9694fdf --- /dev/null +++ b/test/integrations/destinations/refiner/processor/validationTestData.ts @@ -0,0 +1,227 @@ +import { Destination } from '../../../../../src/types'; +import { ProcessorTestData } from '../../../testTypes'; +import { generateMetadata } from '../../../testUtils'; + +const destination: Destination = { + ID: '123', + Name: 'refiner', + DestinationDefinition: { + ID: '123', + Name: 'refiner', + DisplayName: 'Refiner', + Config: {}, + }, + Config: { + apiKey: 'dummyApiKey', + blacklistedEvents: [{ eventName: '' }], + eventDelivery: true, + eventDeliveryTS: 1665474171943, + eventFilteringOption: 'disable', + whitelistedEvents: [{ eventName: '' }], + }, + Enabled: true, + WorkspaceID: '123', + Transformations: [], +}; + +const userTraits = { + age: '30', + city: 'Banglore', + email: 'test@user.com', + phone: '9876543210', + address: { city: 'ahmedabad', state: 'india' }, + lastName: 'user', + username: 'testUser', + firstName: 'test', + userCountry: 'india', +}; + +const properties = { + order_id: '5241735', + coupon: 'APPARELSALE', + currency: 'IND', + products: [ + { id: 'product-bacon-jam', category: 'Merch', brand: '' }, + { id: 'product-t-shirt', category: 'Merch', brand: 'Levis' }, + { id: 'offer-t-shirt', category: 'Merch', brand: 'Levis' }, + ], +}; + +const expectedStatTags = { + destType: 'REFINER', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; + +export const validationTestData: ProcessorTestData[] = [ + { + id: 'refiner-validation-test-1', + name: 'refiner', + description: '[Error]: Check for no message type', + scenario: 'Framework', + successCriteria: 'Response status code should be 400 with respective error message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'user@45', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event type is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'refiner-validation-test-2', + name: 'refiner', + description: 'Error]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by Klaviyo destination and the error message should be Event type alias is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + userId: 'user@45', + type: 'alias', + context: { + traits: userTraits, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event type "alias" is not supported', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'refiner-validation-test-3', + name: 'refiner', + description: 'Error]: userId and email is not present in payload', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400, it should throw instrumetation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'identify', + context: { + traits: {}, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'At least one of `userId` or `email` is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, + { + id: 'refiner-validation-test-4', + name: 'refiner', + description: 'Error]: event name is not present in payload', + scenario: 'Framework', + successCriteria: + 'Response status code should be 400, it should throw instrumetation error with respective message', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + context: { + traits: userTraits, + }, + properties, + timestamp: '2020-01-21T00:21:34.208Z', + }, + metadata: generateMetadata(1), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event name is required', + statTags: expectedStatTags, + statusCode: 400, + metadata: generateMetadata(1), + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/salesforce/dataDelivery/business.ts b/test/integrations/destinations/salesforce/dataDelivery/business.ts new file mode 100644 index 0000000000..4e98a3fc1a --- /dev/null +++ b/test/integrations/destinations/salesforce/dataDelivery/business.ts @@ -0,0 +1,380 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer token', + 'Content-Type': 'application/json', +}; +const params = { destination: 'salesforce' }; + +const users = [ + { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', + }, +]; + +const statTags = { + aborted: { + destType: 'SALESFORCE', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + retryable: { + destType: 'SALESFORCE', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, + throttled: { + destType: 'SALESFORCE', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'throttled', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +export const proxyMetdata: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: {}, + dontBatch: false, +}; + +export const reqMetadataArray = [proxyMetdata]; + +const commonRequestParameters = { + headers: commonHeaders, + JSON: users[0], + params, +}; + +const externalIdSearchData = { Planning_Categories__c: 'pc', External_ID__c: 123 }; +export const externalIDSearchedData = { + headers: commonHeaders, + JSON: externalIdSearchData, + params, +}; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'salesforce_v1_scenario_1', + name: 'salesforce', + description: + '[Proxy v1 API] :: Test for a valid request - Lead creation with existing unchanged leadId and unchanged data', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request for destination: salesforce Processed Successfully', + response: [ + { + error: '{"statusText":"No Content"}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_2', + name: 'salesforce', + description: '[Proxy v1 API] :: Test with session expired scenario', + successCriteria: 'Should return 5XX with error Session expired or invalid, INVALID_SESSION_ID', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/invalid_session_id', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 500, + message: + 'Salesforce Request Failed - due to "Session expired or invalid", (Retryable) during Salesforce Response Handling', + response: [ + { + error: + '[{"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"}]', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + statTags: statTags.retryable, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_3', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for Invalid Auth token passed in header', + successCriteria: 'Should return 401 INVALID_AUTH_HEADER', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/2', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 400, + message: + 'Salesforce Request Failed: "401" due to "INVALID_HEADER_TYPE", (Aborted) during Salesforce Response Handling', + response: [ + { + error: '[{"message":"INVALID_HEADER_TYPE","errorCode":"INVALID_AUTH_HEADER"}]', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + statTags: statTags.aborted, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_4', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for rate limit exceeded scenario', + successCriteria: 'Should return 429 with error message "Request limit exceeded"', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/4', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Salesforce Request Failed - due to "REQUEST_LIMIT_EXCEEDED", (Throttled) during Salesforce Response Handling', + response: [ + { + error: + '[{"message":"Request limit exceeded","errorCode":"REQUEST_LIMIT_EXCEEDED"}]', + metadata: proxyMetdata, + statusCode: 429, + }, + ], + statTags: statTags.throttled, + status: 429, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_5', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for server unavailable scenario', + successCriteria: 'Should return 500 with error message "Server Unavailable"', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/5', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Salesforce Request Failed - due to "Server Unavailable", (Retryable) during Salesforce Response Handling', + response: [ + { + error: '[{"message":"Server Unavailable","errorCode":"SERVER_UNAVAILABLE"}]', + metadata: proxyMetdata, + statusCode: 500, + }, + ], + statTags: statTags.retryable, + status: 500, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_6', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for invalid grant scenario due to authentication failure', + successCriteria: + 'Should return 400 with error message "invalid_grant" due to "authentication failure"', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/6', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Salesforce Request Failed: "400" due to "{"error":"invalid_grant","error_description":"authentication failure"}", (Aborted) during Salesforce Response Handling', + response: [ + { + error: '{"error":"invalid_grant","error_description":"authentication failure"}', + metadata: proxyMetdata, + statusCode: 400, + }, + ], + statTags: statTags.aborted, + status: 400, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_7', + name: 'salesforce', + description: '[Proxy v1 API] :: Test for a valid request - External ID search', + successCriteria: 'Should return 200 with list of matching records with External ID', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...externalIDSearchedData, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/parameterizedSearch/?q=123&sobject=object_name&in=External_ID__c&object_name.fields=id,External_ID__c', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request for destination: salesforce Processed Successfully', + response: [ + { + error: + '{"searchRecords":[{"attributes":{"type":"object_name","url":"/services/data/v50.0/sobjects/object_name/a0J75100002w97gEAA"},"Id":"a0J75100002w97gEAA","External_ID__c":"external_id"},{"attributes":{"type":"object_name","url":"/services/data/v50.0/sobjects/object_name/a0J75200002w9ZsEAI"},"Id":"a0J75200002w9ZsEAI","External_ID__c":"external_id TEST"}]}', + metadata: proxyMetdata, + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/salesforce/dataDelivery/data.ts b/test/integrations/destinations/salesforce/dataDelivery/data.ts index cfaa75e23e..d376289d97 100644 --- a/test/integrations/destinations/salesforce/dataDelivery/data.ts +++ b/test/integrations/destinations/salesforce/dataDelivery/data.ts @@ -1,7 +1,18 @@ import { AxiosError } from 'axios'; import MockAdapter from 'axios-mock-adapter'; +import { testScenariosForV1API } from './business'; +import { otherSalesforceScenariosV1 } from './other'; -export const data = [ +const legacyDataValue = { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', +}; + +const legacyTests = [ { name: 'salesforce', description: 'Test 0', @@ -24,14 +35,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -86,14 +90,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -162,14 +159,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -238,14 +228,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -314,14 +297,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -390,14 +366,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -464,14 +433,7 @@ export const data = [ body: { XML: {}, FORM: {}, - JSON: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + JSON: legacyDataValue, JSON_ARRAY: {}, }, metadata: { @@ -781,3 +743,4 @@ export const data = [ }, }, ]; +export const data = [...legacyTests, ...testScenariosForV1API, ...otherSalesforceScenariosV1]; diff --git a/test/integrations/destinations/salesforce/dataDelivery/other.ts b/test/integrations/destinations/salesforce/dataDelivery/other.ts new file mode 100644 index 0000000000..b3361caba7 --- /dev/null +++ b/test/integrations/destinations/salesforce/dataDelivery/other.ts @@ -0,0 +1,106 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const statTags = { + errorCategory: 'network', + errorType: 'retryable', + destType: 'SALESFORCE', + module: 'destination', + implementation: 'native', + feature: 'dataDelivery', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', +}; +const metadata = { + jobId: 1, + attemptNum: 1, + userId: 'default-userId', + destinationId: 'default-destinationId', + workspaceId: 'default-workspaceId', + sourceId: 'default-sourceId', + secret: { + accessToken: 'default-accessToken', + }, + dontBatch: false, +}; + +export const otherSalesforceScenariosV1: ProxyV1TestData[] = [ + { + id: 'salesforce_v1_other_scenario_1', + name: 'salesforce', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://sf_test_url/test_for_service_not_available', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 500, + metadata, + }, + ], + statTags, + message: + 'Salesforce Request Failed - due to "{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}", (Retryable) during Salesforce Response Handling', + status: 500, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_other_scenario_2', + name: 'salesforce', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload({ + endpoint: 'https://random_test_url/test_for_internal_server_error', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata, + }, + ], + statTags, + message: + 'Salesforce Request Failed - due to ""Internal Server Error"", (Retryable) during Salesforce Response Handling', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/salesforce/network.ts b/test/integrations/destinations/salesforce/network.ts index 396fad9d69..b422271d36 100644 --- a/test/integrations/destinations/salesforce/network.ts +++ b/test/integrations/destinations/salesforce/network.ts @@ -1,15 +1,22 @@ +const commonHeaders = { + Authorization: 'Bearer token', + 'Content-Type': 'application/json', +}; + +const dataValue = { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', +}; + const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/1', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -26,14 +33,7 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/3', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -50,19 +50,11 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/2', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', - Authorization: 'Bearer Incorrect_token', - 'User-Agent': 'RudderLabs', + Authorization: 'Bearer token', }, method: 'POST', }, @@ -74,14 +66,7 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/4', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -98,14 +83,7 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/5', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -122,14 +100,7 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/6', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', @@ -146,19 +117,11 @@ const tfProxyMocksData = [ { httpReq: { url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/7', - data: { - Email: 'denis.kornilov@sbermarket.ru', - Company: 'sbermarket.ru', - LastName: 'Корнилов', - FirstName: 'Денис', - LeadSource: 'App Signup', - account_type__c: 'free_trial', - }, + data: dataValue, params: { destination: 'salesforce' }, headers: { 'Content-Type': 'application/json', Authorization: 'Bearer token', - 'User-Agent': 'RudderLabs', }, method: 'POST', }, @@ -323,4 +286,185 @@ const transformationMocksData = [ }, }, ]; -export const networkCallsData = [...tfProxyMocksData, ...transformationMocksData]; + +const businessMockData = [ + { + description: + 'Mock response from destination depicting a valid lead request, with no changed data', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + data: dataValue, + headers: commonHeaders, + }, + httpRes: { + data: { statusText: 'No Content' }, + status: 204, + }, + }, + { + description: 'Mock response from destination depicting a invalid session id', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/invalid_session_id', + data: dataValue, + headers: commonHeaders, + }, + httpRes: { + data: [{ message: 'Session expired or invalid', errorCode: 'INVALID_SESSION_ID' }], + status: 500, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/2', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer Incorrect_token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: [{ message: 'INVALID_HEADER_TYPE', errorCode: 'INVALID_AUTH_HEADER' }], + status: 401, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/4', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: [{ message: 'Request limit exceeded', errorCode: 'REQUEST_LIMIT_EXCEEDED' }], + status: 403, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/5', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: [{ message: 'Server Unavailable', errorCode: 'SERVER_UNAVAILABLE' }], + status: 503, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/6', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { error: 'invalid_grant', error_description: 'authentication failure' }, + status: 400, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/7', + data: dataValue, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + message: 'Server Unavailable', + errorCode: 'SERVER_UNAVAILABLE', + }, + status: 503, + }, + }, + { + httpReq: { + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/parameterizedSearch/?q=123&sobject=object_name&in=External_ID__c&object_name.fields=id,External_ID__c', + data: { Planning_Categories__c: 'pc', External_ID__c: 123 }, + params: { destination: 'salesforce' }, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + searchRecords: [ + { + attributes: { + type: 'object_name', + url: '/services/data/v50.0/sobjects/object_name/a0J75100002w97gEAA', + }, + Id: 'a0J75100002w97gEAA', + External_ID__c: 'external_id', + }, + { + attributes: { + type: 'object_name', + url: '/services/data/v50.0/sobjects/object_name/a0J75200002w9ZsEAI', + }, + Id: 'a0J75200002w9ZsEAI', + External_ID__c: 'external_id TEST', + }, + ], + }, + status: 200, + }, + }, +]; + +const otherMocksData = [ + { + description: + 'Mock response from destination depicting a valid lead request, with no changed data', + httpReq: { + method: 'post', + url: 'https://sf_test_url/test_for_service_not_available', + }, + httpRes: { + data: { + error: { + message: 'Service Unavailable', + description: + 'The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later.', + }, + }, + status: 503, + }, + }, +]; + +export const networkCallsData = [ + ...tfProxyMocksData, + ...transformationMocksData, + ...businessMockData, + ...otherMocksData, +]; diff --git a/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts b/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts new file mode 100644 index 0000000000..bed8eec8db --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/dataDelivery/data.ts @@ -0,0 +1,3 @@ +import { testScenariosForV1API } from './oauth'; + +export const data = [...testScenariosForV1API]; diff --git a/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts b/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts new file mode 100644 index 0000000000..55eaa9cca1 --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/dataDelivery/oauth.ts @@ -0,0 +1,174 @@ +import { ProxyMetdata } from '../../../../../src/types'; +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload } from '../../../testUtils'; + +const commonHeadersForWrongToken = { + Authorization: 'Bearer expiredAccessToken', + 'Content-Type': 'application/json', +}; + +const commonHeadersForRightToken = { + Authorization: 'Bearer correctAccessToken', + 'Content-Type': 'application/json', +}; +const params = { destination: 'salesforce_oauth' }; + +const users = [ + { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', + }, +]; + +const statTags = { + retryable: { + destType: 'SALESFORCE_OAUTH', + destinationId: 'dummyDestinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'dummyWorkspaceId', + }, +}; + +const commonRequestParametersWithWrongToken = { + headers: commonHeadersForWrongToken, + JSON: users[0], + params, +}; + +const commonRequestParametersWithRightToken = { + headers: commonHeadersForRightToken, + JSON: users[0], + params, +}; + +export const proxyMetdataWithSecretWithWrongAccessToken: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: { + access_token: 'expiredAccessToken', + instanceUrl: 'https://rudderstack.my.salesforce_oauth.com', + }, + destInfo: { authKey: 'dummyDestinationId' }, + dontBatch: false, +}; + +export const proxyMetdataWithSecretWithRightAccessToken: ProxyMetdata = { + jobId: 1, + attemptNum: 1, + userId: 'dummyUserId', + sourceId: 'dummySourceId', + destinationId: 'dummyDestinationId', + workspaceId: 'dummyWorkspaceId', + secret: { + access_token: 'expiredRightToken', + instanceUrl: 'https://rudderstack.my.salesforce_oauth.com', + }, + destInfo: { authKey: 'dummyDestinationId' }, + dontBatch: false, +}; + +export const reqMetadataArrayWithWrongSecret = [proxyMetdataWithSecretWithWrongAccessToken]; +export const reqMetadataArray = [proxyMetdataWithSecretWithRightAccessToken]; + +export const testScenariosForV1API: ProxyV1TestData[] = [ + { + id: 'salesforce_v1_scenario_1', + name: 'salesforce_oauth', + description: '[Proxy v1 API] :: Test with expired access token scenario', + successCriteria: + 'Should return 5XX with error category REFRESH_TOKEN and Session expired or invalid, INVALID_SESSION_ID', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParametersWithWrongToken, + endpoint: + 'https://rudderstack.my.salesforce_oauth.com/services/data/v50.0/sobjects/Lead/20', + }, + reqMetadataArrayWithWrongSecret, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + authErrorCategory: 'REFRESH_TOKEN', + message: + 'Salesforce Request Failed - due to "INVALID_SESSION_ID", (Retryable) during Salesforce Response Handling', + response: [ + { + error: + '[{"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"}]', + metadata: proxyMetdataWithSecretWithWrongAccessToken, + statusCode: 500, + }, + ], + statTags: statTags.retryable, + }, + }, + }, + }, + }, + { + id: 'salesforce_v1_scenario_2', + name: 'salesforce', + description: + '[Proxy v1 API] :: Test for a valid request - Lead creation with existing unchanged leadId and unchanged data', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParametersWithRightToken, + endpoint: + 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + }, + reqMetadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request for destination: salesforce Processed Successfully', + response: [ + { + error: '{"statusText":"No Content"}', + metadata: proxyMetdataWithSecretWithRightAccessToken, + statusCode: 200, + }, + ], + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/salesforce_oauth/network.ts b/test/integrations/destinations/salesforce_oauth/network.ts new file mode 100644 index 0000000000..ae5f9d3fe4 --- /dev/null +++ b/test/integrations/destinations/salesforce_oauth/network.ts @@ -0,0 +1,51 @@ +const headerWithWrongAccessToken = { + Authorization: 'Bearer expiredAccessToken', + 'Content-Type': 'application/json', +}; + +const headerWithRightAccessToken = { + Authorization: 'Bearer correctAccessToken', + 'Content-Type': 'application/json', +}; + +const dataValue = { + Email: 'danis.archurav@sbermarket.ru', + Company: 'itus.ru', + LastName: 'Danis', + FirstName: 'Archurav', + LeadSource: 'App Signup', + account_type__c: 'free_trial', +}; + +const businessMockData = [ + { + description: 'Mock response from destination depicting an expired access token', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce_oauth.com/services/data/v50.0/sobjects/Lead/20', + headers: headerWithWrongAccessToken, + data: dataValue, + params: { destination: 'salesforce_oauth' }, + }, + httpRes: { + data: [{ message: 'Session expired or invalid', errorCode: 'INVALID_SESSION_ID' }], + status: 401, + }, + }, + { + description: + 'Mock response from destination depicting a valid lead request, with no changed data', + httpReq: { + method: 'post', + url: 'https://rudderstack.my.salesforce.com/services/data/v50.0/sobjects/Lead/existing_unchanged_leadId', + data: dataValue, + headers: headerWithRightAccessToken, + }, + httpRes: { + data: { statusText: 'No Content' }, + status: 204, + }, + }, +]; + +export const networkCallsData = [...businessMockData]; diff --git a/test/integrations/destinations/sfmc/network.ts b/test/integrations/destinations/sfmc/network.ts index 7564d8c6d5..93854e3691 100644 --- a/test/integrations/destinations/sfmc/network.ts +++ b/test/integrations/destinations/sfmc/network.ts @@ -11,4 +11,54 @@ export const networkCallsData = [ }, }, }, + { + httpReq: { + url: 'https://testHandleHttpRequest401.auth.marketingcloudapis.com/v2/token', + method: 'POST', + }, + httpRes: { + status: 401, + data: { + error: 'invalid_client', + error_description: + 'Invalid client ID. Use the client ID in Marketing Cloud Installed Packages.', + error_uri: 'https://developer.salesforce.com/docs', + }, + }, + }, + { + httpReq: { + url: 'https://testHandleHttpRequest429.auth.marketingcloudapis.com/v2/token', + method: 'POST', + }, + httpRes: { + status: 429, + data: { + message: 'Your requests are temporarily blocked.', + errorcode: 50200, + documentation: + 'https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/error-handling.htm', + }, + }, + }, + { + httpReq: { + url: 'https://testHandleHttpRequest-dns.auth.marketingcloudapis.com/v2/token', + method: 'POST', + }, + httpRes: { + data: {}, + status: 400, + }, + }, + { + httpReq: { + url: 'https://testHandleHttpRequest-null.auth.marketingcloudapis.com/v2/token', + method: 'POST', + }, + httpRes: { + data: null, + status: 500, + }, + }, ]; diff --git a/test/integrations/destinations/sfmc/processor/data.ts b/test/integrations/destinations/sfmc/processor/data.ts index b2839908ad..883032d223 100644 --- a/test/integrations/destinations/sfmc/processor/data.ts +++ b/test/integrations/destinations/sfmc/processor/data.ts @@ -1894,4 +1894,326 @@ export const data = [ }, }, }, + { + name: 'sfmc', + description: 'Tests 401 un authenticated code from sfmc', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + event: 'message event', + type: 'track', + userId: '12345', + properties: { + id: 'id101', + contactId: 'cid101', + email: 'testemail@gmail.com', + accountNumber: '99110099', + patronName: 'SP', + }, + }, + destination: { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'SFMC', + DestinationDefinition: { + ID: '1pYpYSeQd8OeN6xPdw6VGDzqUd1', + Name: 'SFMC', + DisplayName: 'Salesforce Marketing Cloud', + Config: { + destConfig: [], + excludeKeys: [], + includeKeys: [], + saveDestinationResponse: false, + supportedSourceTypes: [], + transformAt: 'processor', + }, + ResponseRules: {}, + }, + Config: { + clientId: 'testHandleHttpRequest401', + clientSecret: 'testHandleHttpRequest401', + createOrUpdateContacts: false, + eventDelivery: true, + eventDeliveryTS: 1615371070621, + eventToExternalKey: [ + { + from: 'Event Name', + to: 'C500FD37-155C-49BD-A21B-AFCEF3D1A9CB', + }, + { + from: 'Watch', + to: 'C500FD37-155C-49BD-A21B-AFCEF3D1A9CB', + }, + ], + eventToPrimaryKey: [ + { + from: 'userId', + to: 'User Key', + }, + { + from: 'watch', + to: 'Guest Key, Contact Key', + }, + ], + eventToUUID: [ + { + event: 'Event Name', + uuid: true, + }, + ], + eventToDefinitionMapping: [ + { + from: 'message event', + to: 'test-event-definition', + }, + ], + externalKey: 'f3ffa19b-e0b3-4967-829f-549b781080e6', + subDomain: 'testHandleHttpRequest401', + }, + Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"Could not retrieve access token","destinationResponse":{"error":"invalid_client","error_description":"Invalid client ID. Use the client ID in Marketing Cloud Installed Packages.","error_uri":"https://developer.salesforce.com/docs"}}', + statTags: { + destType: 'SFMC', + errorCategory: 'network', + errorType: 'aborted', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 401, + }, + ], + }, + }, + }, + { + name: 'sfmc', + description: 'Tests 429 status code from sfmc', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + event: 'message event', + type: 'track', + userId: '12345', + properties: { + id: 'id101', + contactId: 'cid101', + email: 'testemail@gmail.com', + accountNumber: '99110099', + patronName: 'SP', + }, + }, + destination: { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'SFMC', + DestinationDefinition: { + ID: '1pYpYSeQd8OeN6xPdw6VGDzqUd1', + Name: 'SFMC', + DisplayName: 'Salesforce Marketing Cloud', + Config: { + destConfig: [], + excludeKeys: [], + includeKeys: [], + saveDestinationResponse: false, + supportedSourceTypes: [], + transformAt: 'processor', + }, + ResponseRules: {}, + }, + Config: { + clientId: 'testHandleHttpRequest429', + clientSecret: 'testHandleHttpRequest429', + subDomain: 'testHandleHttpRequest429', + }, + Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"Could not retrieve access token","destinationResponse":{"message":"Your requests are temporarily blocked.","errorcode":50200,"documentation":"https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/error-handling.htm"}}', + statTags: { + destType: 'SFMC', + errorCategory: 'network', + errorType: 'throttled', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 429, + }, + ], + }, + }, + }, + { + name: 'sfmc', + description: 'Tests DNS lookup failure for sfmc', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + event: 'message event', + type: 'track', + userId: '12345', + properties: { + id: 'id101', + contactId: 'cid101', + email: 'testemail@gmail.com', + accountNumber: '99110099', + patronName: 'SP', + }, + }, + destination: { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'SFMC', + DestinationDefinition: { + ID: '1pYpYSeQd8OeN6xPdw6VGDzqUd1', + Name: 'SFMC', + DisplayName: 'Salesforce Marketing Cloud', + Config: { + destConfig: [], + excludeKeys: [], + includeKeys: [], + saveDestinationResponse: false, + supportedSourceTypes: [], + transformAt: 'processor', + }, + ResponseRules: {}, + }, + Config: { + clientId: 'testHandleHttpRequest-dns', + clientSecret: 'testHandleHttpRequest-dns', + subDomain: 'testHandleHttpRequest-dns', + }, + Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: '{"message":"Could not retrieve access token","destinationResponse":{}}', + statTags: { + destType: 'SFMC', + errorCategory: 'network', + errorType: 'aborted', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, + { + name: 'sfmc', + description: 'Test 500 status failure for sfmc', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + event: 'message event', + type: 'track', + userId: '12345', + properties: { + id: 'id101', + contactId: 'cid101', + email: 'testemail@gmail.com', + accountNumber: '99110099', + patronName: 'SP', + }, + }, + destination: { + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'SFMC', + DestinationDefinition: { + ID: '1pYpYSeQd8OeN6xPdw6VGDzqUd1', + Name: 'SFMC', + DisplayName: 'Salesforce Marketing Cloud', + Config: { + destConfig: [], + excludeKeys: [], + includeKeys: [], + saveDestinationResponse: false, + supportedSourceTypes: [], + transformAt: 'processor', + }, + ResponseRules: {}, + }, + Config: { + clientId: 'testHandleHttpRequest-null', + clientSecret: 'testHandleHttpRequest-null', + subDomain: 'testHandleHttpRequest-null', + }, + Enabled: true, + Transformations: [], + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Could not retrieve access token', + statTags: { + destType: 'SFMC', + errorCategory: 'network', + errorType: 'retryable', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 500, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/slack/processor/data.ts b/test/integrations/destinations/slack/processor/data.ts index 7deb555fa9..1fcbb2ca03 100644 --- a/test/integrations/destinations/slack/processor/data.ts +++ b/test/integrations/destinations/slack/processor/data.ts @@ -2017,4 +2017,159 @@ export const data = [ }, }, }, + { + name: 'slack', + description: + 'Test 12-> Identify -> Default template with some whiteListed traits and traits as null', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + ID: '1ZQVSU9SXNg6KYgZALaqjAO3PIL', + Name: 'test-slack', + DestinationDefinition: { + ID: '1ZQUiJVMlmF7lfsdfXg7KXQnlLV', + Name: 'SLACK', + DisplayName: 'Slack', + Config: { + excludeKeys: [], + includeKeys: [], + }, + }, + Config: { + eventChannelSettings: [ + { + eventChannelWebhook: 'https://hooks.slack.com/services/example/test/demo', + eventName: 'is', + eventRegex: true, + }, + ], + eventTemplateSettings: [ + { + eventName: 'is', + eventRegex: true, + eventTemplate: + '{{name}} performed {{event}} with {{properties.key1}} {{properties.key2}}', + }, + { + eventName: '', + eventRegex: false, + eventTemplate: '', + }, + ], + webhookUrl: 'https://hooks.slack.com/services/THZM86VSS/BV9HZ2UN6/demo', + whitelistedTraitsSettings: [], + }, + Enabled: true, + Transformations: [], + IsProcessorEnabled: true, + }, + message: { + anonymousId: '4de817fb-7f8e-4e23-b9be-f6736dbda20f', + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.1.1-rc.1', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.1.1-rc.1', + }, + locale: 'en-US', + os: { + name: '', + version: '', + }, + page: { + path: '/tests/html/script-test.html', + referrer: 'http://localhost:1111/tests/html/', + search: '', + title: '', + url: 'http://localhost:1111/tests/html/script-test.html', + }, + screen: { + density: 1.7999999523162842, + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36', + }, + integrations: { + All: true, + }, + messageId: '9ecc0183-89ed-48bd-87eb-b2d8e1ca6780', + originalTimestamp: '2020-03-23T03:46:30.916Z', + properties: { + path: '/tests/html/script-test.html', + referrer: 'http://localhost:1111/tests/html/', + search: '', + title: '', + url: 'http://localhost:1111/tests/html/script-test.html', + }, + receivedAt: '2020-03-23T09:16:31.041+05:30', + request_ip: '[::1]:52056', + sentAt: '2020-03-23T03:46:30.916Z', + timestamp: '2020-03-23T09:16:31.041+05:30', + type: 'identify', + userId: '12345', + }, + metadata: { + anonymousId: '4de817fb-7f8e-4e23-b9be-f6736dbda20f', + destinationId: '1ZQVSU9SXNg6KYgZALaqjAO3PIL', + destinationType: 'SLACK', + jobId: 126, + messageId: '9ecc0183-89ed-48bd-87eb-b2d8e1ca6780', + sourceId: '1YhwKyDcKstudlGxkeN5p2wgsrp', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://hooks.slack.com/services/THZM86VSS/BV9HZ2UN6/demo', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: {}, + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: { + payload: + '{"text":"Identified User 12345","username":"RudderStack","icon_url":"https://cdn.rudderlabs.com/rudderstack.png"}', + }, + }, + files: {}, + userId: '12345', + statusCode: 200, + }, + metadata: { + anonymousId: '4de817fb-7f8e-4e23-b9be-f6736dbda20f', + destinationId: '1ZQVSU9SXNg6KYgZALaqjAO3PIL', + destinationType: 'SLACK', + jobId: 126, + messageId: '9ecc0183-89ed-48bd-87eb-b2d8e1ca6780', + sourceId: '1YhwKyDcKstudlGxkeN5p2wgsrp', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/snapchat_conversion/processor/data.ts b/test/integrations/destinations/snapchat_conversion/processor/data.ts index b0d14208cc..7de7ed9b8d 100644 --- a/test/integrations/destinations/snapchat_conversion/processor/data.ts +++ b/test/integrations/destinations/snapchat_conversion/processor/data.ts @@ -4600,6 +4600,140 @@ export const data = [ }, }, }, + { + name: 'snapchat_conversion', + description: 'test event mapping from destination config', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + messageId: 'ec5481b6-a926-4d2e-b293-0b3a77c4d3be', + originalTimestamp: '2022-04-22T10:57:58Z', + anonymousId: 'ea5cfab2-3961-4d8a-8187-3d1858c99090', + context: { + traits: { + email: 'test@email.com', + phone: '+91 2111111 ', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', + id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', + manufacturer: 'Google', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + locale: 'en-US', + os: { + name: 'iOS', + version: '14.4.1', + }, + screen: { + density: 2, + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', + }, + type: 'track', + event: 'Custom Event', + properties: { + query: 't-shirts', + event_conversion_type: 'web', + }, + integrations: { + All: true, + }, + sentAt: '2022-04-22T10:57:58Z', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: false, + }, + }, + Config: { + pixelId: 'dummyPixelId', + apiKey: 'dummyApiKey', + rudderEventsToSnapEvents: [ + { + from: 'Custom Event', + to: 'level_complete', + }, + ], + }, + }, + metadata: { + jobId: 47, + destinationId: 'd2', + workspaceId: 'w2', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + metadata: { + jobId: 47, + destinationId: 'd2', + workspaceId: 'w2', + }, + output: { + version: '1', + type: 'REST', + userId: '', + method: 'POST', + endpoint: 'https://tr.snapchat.com/v2/conversion', + headers: { + Authorization: 'Bearer dummyApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_type: 'LEVEL_COMPLETE', + hashed_email: '73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2', + hashed_phone_number: + 'bc77d64d7045fe44795ed926df37231a0cfb6ec6b74588c512790e9f143cc492', + hashed_mobile_ad_id: + 'f9779d734aaee50f16ee0011260bae7048f1d9a128c62b6a661077875701edd2', + hashed_idfv: '54bd0b26a3d39dad90f5149db49b9fd9ba885f8e35d1d94cae69273f5e657b9f', + user_agent: + 'mozilla/5.0 (macintosh; intel mac os x 10_15_2) applewebkit/537.36 (khtml, like gecko) chrome/79.0.3945.88 safari/537.36', + timestamp: '1650625078', + event_conversion_type: 'OFFLINE', + pixel_id: 'dummyPixelId', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + statusCode: 200, + }, + ], + }, + }, + }, ].map((tc) => ({ ...tc, mockFns: (_) => { diff --git a/test/integrations/destinations/splitio/processor/data.ts b/test/integrations/destinations/splitio/processor/data.ts index 3fda93cc30..d4c3e39794 100644 --- a/test/integrations/destinations/splitio/processor/data.ts +++ b/test/integrations/destinations/splitio/processor/data.ts @@ -43,7 +43,7 @@ export const data = [ Config: { apiKey: 'abcde', environment: 'staging', - trafficType: 'user', + trafficType: 'anonymous', }, }, }, @@ -246,7 +246,7 @@ export const data = [ Config: { apiKey: 'abcde', environment: 'production', - trafficType: 'user', + trafficType: 'anonymous', }, }, }, @@ -847,6 +847,9 @@ export const data = [ library: { name: 'http', }, + traits: { + trafficTypeName: 'user', + }, }, type: 'identify', timestamp: '2020-01-21T00:21:34.208Z', @@ -858,7 +861,7 @@ export const data = [ Config: { apiKey: 'abcde', environment: 'staging', - trafficType: 'user', + trafficType: 'anonymous', }, }, }, diff --git a/test/integrations/destinations/the_trade_desk/common.ts b/test/integrations/destinations/the_trade_desk/common.ts index 28f46df829..08ba0ee6ea 100644 --- a/test/integrations/destinations/the_trade_desk/common.ts +++ b/test/integrations/destinations/the_trade_desk/common.ts @@ -5,8 +5,7 @@ const destTypeInUpperCase = 'THE_TRADE_DESK'; const advertiserId = 'test-advertiser-id'; const dataProviderId = 'rudderstack'; const segmentName = 'test-segment'; - -const trackerId = 'test-trackerId'; +const firstPartyDataEndpoint = 'https://sin-data.adsrvr.org/data/advertiser'; const sampleDestination: Destination = { Config: { @@ -48,6 +47,22 @@ const sampleContext = { sources: sampleSource, }; +const proxyV1AbortableErrorStatTags = { + destType: destTypeInUpperCase, + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +const { errorType: _, ...proxyV1PlatformErrorStatTags } = proxyV1AbortableErrorStatTags; +proxyV1PlatformErrorStatTags.errorCategory = 'platform'; + +const proxyV1RetryableErrorStatTags = { ...proxyV1AbortableErrorStatTags, errorType: 'retryable' }; + export { destType, destTypeInUpperCase, @@ -56,4 +71,8 @@ export { segmentName, sampleDestination, sampleContext, + proxyV1AbortableErrorStatTags, + proxyV1PlatformErrorStatTags, + proxyV1RetryableErrorStatTags, + firstPartyDataEndpoint, }; diff --git a/test/integrations/destinations/the_trade_desk/delivery/business.ts b/test/integrations/destinations/the_trade_desk/delivery/business.ts new file mode 100644 index 0000000000..0406a5f0bc --- /dev/null +++ b/test/integrations/destinations/the_trade_desk/delivery/business.ts @@ -0,0 +1,175 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { + destType, + advertiserId, + dataProviderId, + segmentName, + sampleDestination, + proxyV1AbortableErrorStatTags, + firstPartyDataEndpoint, +} from '../common'; + +const validRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + UID2: 'test-uid2-1', + }, + { + DAID: 'test-daid-2', + Data: [ + { + Name: segmentName, + TTLInMinutes: 0, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 0, + }, + ], + UID2: 'test-uid2-2', + }, + ], +}; + +const invalidRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + UID2: 'test-invalid-uid2', + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +export const businessProxyV1: ProxyV1TestData[] = [ + { + id: 'ttd_v1_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Test for a valid request - Successful delivery of Add/Remove IDs to Trade Desk', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: validRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: 'Request Processed Successfully', + response: [ + { + statusCode: 200, + metadata: generateMetadata(1), + error: '{}', + }, + ], + }, + }, + }, + }, + }, + { + id: 'ttd_v1_scenario_2', + name: destType, + description: + '[Proxy v1 API] :: Test for invalid ID - where the destination responds with 200 with invalid ID', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: invalidRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + 'Request failed with status: 200 due to {"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', + response: [ + { + error: + '{"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: proxyV1AbortableErrorStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/delivery/data.ts b/test/integrations/destinations/the_trade_desk/delivery/data.ts index 320eb6dcfe..5099eafce7 100644 --- a/test/integrations/destinations/the_trade_desk/delivery/data.ts +++ b/test/integrations/destinations/the_trade_desk/delivery/data.ts @@ -1,248 +1,3 @@ -import { - destType, - destTypeInUpperCase, - advertiserId, - dataProviderId, - segmentName, - sampleDestination, -} from '../common'; - -beforeAll(() => { - process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY = 'mockedDataProviderSecretKey'; -}); - -afterAll(() => { - delete process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY; -}); - -export const data = [ - { - name: destType, - description: 'Successful delivery of Add/Remove IDs to/from Trade Desk', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - destinationConfig: sampleDestination.Config, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - UID2: 'test-uid2-1', - }, - { - DAID: 'test-daid-2', - Data: [ - { - Name: segmentName, - TTLInMinutes: 0, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 0, - }, - ], - UID2: 'test-uid2-2', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: {}, - status: 200, - }, - message: 'Request Processed Successfully', - status: 200, - }, - }, - }, - }, - }, - { - name: destType, - description: 'Error response from The Trade Desk due to invalid IDs', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - destinationConfig: sampleDestination.Config, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - { - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - UID2: 'test-invalid-uid2', - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: { - response: { - FailedLines: [{ ErrorCode: 'MissingUserId', Message: 'Invalid UID2, item #2' }], - }, - status: 200, - }, - message: - 'Request failed with status: 200 due to {"FailedLines":[{"ErrorCode":"MissingUserId","Message":"Invalid UID2, item #2"}]}', - statTags: { - destType: destTypeInUpperCase, - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, - { - name: destType, - description: - 'Missing advertiser secret key in destination config from proxy request from server', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://sin-data.adsrvr.org/data/advertiser', - headers: {}, - params: {}, - body: { - JSON: { - AdvertiserId: advertiserId, - DataProviderId: dataProviderId, - Items: [ - { - DAID: 'test-daid-1', - Data: [ - { - Name: segmentName, - TTLInMinutes: 43200, - }, - ], - }, - ], - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - destinationResponse: '', - message: 'Advertiser secret key is missing in destination config. Aborting', - statTags: { - destType: destTypeInUpperCase, - destinationId: 'Non-determininable', - errorCategory: 'platform', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - status: 400, - }, - }, - }, - }, - }, -]; +import { businessProxyV1 } from './business'; +import { otherProxyV1 } from './other'; +export const data = [...businessProxyV1, ...otherProxyV1]; diff --git a/test/integrations/destinations/the_trade_desk/delivery/other.ts b/test/integrations/destinations/the_trade_desk/delivery/other.ts new file mode 100644 index 0000000000..bed10e6ec5 --- /dev/null +++ b/test/integrations/destinations/the_trade_desk/delivery/other.ts @@ -0,0 +1,290 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { generateProxyV1Payload, generateMetadata } from '../../../testUtils'; +import { + destType, + advertiserId, + dataProviderId, + segmentName, + proxyV1PlatformErrorStatTags, + proxyV1RetryableErrorStatTags, + firstPartyDataEndpoint, + sampleDestination, +} from '../common'; +import { envMock } from '../mocks'; + +envMock(); + +const validRequestPayload1 = { + AdvertiserId: advertiserId, + DataProviderId: dataProviderId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +// https://partner.thetradedesk.com/v3/portal/data/doc/post-data-advertiser-external#error-codes-messages +export const otherProxyV1: ProxyV1TestData[] = [ + { + id: 'ttd_v1_other_scenario_1', + name: destType, + description: + '[Proxy v1 API] :: Missing advertiser secret key in destination config from proxy request from server', + successCriteria: 'Should return 400 with platform error', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: {}, + params: {}, + JSON: validRequestPayload1, + endpoint: firstPartyDataEndpoint, + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Advertiser secret key is missing in destination config. Aborting', + response: [ + { + error: 'Advertiser secret key is missing in destination config. Aborting', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: proxyV1PlatformErrorStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_2', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_service_not_available', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: + '{"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + statusCode: 503, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: + 'Request failed with status: 503 due to {"error":{"message":"Service Unavailable","description":"The server is currently unable to handle the request due to temporary overloading or maintenance of the server. Please try again later."}}', + status: 503, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_3', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_internal_server_error', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Internal Server Error"', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to "Internal Server Error"', + status: 500, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_4', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_gateway_time_out', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '"Gateway Timeout"', + statusCode: 504, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 504 due to "Gateway Timeout"', + status: 504, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_5', + name: destType, + description: '[Proxy v1 API] :: Scenario for testing null response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_response', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to ""', + status: 500, + }, + }, + }, + }, + }, + { + id: 'ttd_v1_other_scenario_6', + name: destType, + description: + '[Proxy v1 API] :: Scenario for testing null and no status response from destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Framework', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + endpoint: 'https://random_test_url/test_for_null_and_no_status', + }, + metadataArray, + sampleDestination.Config, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + response: [ + { + error: '""', + statusCode: 500, + metadata: generateMetadata(1), + }, + ], + statTags: proxyV1RetryableErrorStatTags, + message: 'Request failed with status: 500 due to ""', + status: 500, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/the_trade_desk/mocks.ts b/test/integrations/destinations/the_trade_desk/mocks.ts index ddcbebae88..bf9d910d42 100644 --- a/test/integrations/destinations/the_trade_desk/mocks.ts +++ b/test/integrations/destinations/the_trade_desk/mocks.ts @@ -3,3 +3,12 @@ import config from '../../../../src/cdk/v2/destinations/the_trade_desk/config'; export const defaultMockFns = () => { jest.replaceProperty(config, 'MAX_REQUEST_SIZE_IN_BYTES', 250); }; + +export const envMock = () => { + process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY = 'mockedDataProviderSecretKey'; + + // Clean up after all tests are done + afterAll(() => { + delete process.env.THE_TRADE_DESK_DATA_PROVIDER_SECRET_KEY; + }); +}; diff --git a/test/integrations/destinations/tiktok_ads/processor/data.ts b/test/integrations/destinations/tiktok_ads/processor/data.ts index d0447da43c..4dfd32d671 100644 --- a/test/integrations/destinations/tiktok_ads/processor/data.ts +++ b/test/integrations/destinations/tiktok_ads/processor/data.ts @@ -1369,7 +1369,7 @@ export const data = [ body: [ { statusCode: 400, - error: 'Either event name is not present or it is not a string', + error: 'Event is a required field and should be a string', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', @@ -5224,7 +5224,6 @@ export const data = [ event_id: '1616318632825_357', event_time: 1600372167, properties: { - content_type: 'product', contents: [ { price: 8, @@ -6055,7 +6054,7 @@ export const data = [ body: [ { statusCode: 400, - error: 'Event name is required', + error: 'Event is a required field and should be a string', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', @@ -6872,7 +6871,7 @@ export const data = [ }, { name: 'tiktok_ads', - description: 'Test 46 -> V2 -> Event with no properties', + description: 'Test 46 -> V2 -> Custom Event with no properties', feature: 'processor', module: 'destination', version: 'v0', @@ -6947,6 +6946,108 @@ export const data = [ event: 'customEvent', event_id: '84e26acc-56a5-4835-8233-591137fca468', event_time: 1600372167, + user: { + locale: 'en-US', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 46 -> V2 -> Standard Event with no properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'Search', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + sendCustomEvents: true, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'Search', + event_id: '84e26acc-56a5-4835-8233-591137fca468', + event_time: 1600372167, properties: { content_type: 'product' }, user: { locale: 'en-US', @@ -7004,7 +7105,7 @@ export const data = [ body: [ { statusCode: 400, - error: 'Either event name is not present or it is not a string', + error: 'Event is a required field and should be a string', statTags: { errorCategory: 'dataValidation', errorType: 'instrumentation', diff --git a/test/integrations/destinations/twitter_ads/router/data.ts b/test/integrations/destinations/twitter_ads/router/data.ts new file mode 100644 index 0000000000..ce9aea6595 --- /dev/null +++ b/test/integrations/destinations/twitter_ads/router/data.ts @@ -0,0 +1,204 @@ +const authHeaderConstant = + 'OAuth oauth_consumer_key="qwe", oauth_nonce="V1kMh028kZLLhfeYozuL0B45Pcx6LvuW", oauth_signature="Di4cuoGv4PnCMMEeqfWTcqhvdwc%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1685603652", oauth_token="dummyAccessToken", oauth_version="1.0"'; + +export const data = [ + { + name: 'twitter_ads', + description: 'tests router flow', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'track', + event: 'ABC Searched', + channel: 'web', + context: { + source: 'test', + userAgent: 'chrome', + traits: { + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + email: 'abc@gmail.com', + phone: '+1234589947', + ge: 'male', + db: '19950715', + lastname: 'Rudderlabs', + firstName: 'Test', + address: { + city: 'Kolkata', + state: 'WB', + zip: '700114', + country: 'IN', + }, + }, + device: { + advertisingId: 'abc123', + }, + library: { + name: 'rudder-sdk-ruby-sync', + version: '1.0.6', + }, + }, + messageId: '7208bbb6-2c4e-45bb-bf5b-ad426f3593e9', + timestamp: '2020-08-14T05:30:30.118Z', + properties: { + conversionTime: '2023-06-01T06:03:08.739Z', + tax: 2, + total: 27.5, + coupon: 'hasbros', + revenue: 48, + price: 25, + quantity: 2, + currency: 'USD', + priceCurrency: 'USD', + conversionId: '213123', + numberItems: '2323', + phone: '+919927455678', + twclid: '543', + shipping: 3, + subtotal: 22.5, + affiliation: 'Google Store', + checkout_id: 'fksdjfsdjfisjf9sdfjsd9f', + email: 'abc@ax.com', + contents: [ + { + price: '123.3345', + quantity: '12', + id: '12', + }, + { + price: 200, + quantity: 11, + id: '4', + }, + ], + }, + anonymousId: '50be5c78-6c3f-4b60-be84-97805a316fb1', + integrations: { + All: true, + }, + }, + metadata: { + secret: { + consumerKey: 'qwe', + consumerSecret: 'fdghv', + accessToken: 'dummyAccessToken', + accessTokenSecret: 'testAccessTokenSecret', + }, + }, + destination: { + Config: { + pixelId: 'dummyPixelId', + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + twitterAdsEventNames: [ + { + rudderEventName: 'ABC Searched', + twitterEventId: 'tw-234234324234', + }, + { + rudderEventName: 'Home Page Viewed', + twitterEventId: 'tw-odt2o-odt2q', + }, + ], + }, + }, + }, + ], + destType: 'twitter_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + batchedRequest: { + body: { + FORM: {}, + JSON: { + conversions: [ + { + contents: [ + { content_id: '12', content_price: 123.3345, num_items: 12 }, + { content_id: '4', content_price: 200, num_items: 11 }, + ], + conversion_id: '213123', + conversion_time: '2023-06-01T06:03:08.739Z', + event_id: 'tw-234234324234', + identifiers: [ + { + hashed_email: + '4c3c8a8cba2f3bb1e9e617301f85d1f68e816a01c7b716f482f2ab9adb8181fb', + }, + { + hashed_phone_number: + 'b308962b96b40cce7981493a372db9478edae79f83c2d8ca6cd15a39566f8c56', + }, + { twclid: '543' }, + ], + number_items: 2, + price_currency: 'USD', + value: '25', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + }, + endpoint: 'https://ads-api.twitter.com/12/measurement/conversions/dummyPixelId', + files: {}, + headers: { + Authorization: + 'OAuth oauth_consumer_key="qwe", oauth_nonce="V1kMh028kZLLhfeYozuL0B45Pcx6LvuW", oauth_signature="Di4cuoGv4PnCMMEeqfWTcqhvdwc%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1685603652", oauth_token="dummyAccessToken", oauth_version="1.0"', + 'Content-Type': 'application/json', + }, + method: 'POST', + params: {}, + type: 'REST', + version: '1', + }, + destination: { + Config: { + pixelId: 'dummyPixelId', + rudderAccountId: '2EOknn1JNH7WK1MfNku4fGYKkRK', + twitterAdsEventNames: [ + { rudderEventName: 'ABC Searched', twitterEventId: 'tw-234234324234' }, + { rudderEventName: 'Home Page Viewed', twitterEventId: 'tw-odt2o-odt2q' }, + ], + }, + }, + metadata: [ + { + secret: { + accessToken: 'dummyAccessToken', + accessTokenSecret: 'testAccessTokenSecret', + consumerKey: 'qwe', + consumerSecret: 'fdghv', + }, + }, + ], + statusCode: 200, + }, + ], + }, + }, + }, + }, +].map((tc) => ({ + ...tc, + mockFns: (_) => { + jest.mock('../../../../../src/v0/destinations/twitter_ads/util', () => ({ + getAuthHeaderForRequest: (_a, _b) => { + return { Authorization: authHeaderConstant }; + }, + })); + }, +})); diff --git a/test/integrations/destinations/webhook/processor/data.ts b/test/integrations/destinations/webhook/processor/data.ts index dbe83a79a5..e9629041fe 100644 --- a/test/integrations/destinations/webhook/processor/data.ts +++ b/test/integrations/destinations/webhook/processor/data.ts @@ -2758,4 +2758,221 @@ export const data = [ }, }, }, + { + name: 'webhook', + description: 'Test POST method with track message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + additional_bet_index: 0, + level: 1, + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { + from: 'Content-Type', + to: 'application/xml', + }, + { + from: 'test2', + to: 'value2', + }, + ], + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + additional_bet_index: 0, + level: 1, + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + userId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + type: 'REST', + method: 'POST', + endpoint: 'http://6b0e6a60.ngrok.io', + headers: { + 'content-type': 'application/xml', + test2: 'value2', + }, + params: {}, + files: {}, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'webhook', + description: 'Test method PATCH', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + context: { + traits: { + address: { + city: 'Dhaka', + country: 'Bangladesh', + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + prop1: 1, + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + user_properties: { + coin_balance: 9466052, + }, + }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + webhookMethod: 'PATCH', + headers: [ + { + from: 'test2', + to: 'value2', + }, + ], + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + context: { + traits: { + address: { + city: 'Dhaka', + country: 'Bangladesh', + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + prop1: 1, + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + user_properties: { + coin_balance: 9466052, + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + userId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + type: 'REST', + method: 'PATCH', + endpoint: 'http://6b0e6a60.ngrok.io', + headers: { + 'content-type': 'application/json', + test2: 'value2', + }, + params: {}, + files: {}, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/webhook/router/data.ts b/test/integrations/destinations/webhook/router/data.ts index a082eb12f1..6c738ee8a7 100644 --- a/test/integrations/destinations/webhook/router/data.ts +++ b/test/integrations/destinations/webhook/router/data.ts @@ -419,4 +419,391 @@ export const data = [ }, }, }, + { + name: 'webhook', + description: 'Identify payload with 3 events in 1 batch', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: '234234234234234', + channel: 'mobile', + context: { + app: { + build: '1', + name: 'AMTestProject', + namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', + version: '1.0', + }, + device: { + id: '8d872292709c6fbe', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'android', + }, + library: { + name: 'com.rudderstack.android.sdk.core', + version: '1.0.2', + }, + locale: 'en-US', + network: { + carrier: 'Android', + bluetooth: false, + cellular: true, + wifi: true, + }, + os: { + name: 'Android', + version: '9', + }, + screen: { + density: 420, + height: 1794, + width: 1080, + }, + timezone: 'Asia/Kolkata', + traits: { + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createdat: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify@test.com', + firstname: 'John', + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + phone: '9876543210', + username: 'john_sparrow', + quantity: '5', + price: '56.0', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'identify', + integrations: { + All: true, + }, + messageId: '1590431830865-3be680d6-7dcd-4b05-8460-f3acc30046d9', + originalTimestamp: '2020-05-25T18:37:10.865Z', + sentAt: '2020-05-25T18:37:10.917Z', + type: 'identify', + userId: 'sample_user_id', + }, + metadata: { jobId: 2, userId: 'u1' }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { from: '', to: '' }, + { from: 'test2', to: 'value2' }, + ], + }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + { + message: { + anonymousId: '1231241234123', + channel: 'mobile', + context: { + timezone: 'Asia/Kolkata', + traits: { + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createdat: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify2@test.com', + firstname: 'John', + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + phone: '9876543210', + username: 'john_sparrow', + quantity: '5', + price: '56.0', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'identify', + integrations: { + All: true, + }, + messageId: '23432324-3be680d6-7dcd-4b05-8460-f3acc30046d9', + type: 'identify', + userId: 'sample_user_id', + }, + metadata: { jobId: 3, userId: 'u1' }, + destination: { + Config: { webhookUrl: 'https://6b0e6a60.ngrok.io/n' }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + { + message: { + anonymousId: '345345435', + channel: 'mobile', + event: 'identify', + integrations: { + All: true, + }, + messageId: '23432324-3be680d6-7dcd-4b05-8460-f3acc30046d9', + type: 'identify', + userId: 'sample_user_id', + }, + metadata: { jobId: 4, userId: 'u1' }, + destination: { + Config: { webhookUrl: 'https://6b0e6a60.ngrok.io/n' }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + ], + destType: 'webhook', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + body: { + XML: {}, + JSON_ARRAY: {}, + JSON: { + anonymousId: '234234234234234', + channel: 'mobile', + context: { + app: { + build: '1', + name: 'AMTestProject', + namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', + version: '1.0', + }, + device: { + id: '8d872292709c6fbe', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'android', + }, + library: { + name: 'com.rudderstack.android.sdk.core', + version: '1.0.2', + }, + locale: 'en-US', + network: { + carrier: 'Android', + bluetooth: false, + cellular: true, + wifi: true, + }, + os: { + name: 'Android', + version: '9', + }, + screen: { + density: 420, + height: 1794, + width: 1080, + }, + timezone: 'Asia/Kolkata', + traits: { + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createdat: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify@test.com', + firstname: 'John', + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + phone: '9876543210', + username: 'john_sparrow', + quantity: '5', + price: '56.0', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'identify', + integrations: { + All: true, + }, + messageId: '1590431830865-3be680d6-7dcd-4b05-8460-f3acc30046d9', + originalTimestamp: '2020-05-25T18:37:10.865Z', + sentAt: '2020-05-25T18:37:10.917Z', + type: 'identify', + userId: 'sample_user_id', + }, + FORM: {}, + }, + files: {}, + endpoint: 'http://6b0e6a60.ngrok.io', + userId: '234234234234234', + headers: { 'content-type': 'application/json', test2: 'value2' }, + version: '1', + params: {}, + type: 'REST', + method: 'POST', + }, + metadata: [{ jobId: 2, userId: 'u1' }], + batched: false, + statusCode: 200, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { from: '', to: '' }, + { from: 'test2', to: 'value2' }, + ], + }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + { + batchedRequest: { + body: { + XML: {}, + JSON_ARRAY: {}, + JSON: { + anonymousId: '1231241234123', + channel: 'mobile', + context: { + timezone: 'Asia/Kolkata', + traits: { + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createdat: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify2@test.com', + firstname: 'John', + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + phone: '9876543210', + username: 'john_sparrow', + quantity: '5', + price: '56.0', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'identify', + integrations: { + All: true, + }, + messageId: '23432324-3be680d6-7dcd-4b05-8460-f3acc30046d9', + type: 'identify', + userId: 'sample_user_id', + }, + FORM: {}, + }, + files: {}, + endpoint: 'https://6b0e6a60.ngrok.io/n', + userId: '1231241234123', + headers: { 'content-type': 'application/json' }, + version: '1', + params: {}, + type: 'REST', + method: 'POST', + }, + metadata: [{ jobId: 3, userId: 'u1' }], + batched: false, + statusCode: 200, + destination: { + Config: { + webhookUrl: 'https://6b0e6a60.ngrok.io/n', + }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + { + batchedRequest: { + body: { + XML: {}, + JSON_ARRAY: {}, + JSON: { + anonymousId: '345345435', + channel: 'mobile', + event: 'identify', + integrations: { + All: true, + }, + messageId: '23432324-3be680d6-7dcd-4b05-8460-f3acc30046d9', + type: 'identify', + userId: 'sample_user_id', + }, + FORM: {}, + }, + files: {}, + endpoint: 'https://6b0e6a60.ngrok.io/n', + userId: '345345435', + headers: { 'content-type': 'application/json' }, + version: '1', + params: {}, + type: 'REST', + method: 'POST', + }, + metadata: [{ jobId: 4, userId: 'u1' }], + batched: false, + statusCode: 200, + destination: { + Config: { + webhookUrl: 'https://6b0e6a60.ngrok.io/n', + }, + DestinationDefinition: { Config: { cdkV2Enabled: true } }, + }, + }, + ], + }, + }, + }, + }, ]; diff --git a/test/integrations/destinations/yahoo_dsp/processor/data.ts b/test/integrations/destinations/yahoo_dsp/processor/data.ts index eb607d60fb..3d04f9aa5c 100644 --- a/test/integrations/destinations/yahoo_dsp/processor/data.ts +++ b/test/integrations/destinations/yahoo_dsp/processor/data.ts @@ -51,7 +51,7 @@ export const data = [ }, { ipAddress: 'fdffddf', - email: 'van@abc.com', + email: 'van@abc.com ', deviceId: 'djfdjfkdjf', phone: '@09876543210', firstName: 'test', diff --git a/test/integrations/destinations/yandex_metrica_offline_events/processor/data.ts b/test/integrations/destinations/yandex_metrica_offline_events/processor/data.ts new file mode 100644 index 0000000000..4bb27bdf57 --- /dev/null +++ b/test/integrations/destinations/yandex_metrica_offline_events/processor/data.ts @@ -0,0 +1,746 @@ +export const data = [ + { + name: 'yandex_metrica_offline_events', + description: 'Successful identify event with YCLID identifier type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'YCLID', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + Currency: 'RUB', + DateTime: '1481718166', + Price: 100, + Target: 'GOAL1', + Yclid: '133591247640966458', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Successful identify event with ClientId identifier type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'ClientId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + Currency: 'RUB', + DateTime: '1481718166', + Price: 100, + Target: 'GOAL1', + ClientId: '133591247640966458', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Successful identify event with UserId identifier type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'UserId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + Currency: 'RUB', + DateTime: '1481718166', + Price: 100, + Target: 'GOAL1', + UserId: '133591247640966458', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Failed identify event with Price passed as string', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: '100', + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'UserId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Price can only be a numerical value. Aborting!: Workflow: procWorkflow, Step: prepareData, ChildStep: undefined, OriginalError: Price can only be a numerical value. Aborting!', + statTags: { + destType: 'YANDEX_METRICA_OFFLINE_EVENTS', + destinationId: 'destId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + workspaceId: 'wspId', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Failed identify event with invalid identifier type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '1481718166', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'InvalidIdentifierType', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Invalid identifier type passed in external Id. Valid types are ClientId, YCLID, UserId. Aborting!: Workflow: procWorkflow, Step: prepareData, ChildStep: undefined, OriginalError: Invalid identifier type passed in external Id. Valid types are ClientId, YCLID, UserId. Aborting!', + statTags: { + destType: 'YANDEX_METRICA_OFFLINE_EVENTS', + destinationId: 'destId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + workspaceId: 'wspId', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Failed identify event with invalid timestamp', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: 'invalidTimestamp', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'ClientId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Invalid timestamp. Aborting!: Workflow: procWorkflow, Step: prepareData, ChildStep: undefined, OriginalError: Invalid timestamp. Aborting!', + statTags: { + destType: 'YANDEX_METRICA_OFFLINE_EVENTS', + destinationId: 'destId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + workspaceId: 'wspId', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Successful identify event with non unix timestamp', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '2023-08-14T05:30:30.118Z', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'YCLID', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: '', + headers: {}, + params: {}, + body: { + JSON: { + Currency: 'RUB', + DateTime: '1691991030', + Price: 100, + Target: 'GOAL1', + Yclid: '133591247640966458', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, + { + name: 'yandex_metrica_offline_events', + description: 'Failed identify event with null or empty timestamp', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + type: 'identify', + traits: { + Price: 100, + Target: 'GOAL1', + Currency: 'RUB', + DateTime: '', + }, + userId: '', + channel: 'sources', + context: { + sources: { + job_id: '2du7rQyxlbIJl4LKgZAaaErEjcE', + version: '1849/merge', + job_run_id: 'cnsn3tt5fleigsfclr6g', + task_run_id: 'cnsn3tt5fleigsfclr70', + }, + externalId: [ + { + id: '133591247640966458', + type: 'YANDEX_METRICA_OFFLINE_EVENTS-conversions', + identifierType: 'ClientId', + }, + ], + mappedToDestination: 'true', + }, + recordId: '1', + rudderId: '14a58046-23a5-46bd-afbf-87c8acaa9d2e', + messageId: '91ef85d9-b170-440c-bae2-6284d4070338', + }, + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + Config: { + counterId: '574342423', + goalId: '23432565', + rudderAccountId: '2du7fLeK82nk9L2Xd1X507uiD1B', + authStatus: 'active', + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'DateTime cannot be empty. Aborting!: Workflow: procWorkflow, Step: prepareData, ChildStep: undefined, OriginalError: DateTime cannot be empty. Aborting!', + statTags: { + destType: 'YANDEX_METRICA_OFFLINE_EVENTS', + destinationId: 'destId', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'cdkV2', + module: 'destination', + workspaceId: 'wspId', + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/zendesk/processor/data.ts b/test/integrations/destinations/zendesk/processor/data.ts index 4dab6c5d5d..d84cdba15a 100644 --- a/test/integrations/destinations/zendesk/processor/data.ts +++ b/test/integrations/destinations/zendesk/processor/data.ts @@ -2370,4 +2370,89 @@ export const data = [ }, }, }, + { + name: 'zendesk', + description: 'Group Call : If message.traits are not present, aborting the call.', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + apiToken: 'myDummyApiToken4', + createUsersAsVerified: true, + domain: 'rudderlabshelp', + email: 'myDummyUserName1', + password: 'myDummyPwd1', + removeUsersFromOrganization: true, + sendGroupCallsWithoutUserId: true, + }, + DestinationDefinition: { + DisplayName: 'Zendesk', + ID: '1YknZ1ENqB8UurJQJE2VrEA61tr', + Name: 'ZENDESK', + }, + Enabled: true, + ID: 'xxxxxxxxxxxxxxxxxxxxxxxO51P', + Name: 'zendesk', + Transformations: [], + }, + message: { + anonymousId: '297b0750-934b-4411-b66c-9b418cdbc0c9', + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.1.0-beta.2', + }, + ip: '0.0.0.0', + library: { name: 'RudderLabs JavaScript SDK', version: '1.1.0-beta.2' }, + locale: 'en-GB', + os: { name: '', version: '' }, + screen: { density: 2 }, + traits: { email: 'example124@email.com', name: 'abcd124' }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36', + }, + groupId: 'group-124', + integrations: { All: true }, + messageId: '2d54ba80-ce5f-4bcb-b1d7-7587e7a865fc', + originalTimestamp: '2020-03-23T18:27:28.983Z', + receivedAt: '2020-03-23T23:57:29.022+05:30', + request_ip: '[::1]:51574', + sentAt: '2020-03-23T18:27:28.983Z', + timestamp: '2020-03-23T23:57:29.022+05:30', + type: 'group', + userId: 'abcd-124', + }, + }, + ], + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Organisation Traits are missing. Aborting.', + statTags: { + destType: 'ZENDESK', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/sources/adjust/data.ts b/test/integrations/sources/adjust/data.ts new file mode 100644 index 0000000000..975543fbec --- /dev/null +++ b/test/integrations/sources/adjust/data.ts @@ -0,0 +1,122 @@ +import utils from '../../../../src/v0/util'; + +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('97fcd7b2-cc24-47d7-b776-057b7b199513'); +}; + +export const data = [ + { + name: 'adjust', + description: 'Simple track call', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + id: 'adjust', + query_parameters: { + gps_adid: ['38400000-8cf0-11bd-b23e-10b96e40000d'], + adid: ['18546f6171f67e29d1cb983322ad1329'], + tracker_token: ['abc'], + custom: ['custom'], + tracker_name: ['dummy'], + created_at: ['1404214665'], + event_name: ['Click'], + }, + updated_at: '2023-02-10T12:16:07.251Z', + created_at: '2023-02-10T12:05:04.402Z', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'Adjust', + }, + device: { + 'id ': '18546f6171f67e29d1cb983322ad1329', + }, + }, + integrations: { + Adjust: false, + }, + type: 'track', + event: 'Click', + originalTimestamp: '2014-07-01T11:37:45.000Z', + timestamp: '2014-07-01T11:37:45.000Z', + properties: { + gps_adid: '38400000-8cf0-11bd-b23e-10b96e40000d', + tracker_token: 'abc', + custom: 'custom', + tracker_name: 'dummy', + }, + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'adjust', + description: 'Simple track call with no query parameters', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + id: 'adjust', + updated_at: '2023-02-10T12:16:07.251Z', + created_at: '2023-02-10T12:05:04.402Z', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + error: 'Query_parameters is missing', + statTags: { + destinationId: 'Non determinable', + errorCategory: 'transformation', + implementation: 'native', + module: 'source', + workspaceId: 'Non determinable', + }, + statusCode: 400, + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, +]; diff --git a/test/integrations/sources/auth0/data.ts b/test/integrations/sources/auth0/data.ts new file mode 100644 index 0000000000..44b511cad2 --- /dev/null +++ b/test/integrations/sources/auth0/data.ts @@ -0,0 +1,1526 @@ +import utils from '../../../../src/v0/util'; + +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('97fcd7b2-cc24-47d7-b776-057b7b199513'); +}; + +export const data = [ + { + name: 'auth0', + description: 'successful signup', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031055712103169676686005480714681762668315934738', + data: { + date: '2022-10-31T05:57:06.859Z', + type: 'ss', + description: '', + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + ip: '35.166.202.113', + user_agent: 'unknown', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + user_id: 'auth0|dummyPassword', + user_name: 'testRudderlabs+21@gmail.com', + strategy: 'auth0', + strategy_type: 'database', + log_id: '90020221031055712103169676686005480714681762668315934738', + }, + }, + { + log_id: '90020221031055712103169676686007898566320991926665347090', + data: { + date: '2022-10-31T05:57:06.874Z', + type: 'sapi', + description: 'Create a User', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + ip: '35.166.202.113', + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + details: { + request: { + ip: '35.166.202.113', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'auth0|dummyPassword', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + scopes: [ + 'create:actions', + 'create:actions_log_sessions', + 'create:client_credentials', + 'create:client_grants', + 'create:clients', + 'create:connections', + 'create:custom_domains', + 'create:email_provider', + 'create:email_templates', + 'create:guardian_enrollment_tickets', + 'create:integrations', + 'create:log_streams', + 'create:organization_connections', + 'create:organization_invitations', + 'create:organization_member_roles', + 'create:organization_members', + 'create:organizations', + 'create:requested_scopes', + 'create:resource_servers', + 'create:roles', + 'create:rules', + 'create:shields', + 'create:signing_keys', + 'create:tenant_invitations', + 'create:test_email_dispatch', + 'create:users', + 'delete:actions', + 'delete:anomaly_blocks', + 'delete:branding', + 'delete:client_credentials', + 'delete:client_grants', + 'delete:clients', + 'delete:connections', + 'delete:custom_domains', + 'delete:device_credentials', + 'delete:email_provider', + 'delete:email_templates', + 'delete:grants', + 'delete:guardian_enrollments', + 'delete:integrations', + 'delete:log_streams', + 'delete:organization_connections', + 'delete:organization_invitations', + 'delete:organization_member_roles', + 'delete:organization_members', + 'delete:organizations', + 'delete:owners', + 'delete:requested_scopes', + 'delete:resource_servers', + 'delete:roles', + 'delete:rules', + 'delete:rules_configs', + 'delete:shields', + 'delete:tenant_invitations', + 'delete:tenant_members', + 'delete:tenants', + 'delete:users', + 'read:actions', + 'read:anomaly_blocks', + 'read:attack_protection', + 'read:branding', + 'read:checks', + 'read:client_credentials', + 'read:client_grants', + 'read:client_keys', + 'read:clients', + 'read:connections', + 'read:custom_domains', + 'read:device_credentials', + 'read:email_provider', + 'read:email_templates', + 'read:email_triggers', + 'read:entity_counts', + 'read:grants', + 'read:guardian_factors', + 'read:insights', + 'read:integrations', + 'read:log_streams', + 'read:logs', + 'read:mfa_policies', + 'read:organization_connections', + 'read:organization_invitations', + 'read:organization_member_roles', + 'read:organization_members', + 'read:organizations', + 'read:prompts', + 'read:requested_scopes', + 'read:resource_servers', + 'read:roles', + 'read:rules', + 'read:rules_configs', + 'read:shields', + 'read:signing_keys', + 'read:stats', + 'read:tenant_invitations', + 'read:tenant_members', + 'read:tenant_settings', + 'read:triggers', + 'read:users', + 'run:checks', + 'update:actions', + 'update:attack_protection', + 'update:branding', + 'update:client_credentials', + 'update:client_grants', + 'update:client_keys', + 'update:clients', + 'update:connections', + 'update:custom_domains', + 'update:email_provider', + 'update:email_templates', + 'update:email_triggers', + 'update:guardian_factors', + 'update:integrations', + 'update:log_streams', + 'update:mfa_policies', + 'update:organization_connections', + 'update:organizations', + 'update:prompts', + 'update:requested_scopes', + 'update:resource_servers', + 'update:roles', + 'update:rules', + 'update:rules_configs', + 'update:shields', + 'update:signing_keys', + 'update:tenant_members', + 'update:tenant_settings', + 'update:triggers', + 'update:users', + ], + }, + }, + body: { + email: 'testRudderlabs+21@gmail.com', + password: 'dummyPassword', + connection: 'Username-Password-Authentication', + }, + path: '/api/v2/users', + query: {}, + method: 'post', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: { + name: 'testRudderlabs+21@gmail.com', + email: 'testRudderlabs+21@gmail.com', + picture: + 'https://s.gravatar.com/avatar/0902f9d02b92aed9f0ac59aaf9475b60?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fbh.png', + user_id: 'auth0|dummyPassword', + nickname: 'testRudderlabs+21', + created_at: '2022-10-31T05:57:06.864Z', + identities: [ + { + user_id: 'auth0|dummyPassword', + isSocial: false, + provider: 'auth0', + connection: 'Username-Password-Authentication', + }, + ], + updated_at: '2022-10-31T05:57:06.864Z', + email_verified: false, + }, + statusCode: 201, + }, + }, + user_id: 'auth0|dummyPassword', + log_id: '90020221031055712103169676686007898566320991926665347090', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'identify', + sentAt: '2022-10-31T05:57:06.859Z', + traits: { + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + }, + userId: 'auth0|dummyPassword', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + traits: { + userId: 'auth0|dummyPassword', + user_name: 'testRudderlabs+21@gmail.com', + }, + library: { + name: 'unknown', + version: 'unknown', + }, + userAgent: 'unknown', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686005480714681762668315934738', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + description: '', + source_type: 'ss', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.859Z', + }, + ], + }, + }, + { + output: { + batch: [ + { + type: 'track', + event: 'Success API Operation', + sentAt: '2022-10-31T05:57:06.874Z', + userId: 'auth0|dummyPassword', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + traits: { + userId: 'auth0|dummyPassword', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686007898566320991926665347090', + details: { + request: { + ip: '35.166.202.113', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'auth0|dummyPassword', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + scopes: [ + 'create:actions', + 'create:actions_log_sessions', + 'create:client_credentials', + 'create:client_grants', + 'create:clients', + 'create:connections', + 'create:custom_domains', + 'create:email_provider', + 'create:email_templates', + 'create:guardian_enrollment_tickets', + 'create:integrations', + 'create:log_streams', + 'create:organization_connections', + 'create:organization_invitations', + 'create:organization_member_roles', + 'create:organization_members', + 'create:organizations', + 'create:requested_scopes', + 'create:resource_servers', + 'create:roles', + 'create:rules', + 'create:shields', + 'create:signing_keys', + 'create:tenant_invitations', + 'create:test_email_dispatch', + 'create:users', + 'delete:actions', + 'delete:anomaly_blocks', + 'delete:branding', + 'delete:client_credentials', + 'delete:client_grants', + 'delete:clients', + 'delete:connections', + 'delete:custom_domains', + 'delete:device_credentials', + 'delete:email_provider', + 'delete:email_templates', + 'delete:grants', + 'delete:guardian_enrollments', + 'delete:integrations', + 'delete:log_streams', + 'delete:organization_connections', + 'delete:organization_invitations', + 'delete:organization_member_roles', + 'delete:organization_members', + 'delete:organizations', + 'delete:owners', + 'delete:requested_scopes', + 'delete:resource_servers', + 'delete:roles', + 'delete:rules', + 'delete:rules_configs', + 'delete:shields', + 'delete:tenant_invitations', + 'delete:tenant_members', + 'delete:tenants', + 'delete:users', + 'read:actions', + 'read:anomaly_blocks', + 'read:attack_protection', + 'read:branding', + 'read:checks', + 'read:client_credentials', + 'read:client_grants', + 'read:client_keys', + 'read:clients', + 'read:connections', + 'read:custom_domains', + 'read:device_credentials', + 'read:email_provider', + 'read:email_templates', + 'read:email_triggers', + 'read:entity_counts', + 'read:grants', + 'read:guardian_factors', + 'read:insights', + 'read:integrations', + 'read:log_streams', + 'read:logs', + 'read:mfa_policies', + 'read:organization_connections', + 'read:organization_invitations', + 'read:organization_member_roles', + 'read:organization_members', + 'read:organizations', + 'read:prompts', + 'read:requested_scopes', + 'read:resource_servers', + 'read:roles', + 'read:rules', + 'read:rules_configs', + 'read:shields', + 'read:signing_keys', + 'read:stats', + 'read:tenant_invitations', + 'read:tenant_members', + 'read:tenant_settings', + 'read:triggers', + 'read:users', + 'run:checks', + 'update:actions', + 'update:attack_protection', + 'update:branding', + 'update:client_credentials', + 'update:client_grants', + 'update:client_keys', + 'update:clients', + 'update:connections', + 'update:custom_domains', + 'update:email_provider', + 'update:email_templates', + 'update:email_triggers', + 'update:guardian_factors', + 'update:integrations', + 'update:log_streams', + 'update:mfa_policies', + 'update:organization_connections', + 'update:organizations', + 'update:prompts', + 'update:requested_scopes', + 'update:resource_servers', + 'update:roles', + 'update:rules', + 'update:rules_configs', + 'update:shields', + 'update:signing_keys', + 'update:tenant_members', + 'update:tenant_settings', + 'update:triggers', + 'update:users', + ], + }, + }, + body: { + email: 'testRudderlabs+21@gmail.com', + password: 'dummyPassword', + connection: 'Username-Password-Authentication', + }, + path: '/api/v2/users', + query: {}, + method: 'post', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: { + name: 'testRudderlabs+21@gmail.com', + email: 'testRudderlabs+21@gmail.com', + picture: + 'https://s.gravatar.com/avatar/0902f9d02b92aed9f0ac59aaf9475b60?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fbh.png', + user_id: 'auth0|dummyPassword', + nickname: 'testRudderlabs+21', + created_at: '2022-10-31T05:57:06.864Z', + identities: [ + { + user_id: 'auth0|dummyPassword', + isSocial: false, + provider: 'auth0', + connection: 'Username-Password-Authentication', + }, + ], + updated_at: '2022-10-31T05:57:06.864Z', + email_verified: false, + }, + statusCode: 201, + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + description: 'Create a User', + source_type: 'sapi', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.874Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'add member to an organization', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031061004280169676882609459981150114445973782546', + data: { + date: '2022-10-31T06:09:59.135Z', + type: 'sapi', + description: 'Add members to an organization', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + ip: '35.167.74.121', + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + details: { + request: { + ip: '35.167.74.121', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'google-oauth2|123456', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + }, + }, + body: { + members: ['auth0|123456'], + }, + path: '/api/v2/organizations/org_eoe8p2atZ7furBxg/members', + query: {}, + method: 'post', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: {}, + statusCode: 204, + }, + }, + user_id: 'google-oauth2|123456', + log_id: '90020221031061004280169676882609459981150114445973782546', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'group', + sentAt: '2022-10-31T06:09:59.135Z', + userId: 'google-oauth2|123456', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + traits: { + userId: 'google-oauth2|123456', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + request_ip: '35.167.74.121', + integration: { + name: 'Auth0', + }, + }, + groupId: 'org_eoe8p2atZ7furBxg', + properties: { + log_id: '90020221031061004280169676882609459981150114445973782546', + details: { + request: { + ip: '35.167.74.121', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'google-oauth2|123456', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + }, + }, + body: { + members: ['auth0|123456'], + }, + path: '/api/v2/organizations/org_eoe8p2atZ7furBxg/members', + query: {}, + method: 'post', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: {}, + statusCode: 204, + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + description: 'Add members to an organization', + source_type: 'sapi', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T06:09:59.135Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'update tenant settings', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031061527239169676960191065529099349299958906898', + data: { + date: '2022-10-31T06:15:25.201Z', + type: 'sapi', + description: 'Update tenant settings', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + ip: '35.160.3.103', + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + details: { + request: { + ip: '35.160.3.103', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'google-oauth2|123456', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + scopes: [ + 'create:actions', + 'create:actions_log_sessions', + 'create:client_credentials', + 'create:client_grants', + 'create:clients', + 'create:connections', + 'create:custom_domains', + 'create:email_provider', + 'create:email_templates', + 'create:guardian_enrollment_tickets', + 'create:integrations', + 'create:log_streams', + 'create:organization_connections', + 'create:organization_invitations', + 'create:organization_member_roles', + 'create:organization_members', + 'create:organizations', + 'create:requested_scopes', + 'create:resource_servers', + 'create:roles', + 'create:rules', + 'create:shields', + 'create:signing_keys', + 'create:tenant_invitations', + 'create:test_email_dispatch', + 'create:users', + 'delete:actions', + 'delete:anomaly_blocks', + 'delete:branding', + 'delete:client_credentials', + 'delete:client_grants', + 'delete:clients', + 'delete:connections', + 'delete:custom_domains', + 'delete:device_credentials', + 'delete:email_provider', + 'delete:email_templates', + 'delete:grants', + 'delete:guardian_enrollments', + 'delete:integrations', + 'delete:log_streams', + 'delete:organization_connections', + 'delete:organization_invitations', + 'delete:organization_member_roles', + 'delete:organization_members', + 'delete:organizations', + 'delete:owners', + 'delete:requested_scopes', + 'delete:resource_servers', + 'delete:roles', + 'delete:rules', + 'delete:rules_configs', + 'delete:shields', + 'delete:tenant_invitations', + 'delete:tenant_members', + 'delete:tenants', + 'delete:users', + 'read:actions', + 'read:anomaly_blocks', + 'read:attack_protection', + 'read:branding', + 'read:checks', + 'read:client_credentials', + 'read:client_grants', + 'read:client_keys', + 'read:clients', + 'read:connections', + 'read:custom_domains', + 'read:device_credentials', + 'read:email_provider', + 'read:email_templates', + 'read:email_triggers', + 'read:entity_counts', + 'read:grants', + 'read:guardian_factors', + 'read:insights', + 'read:integrations', + 'read:log_streams', + 'read:logs', + 'read:mfa_policies', + 'read:organization_connections', + 'read:organization_invitations', + 'read:organization_member_roles', + 'read:organization_members', + 'read:organizations', + 'read:prompts', + 'read:requested_scopes', + 'read:resource_servers', + 'read:roles', + 'read:rules', + 'read:rules_configs', + 'read:shields', + 'read:signing_keys', + 'read:stats', + 'read:tenant_invitations', + 'read:tenant_members', + 'read:tenant_settings', + 'read:triggers', + 'read:users', + 'run:checks', + 'update:actions', + 'update:attack_protection', + 'update:branding', + 'update:client_credentials', + 'update:client_grants', + 'update:client_keys', + 'update:clients', + 'update:connections', + 'update:custom_domains', + 'update:email_provider', + 'update:email_templates', + 'update:email_triggers', + 'update:guardian_factors', + 'update:integrations', + 'update:log_streams', + 'update:mfa_policies', + 'update:organization_connections', + 'update:organizations', + 'update:prompts', + 'update:requested_scopes', + 'update:resource_servers', + 'update:roles', + 'update:rules', + 'update:rules_configs', + 'update:shields', + 'update:signing_keys', + 'update:tenant_members', + 'update:tenant_settings', + 'update:triggers', + 'update:users', + ], + }, + }, + body: { + picture_url: '', + support_url: '', + friendly_name: 'mecro-action', + support_email: 'support@test.com', + }, + path: '/api/v2/tenants/settings', + query: {}, + method: 'patch', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: { + flags: { + enable_sso: true, + universal_login: true, + disable_impersonation: true, + allow_changing_enable_sso: false, + revoke_refresh_token_grant: false, + disable_clickjack_protection_headers: false, + new_universal_login_experience_enabled: true, + enforce_client_authentication_on_passwordless_start: true, + cannot_change_enforce_client_authentication_on_passwordless_start: true, + }, + picture_url: '', + support_url: '', + friendly_name: 'mecro-action', + support_email: 'support@test.com', + enabled_locales: ['en'], + sandbox_version: '16', + universal_login: {}, + }, + statusCode: 200, + }, + }, + user_id: 'google-oauth2|123456', + log_id: '90020221031061527239169676960191065529099349299958906898', + }, + }, + { + log_id: '90020221031061530247169676961198100736838335677367058450', + data: { + date: '2022-10-31T06:15:25.196Z', + type: 'gd_tenant_update', + description: 'Guardian - Updates tenant settings', + ip: '35.160.3.103', + details: { + request: { + ip: '35.160.3.103', + auth: { + scopes: [ + 'read:authenticators', + 'remove:authenticators', + 'update:authenticators', + 'create:authenticators', + 'read:enrollments', + 'delete:enrollments', + 'read:factors', + 'update:factors', + 'update:tenant_settings', + 'update:users', + 'create:enrollment_tickets', + 'create:users', + ], + subject: 'google-oauth2|123456', + strategy: 'jwt_api2_internal_token', + }, + body: { + picture_url: '[REDACTED]', + friendly_name: '[REDACTED]', + }, + path: '/api/tenants/settings', + query: {}, + method: 'PATCH', + }, + response: { + body: { + name: 'dev-cu4jy2zgao6yx15x', + picture_url: '[REDACTED]', + friendly_name: '[REDACTED]', + guardian_mfa_page: '[REDACTED]', + }, + statusCode: 200, + }, + }, + user_id: 'google-oauth2|123456', + log_id: '90020221031061530247169676961198100736838335677367058450', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'track', + event: 'Success API Operation', + sentAt: '2022-10-31T06:15:25.201Z', + userId: 'google-oauth2|123456', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + traits: { + userId: 'google-oauth2|123456', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + request_ip: '35.160.3.103', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031061527239169676960191065529099349299958906898', + details: { + request: { + ip: '35.160.3.103', + auth: { + user: { + name: 'rudder test', + email: 'test@rudderstack.com', + user_id: 'google-oauth2|123456', + }, + strategy: 'jwt', + credentials: { + jti: '571921bf7833a97efabf08d765a0ec8f', + scopes: [ + 'create:actions', + 'create:actions_log_sessions', + 'create:client_credentials', + 'create:client_grants', + 'create:clients', + 'create:connections', + 'create:custom_domains', + 'create:email_provider', + 'create:email_templates', + 'create:guardian_enrollment_tickets', + 'create:integrations', + 'create:log_streams', + 'create:organization_connections', + 'create:organization_invitations', + 'create:organization_member_roles', + 'create:organization_members', + 'create:organizations', + 'create:requested_scopes', + 'create:resource_servers', + 'create:roles', + 'create:rules', + 'create:shields', + 'create:signing_keys', + 'create:tenant_invitations', + 'create:test_email_dispatch', + 'create:users', + 'delete:actions', + 'delete:anomaly_blocks', + 'delete:branding', + 'delete:client_credentials', + 'delete:client_grants', + 'delete:clients', + 'delete:connections', + 'delete:custom_domains', + 'delete:device_credentials', + 'delete:email_provider', + 'delete:email_templates', + 'delete:grants', + 'delete:guardian_enrollments', + 'delete:integrations', + 'delete:log_streams', + 'delete:organization_connections', + 'delete:organization_invitations', + 'delete:organization_member_roles', + 'delete:organization_members', + 'delete:organizations', + 'delete:owners', + 'delete:requested_scopes', + 'delete:resource_servers', + 'delete:roles', + 'delete:rules', + 'delete:rules_configs', + 'delete:shields', + 'delete:tenant_invitations', + 'delete:tenant_members', + 'delete:tenants', + 'delete:users', + 'read:actions', + 'read:anomaly_blocks', + 'read:attack_protection', + 'read:branding', + 'read:checks', + 'read:client_credentials', + 'read:client_grants', + 'read:client_keys', + 'read:clients', + 'read:connections', + 'read:custom_domains', + 'read:device_credentials', + 'read:email_provider', + 'read:email_templates', + 'read:email_triggers', + 'read:entity_counts', + 'read:grants', + 'read:guardian_factors', + 'read:insights', + 'read:integrations', + 'read:log_streams', + 'read:logs', + 'read:mfa_policies', + 'read:organization_connections', + 'read:organization_invitations', + 'read:organization_member_roles', + 'read:organization_members', + 'read:organizations', + 'read:prompts', + 'read:requested_scopes', + 'read:resource_servers', + 'read:roles', + 'read:rules', + 'read:rules_configs', + 'read:shields', + 'read:signing_keys', + 'read:stats', + 'read:tenant_invitations', + 'read:tenant_members', + 'read:tenant_settings', + 'read:triggers', + 'read:users', + 'run:checks', + 'update:actions', + 'update:attack_protection', + 'update:branding', + 'update:client_credentials', + 'update:client_grants', + 'update:client_keys', + 'update:clients', + 'update:connections', + 'update:custom_domains', + 'update:email_provider', + 'update:email_templates', + 'update:email_triggers', + 'update:guardian_factors', + 'update:integrations', + 'update:log_streams', + 'update:mfa_policies', + 'update:organization_connections', + 'update:organizations', + 'update:prompts', + 'update:requested_scopes', + 'update:resource_servers', + 'update:roles', + 'update:rules', + 'update:rules_configs', + 'update:shields', + 'update:signing_keys', + 'update:tenant_members', + 'update:tenant_settings', + 'update:triggers', + 'update:users', + ], + }, + }, + body: { + picture_url: '', + support_url: '', + friendly_name: 'mecro-action', + support_email: 'support@test.com', + }, + path: '/api/v2/tenants/settings', + query: {}, + method: 'patch', + channel: 'https://manage.auth0.com/', + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + }, + response: { + body: { + flags: { + enable_sso: true, + universal_login: true, + disable_impersonation: true, + allow_changing_enable_sso: false, + revoke_refresh_token_grant: false, + disable_clickjack_protection_headers: false, + new_universal_login_experience_enabled: true, + enforce_client_authentication_on_passwordless_start: true, + cannot_change_enforce_client_authentication_on_passwordless_start: true, + }, + picture_url: '', + support_url: '', + friendly_name: 'mecro-action', + support_email: 'support@test.com', + enabled_locales: ['en'], + sandbox_version: '16', + universal_login: {}, + }, + statusCode: 200, + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + description: 'Update tenant settings', + source_type: 'sapi', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T06:15:25.201Z', + }, + ], + }, + }, + { + output: { + batch: [ + { + type: 'track', + event: 'Guardian tenant update', + sentAt: '2022-10-31T06:15:25.196Z', + userId: 'google-oauth2|123456', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + traits: { + userId: 'google-oauth2|123456', + }, + request_ip: '35.160.3.103', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031061530247169676961198100736838335677367058450', + details: { + request: { + ip: '35.160.3.103', + auth: { + scopes: [ + 'read:authenticators', + 'remove:authenticators', + 'update:authenticators', + 'create:authenticators', + 'read:enrollments', + 'delete:enrollments', + 'read:factors', + 'update:factors', + 'update:tenant_settings', + 'update:users', + 'create:enrollment_tickets', + 'create:users', + ], + subject: 'google-oauth2|123456', + strategy: 'jwt_api2_internal_token', + }, + body: { + picture_url: '[REDACTED]', + friendly_name: '[REDACTED]', + }, + path: '/api/tenants/settings', + query: {}, + method: 'PATCH', + }, + response: { + body: { + name: 'dev-cu4jy2zgao6yx15x', + picture_url: '[REDACTED]', + friendly_name: '[REDACTED]', + guardian_mfa_page: '[REDACTED]', + }, + statusCode: 200, + }, + }, + description: 'Guardian - Updates tenant settings', + source_type: 'gd_tenant_update', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T06:15:25.196Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'missing userId', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031055712103169676686005480714681762668315934738', + data: { + date: '2022-10-31T05:57:06.859Z', + type: 'ss', + description: '', + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + ip: '35.166.202.113', + user_agent: 'unknown', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + user_id: '', + user_name: 'testRudderlabs+21@gmail.com', + strategy: 'auth0', + strategy_type: 'database', + log_id: '90020221031055712103169676686005480714681762668315934738', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'identify', + sentAt: '2022-10-31T05:57:06.859Z', + traits: { + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + }, + userId: '', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + traits: { + userId: '', + user_name: 'testRudderlabs+21@gmail.com', + }, + library: { + name: 'unknown', + version: 'unknown', + }, + userAgent: 'unknown', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686005480714681762668315934738', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + description: '', + source_type: 'ss', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.859Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'missing userId for all the requests in a batch', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + log_id: '90020221031055712103169676686005480714681762668315934738', + data: { + date: '2022-10-31T05:57:06.859Z', + type: 'ss', + description: '', + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + ip: '35.166.202.113', + user_agent: 'unknown', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + user_id: '', + user_name: 'testRudderlabs+21@gmail.com', + strategy: 'auth0', + strategy_type: 'database', + log_id: '90020221031055712103169676686005480714681762668315934738', + }, + }, + { + log_id: '90020221031055712103169676686007898566320991926665347090', + data: { + date: '2022-10-31T05:57:06.874Z', + type: 'sapi', + description: 'Create a User', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + ip: '35.166.202.113', + user_agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + log_id: '90020221031055712103169676686007898566320991926665347090', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + type: 'identify', + userId: '', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + sentAt: '2022-10-31T05:57:06.859Z', + traits: { + connection: 'Username-Password-Authentication', + connection_id: 'con_djwCjiwyID0vZy1S', + }, + context: { + traits: { + userId: '', + user_name: 'testRudderlabs+21@gmail.com', + }, + library: { + name: 'unknown', + version: 'unknown', + }, + userAgent: 'unknown', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686005480714681762668315934738', + details: { + body: { + email: 'testRudderlabs+21@gmail.com', + tenant: 'dev-cu4jy2zgao6yx15x', + password: 'dummyPassword', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + connection: 'Username-Password-Authentication', + }, + }, + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: 'All Applications', + description: '', + source_type: 'ss', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.859Z', + }, + ], + }, + }, + { + output: { + batch: [ + { + type: 'track', + event: 'Success API Operation', + sentAt: '2022-10-31T05:57:06.874Z', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', + request_ip: '35.166.202.113', + integration: { + name: 'Auth0', + }, + }, + properties: { + log_id: '90020221031055712103169676686007898566320991926665347090', + client_id: 'vQcJNDTxsM1W72eHFonRJdzyOvawlwIt', + client_name: '', + description: 'Create a User', + source_type: 'sapi', + }, + integrations: { + Auth0: false, + }, + originalTimestamp: '2022-10-31T05:57:06.874Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'auth0', + description: 'empty batch', + module: 'source', + version: 'v0', + input: { + request: { + body: [], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, +]; diff --git a/test/integrations/sources/close_crm/data.ts b/test/integrations/sources/close_crm/data.ts new file mode 100644 index 0000000000..08b9d13a1b --- /dev/null +++ b/test/integrations/sources/close_crm/data.ts @@ -0,0 +1,395 @@ +import utils from '../../../../src/v0/util'; + +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('97fcd7b2-cc24-47d7-b776-057b7b199513'); +}; + +export const data = [ + { + name: 'close_crm', + description: 'lead update', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + event: { + date_created: '2019-01-15T12:48:23.395000', + meta: { + request_method: 'PUT', + request_path: '/api/v1/opportunity/object_id/', + }, + id: 'ev_123', + action: 'updated', + date_updated: '2019-01-15T12:48:23.395000', + changed_fields: [ + 'confidence', + 'date_updated', + 'status_id', + 'status_label', + 'status_type', + ], + previous_data: { + status_type: 'active', + confidence: 70, + date_updated: '2019-01-15T12:47:39.873000+00:00', + status_id: 'stat_123', + status_label: 'Active', + }, + organization_id: 'orga_123', + data: { + contact_name: 'Mr. Jones', + user_name: 'Joe Kemp', + value_period: 'one_time', + updated_by_name: 'Joe Kemp', + date_created: '2019-01-15T12:41:24.496000+00:00', + user_id: 'user_123', + updated_by: 'user_123', + value_currency: 'USD', + organization_id: 'orga_123', + status_label: 'Won', + contact_id: 'cont_123', + status_type: 'won', + created_by_name: 'Joe Kemp', + id: 'id_12', + lead_name: 'KLine', + date_lost: null, + note: '', + date_updated: '2019-01-15T12:48:23.392000+00:00', + status_id: 'stat_12', + value: 100000, + created_by: 'user_123', + value_formatted: '$1,000', + date_won: '2019-01-15', + lead_id: 'lead_123', + confidence: 100, + }, + request_id: 'req_123', + object_id: 'object_id', + user_id: 'user_123', + object_type: 'opportunity', + lead_id: 'lead_123', + }, + subscription_id: 'whsub_123', + }, + source: {}, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'CloseCRM', + }, + }, + integrations: { + CloseCRM: false, + }, + type: 'track', + event: 'opportunity updated', + messageId: 'ev_123', + userId: 'lead_123', + originalTimestamp: '2019-01-TuT12:48:23.395+00:00', + properties: { + date_created: '2019-01-15T12:48:23.395000', + meta: { + request_method: 'PUT', + request_path: '/api/v1/opportunity/object_id/', + }, + id: 'ev_123', + action: 'updated', + date_updated: '2019-01-15T12:48:23.395000', + organization_id: 'orga_123', + data: { + contact_name: 'Mr. Jones', + user_name: 'Joe Kemp', + value_period: 'one_time', + updated_by_name: 'Joe Kemp', + date_created: '2019-01-15T12:41:24.496000+00:00', + user_id: 'user_123', + updated_by: 'user_123', + value_currency: 'USD', + organization_id: 'orga_123', + status_label: 'Won', + contact_id: 'cont_123', + status_type: 'won', + created_by_name: 'Joe Kemp', + id: 'id_12', + lead_name: 'KLine', + note: '', + date_updated: '2019-01-15T12:48:23.392000+00:00', + status_id: 'stat_12', + value: 100000, + created_by: 'user_123', + value_formatted: '$1,000', + date_won: '2019-01-15', + lead_id: 'lead_123', + confidence: 100, + }, + request_id: 'req_123', + object_id: 'object_id', + user_id: 'user_123', + object_type: 'opportunity', + lead_id: 'lead_123', + subscription_id: 'whsub_123', + }, + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'close_crm', + description: 'group creation', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + subscription_id: 'whsub_123', + event: { + id: 'ev_123', + date_created: '2024-06-13T03:53:33.917000', + date_updated: '2024-06-13T03:53:33.917000', + organization_id: 'orga_123', + user_id: 'user_123', + request_id: 'req_123', + api_key_id: null, + oauth_client_id: null, + oauth_scope: null, + object_type: 'group', + object_id: 'group_123', + lead_id: null, + action: 'created', + changed_fields: [], + meta: { + request_path: '/api/v1/graphql/', + request_method: 'POST', + }, + data: { + id: 'group_123', + name: 'Test group', + members: [ + { + user_id: 'user_123', + }, + ], + }, + previous_data: {}, + }, + }, + source: {}, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', + context: { + integration: { + name: 'CloseCRM', + }, + library: { + name: 'unknown', + version: 'unknown', + }, + }, + event: 'group created', + integrations: { + CloseCRM: false, + }, + messageId: 'ev_123', + originalTimestamp: '2024-06-ThT03:53:33.917+00:00', + properties: { + action: 'created', + data: { + id: 'group_123', + members: [ + { + user_id: 'user_123', + }, + ], + name: 'Test group', + }, + date_created: '2024-06-13T03:53:33.917000', + date_updated: '2024-06-13T03:53:33.917000', + id: 'ev_123', + meta: { + request_method: 'POST', + request_path: '/api/v1/graphql/', + }, + object_id: 'group_123', + object_type: 'group', + organization_id: 'orga_123', + request_id: 'req_123', + subscription_id: 'whsub_123', + user_id: 'user_123', + }, + type: 'track', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'close_crm', + description: 'lead deletion', + module: 'source', + version: 'v1', + input: { + request: { + body: [ + { + event: { + subscription_id: 'whsub_123', + event: { + id: 'ev_123', + date_created: '2024-06-14T05:16:04.138000', + date_updated: '2024-06-14T05:16:04.138000', + organization_id: 'orga_123', + user_id: 'user_123', + request_id: 'req_123', + api_key_id: 'api_123', + oauth_client_id: null, + oauth_scope: null, + object_type: 'lead', + object_id: 'lead_123', + lead_id: 'lead_123', + action: 'deleted', + changed_fields: [], + meta: { + request_path: '/api/v1/lead/lead_123/', + request_method: 'DELETE', + }, + data: {}, + previous_data: { + created_by_name: 'Rudder User', + addresses: [], + description: '', + url: null, + date_created: '2024-06-14T05:13:42.239000+00:00', + status_id: 'stat_123', + contact_ids: ['cont_123'], + id: 'lead_12', + date_updated: '2024-06-14T05:13:42.262000+00:00', + updated_by_name: 'Rudder User', + status_label: 'Potential', + name: 'test name', + display_name: 'test name', + organization_id: 'orga_123', + updated_by: 'user_123', + created_by: 'user_123', + }, + }, + }, + source: {}, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'CloseCRM', + }, + }, + integrations: { + CloseCRM: false, + }, + type: 'track', + event: 'lead deleted', + userId: 'lead_123', + messageId: 'ev_123', + originalTimestamp: '2024-06-FrT05:16:04.138+00:00', + properties: { + id: 'ev_123', + date_created: '2024-06-14T05:16:04.138000', + date_updated: '2024-06-14T05:16:04.138000', + organization_id: 'orga_123', + user_id: 'user_123', + request_id: 'req_123', + api_key_id: 'api_123', + object_type: 'lead', + object_id: 'lead_123', + lead_id: 'lead_123', + action: 'deleted', + meta: { + request_path: '/api/v1/lead/lead_123/', + request_method: 'DELETE', + }, + data: {}, + subscription_id: 'whsub_123', + }, + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, +]; diff --git a/test/integrations/sources/revenuecat/data.ts b/test/integrations/sources/revenuecat/data.ts index 4963781763..2762bac5b2 100644 --- a/test/integrations/sources/revenuecat/data.ts +++ b/test/integrations/sources/revenuecat/data.ts @@ -1,3 +1,9 @@ +import utils from '../../../../src/v0/util'; + +const defaultMockFns = () => { + jest.spyOn(utils, 'generateUUID').mockReturnValue('97fcd7b2-cc24-47d7-b776-057b7b199513'); +}; + export const data = [ { name: 'revenuecat', @@ -144,6 +150,7 @@ export const data = [ type: 'TEST', }, event: 'TEST', + userId: 'f8e14f51-0c76-49ba-8d67-c229f1875dd9', messageId: '8CF0CD6C-CAF3-41FB-968A-661938235AF0', originalTimestamp: '2023-10-29T22:06:57.232Z', sentAt: '2023-10-29T22:06:57.232Z', @@ -154,6 +161,9 @@ export const data = [ ], }, }, + mockFns: () => { + defaultMockFns(); + }, }, { name: 'revenuecat', @@ -272,6 +282,130 @@ export const data = [ type: 'INITIAL_PURCHASE', }, event: 'INITIAL_PURCHASE', + userId: 'yourCustomerAppUserID', + messageId: 'UniqueIdentifierOfEvent', + originalTimestamp: '2020-06-02T18:17:35.319Z', + sentAt: '2020-06-02T18:17:35.319Z', + }, + ], + }, + }, + ], + }, + }, + mockFns: () => { + defaultMockFns(); + }, + }, + { + name: 'revenuecat', + description: 'Purchase event with anonymous user', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + api_version: '1.0', + event: { + aliases: ['yourCustomerAliasedID', 'yourCustomerAliasedID'], + app_id: 'yourAppID', + commission_percentage: 0.3, + country_code: 'US', + currency: 'USD', + entitlement_id: 'pro_cat', + entitlement_ids: ['pro_cat'], + environment: 'PRODUCTION', + event_timestamp_ms: 1591121855319, + expiration_at_ms: 1591726653000, + id: 'UniqueIdentifierOfEvent', + is_family_share: false, + offer_code: 'free_month', + original_transaction_id: '1530648507000', + period_type: 'NORMAL', + presented_offering_id: 'OfferingID', + price: 2.49, + price_in_purchased_currency: 2.49, + product_id: 'onemonth_no_trial', + purchased_at_ms: 1591121853000, + store: 'APP_STORE', + subscriber_attributes: { + '$Favorite Cat': { + updated_at_ms: 1581121853000, + value: 'Garfield', + }, + }, + takehome_percentage: 0.7, + tax_percentage: 0.3, + transaction_id: '170000869511114', + type: 'INITIAL_PURCHASE', + }, + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'RevenueCat', + }, + }, + integrations: { + RevenueCat: false, + }, + type: 'track', + properties: { + aliases: ['yourCustomerAliasedID', 'yourCustomerAliasedID'], + appId: 'yourAppID', + commissionPercentage: 0.3, + countryCode: 'US', + currency: 'USD', + entitlementId: 'pro_cat', + entitlementIds: ['pro_cat'], + environment: 'PRODUCTION', + eventTimestampMs: 1591121855319, + expirationAtMs: 1591726653000, + id: 'UniqueIdentifierOfEvent', + isFamilyShare: false, + offerCode: 'free_month', + originalTransactionId: '1530648507000', + periodType: 'NORMAL', + presentedOfferingId: 'OfferingID', + price: 2.49, + priceInPurchasedCurrency: 2.49, + productId: 'onemonth_no_trial', + purchasedAtMs: 1591121853000, + store: 'APP_STORE', + subscriberAttributes: { + '$Favorite Cat': { + updated_at_ms: 1581121853000, + value: 'Garfield', + }, + }, + takehomePercentage: 0.7, + taxPercentage: 0.3, + transactionId: '170000869511114', + type: 'INITIAL_PURCHASE', + }, + event: 'INITIAL_PURCHASE', + userId: '', + anonymousId: '97fcd7b2-cc24-47d7-b776-057b7b199513', messageId: 'UniqueIdentifierOfEvent', originalTimestamp: '2020-06-02T18:17:35.319Z', sentAt: '2020-06-02T18:17:35.319Z', @@ -282,5 +416,8 @@ export const data = [ ], }, }, + mockFns: () => { + defaultMockFns(); + }, }, ]; diff --git a/test/integrations/sources/slack/data.ts b/test/integrations/sources/slack/data.ts new file mode 100644 index 0000000000..def8a63408 --- /dev/null +++ b/test/integrations/sources/slack/data.ts @@ -0,0 +1,282 @@ +export const data = [ + { + name: 'slack', + description: 'Webhook url verificatin event', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + token: 'Jhj5dZrVaK7ZwHHjRyZWjbDl', + challenge: '3eZbrw1aB10FEMAGAZd4FyFQ', + type: 'url_verification', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + outputToSource: { + body: 'eyJjaGFsbGVuZ2UiOiIzZVpicncxYUIxMEZFTUFHQVpkNEZ5RlEifQ==', + contentType: 'application/json', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'slack', + description: 'Team joined event', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + event: { + type: 'team_join', + user: { + id: 'W012CDE', + name: 'johnd', + real_name: 'John Doe', + }, + }, + type: 'event_callback', + event_id: 'Ev06TJ0NG5', + event_time: 1709441309, + token: 'REm276ggfh72Lq', + team_id: 'T0GFJL5J7', + context_team_id: 'T0GFJL5J7', + context_enterprise_id: null, + api_app_id: 'B02SJMHRR', + authorizations: [ + { + enterprise_id: null, + team_id: 'T0GFJL5J7', + user_id: 'U04G7H550', + is_bot: true, + is_enterprise_install: false, + }, + ], + is_ext_shared_channel: false, + event_context: 'eJldCI65436EUEpMSFhgfhg76joiQzAxRTRQTEIxMzUifQ', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'SLACK', + }, + externalId: [ + { + type: 'slackUserId', + id: 'W012CDE', + }, + ], + }, + integrations: { + SLACK: false, + }, + type: 'identify', + event: 'Team Join', + anonymousId: '2bc5ae2825a712d3d154cbdacb86ac16c278', + originalTimestamp: '2024-03-03T04:48:29.000Z', + sentAt: '2024-03-03T04:48:29.000Z', + properties: { + type: 'team_join', + user: { + id: 'W012CDE', + name: 'johnd', + real_name: 'John Doe', + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'slack', + description: 'Message event', + module: 'source', + version: 'v0', + input: { + request: { + body: [ + { + event: { + user: 'U04G7H550', + type: 'message', + ts: '1709441309.308399', + client_msg_id: '834r664e-ec75-445d-t5c6-b873a07y9c81', + text: 'What is the pricing of product X', + team: 'T0GFJL5J7', + thread_ts: '1709407304.839329', + parent_user_id: 'U06P6LQTPV', + blocks: [ + { + type: 'rich_text', + block_id: 'xGKJl', + elements: [ + { + type: 'rich_text_section', + elements: [ + { + type: 'text', + text: 'What is the pricing of product X', + }, + { + type: 'channel', + channel_id: 'C03CDQTPI65', + }, + { + type: 'text', + text: ' to do this', + }, + ], + }, + ], + }, + ], + channel: 'C03CDQTPI65', + event_ts: '1709441309.308399', + channel_type: 'channel', + }, + type: 'event_callback', + event_id: 'EvY5JTJ0NG5', + event_time: 1709441309, + token: 'REm2987dqtpi72Lq', + team_id: 'T0GFJL5J7', + context_team_id: 'T01gqtPIL5J7', + context_enterprise_id: null, + api_app_id: 'A04QTPIHRR', + authorizations: [ + { + enterprise_id: null, + team_id: 'T0GFJL5J7', + user_id: 'W012CDE', + is_bot: true, + is_enterprise_install: false, + }, + ], + is_ext_shared_channel: false, + event_context: '4-wd6joiQfdgTRQTpIzdfifQ', + }, + ], + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + batch: [ + { + context: { + library: { + name: 'unknown', + version: 'unknown', + }, + integration: { + name: 'SLACK', + }, + externalId: [ + { + type: 'slackUserId', + id: 'U04G7H550', + }, + ], + }, + integrations: { + SLACK: false, + }, + type: 'track', + event: 'Message', + anonymousId: '7509c04f547b05afb6838aa742f4910263d6', + originalTimestamp: '2024-03-03T04:48:29.308Z', + sentAt: '2024-03-03T04:48:29.000Z', + properties: { + user: 'U04G7H550', + type: 'message', + ts: '1709441309.308399', + client_msg_id: '834r664e-ec75-445d-t5c6-b873a07y9c81', + text: 'What is the pricing of product X', + team: 'T0GFJL5J7', + thread_ts: '1709407304.839329', + parent_user_id: 'U06P6LQTPV', + blocks: [ + { + type: 'rich_text', + block_id: 'xGKJl', + elements: [ + { + type: 'rich_text_section', + elements: [ + { + type: 'text', + text: 'What is the pricing of product X', + }, + { + type: 'channel', + channel_id: 'C03CDQTPI65', + }, + { + type: 'text', + text: ' to do this', + }, + ], + }, + ], + }, + ], + channel: 'C03CDQTPI65', + event_ts: '1709441309.308399', + channel_type: 'channel', + }, + }, + ], + }, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/testTypes.ts b/test/integrations/testTypes.ts index a46277d552..3df732d84f 100644 --- a/test/integrations/testTypes.ts +++ b/test/integrations/testTypes.ts @@ -37,6 +37,7 @@ export interface mockType { } export interface TestCaseData { + id?: string; name: string; description: string; scenario?: string; @@ -77,6 +78,7 @@ export type ProcessorTestData = { body: ProcessorTransformationResponse[]; }; }; + mockFns?: (mockAdapter: MockAdapter) => {}; }; export type RouterTestData = { id: string; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index 2abe4c6d9a..a6f0720e37 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -1,4 +1,3 @@ -import { z } from 'zod'; import { globSync } from 'glob'; import { join } from 'path'; import { MockHttpCallsData, TestCaseData } from './testTypes'; @@ -6,24 +5,18 @@ import MockAdapter from 'axios-mock-adapter'; import isMatch from 'lodash/isMatch'; import { OptionValues } from 'commander'; import { removeUndefinedAndNullValues } from '@rudderstack/integrations-lib'; -import { - Destination, - Metadata, - ProxyMetdata, - ProxyV0Request, - ProxyV1Request, -} from '../../src/types'; +import tags from '../../src/v0/util/tags'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; +import { Destination, ProxyMetdata, ProxyV0Request, ProxyV1Request } from '../../src/types'; import { DeliveryV0ResponseSchema, DeliveryV0ResponseSchemaForOauth, DeliveryV1ResponseSchema, DeliveryV1ResponseSchemaForOauth, ProcessorTransformationResponseListSchema, - ProcessorTransformationResponseSchema, ProxyV0RequestSchema, ProxyV1RequestSchema, RouterTransformationResponseListSchema, - RouterTransformationResponseSchema, } from '../../src/types/zodTypes'; const generateAlphanumericId = (size = 36) => @@ -104,6 +97,49 @@ export const overrideDestination = (destination: Destination, overrideConfigValu }); }; +export const produceTestData = (testData: TestCaseData[], filterKeys = []) => { + const result: any = []; + testData.forEach((tcData) => { + let events; + try { + switch (tcData.feature) { + case tags.FEATURES.PROCESSOR: + events = tcData.input.request.body; + break; + case tags.FEATURES.BATCH: + events = tcData.input.request.body.input; + break; + case tags.FEATURES.ROUTER: + events = tcData.input.request.body.input; + break; + } + } catch (e) { + throw new Error( + `Error in producing test data for destination:${tcData.name}, id:${tcData.id}: ${e}`, + ); + } + + events.forEach((event) => { + const { message } = event; + // remove unwanted keys + filterKeys.forEach((key) => { + delete message[key]; + }); + result.push(message); + }); + }); + + // write the data to a file + + // create directory if not exists + const dir = join(__dirname, '../../temp'); + if (!existsSync(dir)) { + mkdirSync(dir); + } + writeFileSync(join(__dirname, '../../temp/test_data.json'), JSON.stringify(result, null, 2)); + console.log('Data generated successfully at temp/test_data.json'); +}; + export const generateIndentifyPayload: any = (parametersOverride: any) => { const payload = { type: 'identify', @@ -180,6 +216,7 @@ export const generateTrackPayload: any = (parametersOverride: any) => { campaign: {}, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + dataProcessingOptions: parametersOverride.context.dataProcessingOptions, }), rudderId: parametersOverride.rudderId || generateAlphanumericId(36), messageId: parametersOverride.messageId || generateAlphanumericId(36), @@ -206,6 +243,9 @@ export const generateSimplifiedTrackPayload: any = (parametersOverride: any) => context: removeUndefinedAndNullValues({ externalId: parametersOverride.context.externalId, traits: parametersOverride.context.traits, + device: parametersOverride.context.device, + os: parametersOverride.context.os, + app: parametersOverride.context.app, }), anonymousId: parametersOverride.anonymousId || 'default-anonymousId', originalTimestamp: parametersOverride.originalTimestamp || '2021-01-03T17:02:53.193Z', @@ -249,12 +289,14 @@ export const generatePageOrScreenPayload: any = (parametersOverride: any, eventT screen: { density: 2, }, + page: parametersOverride.context.page, traits: parametersOverride.context.traits, externalId: parametersOverride.externalId, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', }), event: parametersOverride.event, + name: parametersOverride.name, anonymousId: parametersOverride.anonymousId || 'default-anonymousId', properties: parametersOverride.properties, type: eventType || 'page', @@ -274,6 +316,7 @@ export const generateSimplifiedPageOrScreenPayload: any = ( userId: parametersOverride.userId || 'default-userId', type: eventType || 'page', event: parametersOverride.event, + name: parametersOverride.name, properties: parametersOverride.properties, integrations: parametersOverride.integrations, rudderId: parametersOverride.rudderId || generateAlphanumericId(36), @@ -312,8 +355,9 @@ export const generateGroupPayload: any = (parametersOverride: any) => { screen: { density: 2, }, + device: parametersOverride.context.device, traits: parametersOverride.context.traits, - externalId: parametersOverride.externalId, + externalId: parametersOverride.context.externalId, }), messageId: parametersOverride.messageId || generateAlphanumericId(36), session_id: parametersOverride.session_id || generateAlphanumericId(36), @@ -446,7 +490,7 @@ export const generateProxyV1Payload = ( workspaceId: 'default-workspaceId', sourceId: 'default-sourceId', secret: { - accessToken: 'default-accessToken', + accessToken: payloadParameters.accessToken || 'default-accessToken', }, dontBatch: false, }, diff --git a/test/scripts/testDataGenerator.ts b/test/scripts/testDataGenerator.ts new file mode 100644 index 0000000000..a00e9fce32 --- /dev/null +++ b/test/scripts/testDataGenerator.ts @@ -0,0 +1,88 @@ +import path from 'path'; +import { TestCaseData } from '../integrations/testTypes'; +import { getTestData, getTestDataFilePaths, produceTestData } from '../integrations/testUtils'; +import { Command } from 'commander'; +import axios from 'axios'; +import * as fs from 'fs'; + +// Produces test data for a given destination +// Example usage +// npx ts-node test/scripts/testDataGenerator.ts --destination=klaviyo --feature=processor + +const command = new Command(); +command + .allowUnknownOption() + .option('-d, --destination ', 'Enter Destination Name') + .option('-f, --feature ', 'Enter Feature Name(processor, router)') + .option('-i, --index ', 'Enter Test index') + .option('-id, --id ', 'Enter unique "Id" of the test case you want to run') + .option('-dp, --dataPlane ', 'Enter Data Plane URL') + .option('-wk, --writeKey ', 'Enter Write Key') + .option( + '-fk, --filterKeys ', + 'Enter Keys to filter from the test data(originalTimestamp, timestamp, messageId etc)', + ) + .parse(); + +const opts = command.opts(); + +if (opts.destination === undefined) { + throw new Error('Destination is not provided'); +} + +const filterKeys = opts.filterKeys ? opts.filterKeys.split(',') : []; + +const rootDir = __dirname; +const resolvedpath = path.resolve(rootDir, '../integrations'); +const destinationTestDataPaths = getTestDataFilePaths(resolvedpath, opts); + +destinationTestDataPaths.forEach((testDataPath) => { + let testData: TestCaseData[] = getTestData(testDataPath); + if (opts.index !== undefined) { + testData = [testData[parseInt(opts.index)]]; + } + if (opts.id) { + testData = testData.filter((data) => { + if (data['id'] === opts.id) { + return true; + } + return false; + }); + } + console.log('Writing test data to ../../temp/test_data.json'); + produceTestData(testData, filterKeys); + + if (opts.dataPlane && opts.writeKey) { + // read file ../../temp/test_data.json + console.log('Sending data to data plane URL: ', opts.dataPlane); + + const resolvedpathForData = path.resolve(rootDir, '../../temp/test_data.json'); + + fs.readFile(resolvedpathForData, 'utf8', function (err, data) { + if (err) { + console.log(err); + } else { + const parsedData = JSON.parse(data); + axios + .post( + `${opts.dataPlane}/v1/batch`, + { + batch: parsedData, + }, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${Buffer.from(opts.writeKey + ':').toString('base64')}`, + }, + }, + ) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); + } + }); + } +});