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 2af853f643..a5ca48e3f8 100644 --- a/.github/workflows/prepare-for-prod-dt-deploy.yml +++ b/.github/workflows/prepare-for-prod-dt-deploy.yml @@ -144,3 +144,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/.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..3c5535cf60 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.19.0 +18.19.1 diff --git a/Dockerfile b/Dockerfile index 6bd03c9515..8cd4005a7b 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.19.1-alpine3.18 AS base ENV HUSKY 0 RUN apk update 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/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/constants/destinationCanonicalNames.js b/src/constants/destinationCanonicalNames.js index d1b2b24de0..17848e6b94 100644 --- a/src/constants/destinationCanonicalNames.js +++ b/src/constants/destinationCanonicalNames.js @@ -152,6 +152,7 @@ const DestCanonicalNames = { 'the trade desk', ], INTERCOM: ['INTERCOM', 'intercom', 'Intercom'], + koala: ['Koala', 'koala', 'KOALA'], }; module.exports = { DestHandlerMap, DestCanonicalNames }; diff --git a/src/controllers/bulkUpload.ts b/src/controllers/bulkUpload.ts index babb8b6db1..dbd77dc07f 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 { + getJobStatusHandler, + getPollStatusHandler, + getDestFileUploadHandler, +} from '../util/fetchDestinationHandlers'; import { CatchErr, ContextBodySimple } from '../util/types'; // 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) => diff --git a/src/features.json b/src/features.json index 5460111a22..dc52044048 100644 --- a/src/features.json +++ b/src/features.json @@ -66,7 +66,8 @@ "REDDIT": true, "THE_TRADE_DESK": true, "INTERCOM": true, - "NINETAILED": true + "NINETAILED": true, + "KOALA": true }, "regulations": [ "BRAZE", 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/prometheus.js b/src/util/prometheus.js index 0fa17dc9bd..89e5424c0c 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -710,7 +710,7 @@ class Prometheus { name: 'get_libraries_code_time', help: 'get_libraries_code_time', type: 'histogram', - labelNames: ['libraryVersionId', 'versionId', 'type'], + labelNames: ['libraryVersionId', 'versionId', 'type', 'version'], }, { name: 'isolate_cpu_time', diff --git a/src/util/redis/redisConnector.test.js b/src/util/redis/redisConnector.test.js index 840f222e37..e0491132ff 100644 --- a/src/util/redis/redisConnector.test.js +++ b/src/util/redis/redisConnector.test.js @@ -70,7 +70,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..04b80b8fc9 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", @@ -115,7 +116,8 @@ "input": { "cart_token": "shopifyGetAnonymousId", "query_parameters": { - "topic": ["checkouts_delete"] + "topic": ["checkouts_delete"], + "writeKey": ["wr"] }, "line_items": [], "note": null, @@ -154,7 +156,8 @@ "input": { "id": "shopify_test3", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test3", "line_items": [], @@ -252,7 +255,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 +1260,8 @@ "input": { "id": "shopify_test4", "query_parameters": { - "topic": ["carts_update"] + "topic": ["carts_update"], + "writeKey": ["wr"] }, "token": "shopify_test4", "line_items": [], 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/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..4f09984054 100644 --- a/src/v0/sources/shopify/transform.js +++ b/src/v0/sources/shopify/transform.js @@ -143,7 +143,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; @@ -155,7 +155,8 @@ const processEvent = async (inputEvent, metricMetadata) => { if (!SUPPORTED_TRACK_EVENTS.includes(shopifyTopic)) { stats.increment('invalid_shopify_event', { event: shopifyTopic, - ...metricMetadata, + source: metricMetadata.source, + shopifyTopic: metricMetadata.shopifyTopic, }); return NO_OPERATION_SUCCESS; } @@ -215,7 +216,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,14 +238,16 @@ 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}`); stats.increment('shopify_redis_failures', { type: 'set', - ...metricMetadata, + source: metricMetadata.source, + writeKey: metricMetadata.writeKey, }); } } diff --git a/src/v0/sources/shopify/util.js b/src/v0/sources/shopify/util.js index 6f31ade4a7..c4bbb61b9c 100644 --- a/src/v0/sources/shopify/util.js +++ b/src/v0/sources/shopify/util.js @@ -29,7 +29,8 @@ const getDataFromRedis = async (key, metricMetadata) => { 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/v1/destinations/algolia/networkHandler.js b/src/v1/destinations/algolia/networkHandler.js new file mode 100644 index 0000000000..d953251050 --- /dev/null +++ b/src/v1/destinations/algolia/networkHandler.js @@ -0,0 +1,84 @@ +/* eslint-disable no-restricted-syntax */ +const { TransformerProxyError } = require('../../../v0/util/errorTypes'); +const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); +const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = 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 = []; + // response: + // {status: 200, message: 'OK'} + // {response:'[ENOTFOUND] :: DNS lookup failed', status: 400} + // destinationResponse = { + // response: {"status": 422, "message": "EventType must be one of \"click\", \"conversion\" or \"view\""}, status: 422 + // } + 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'; + let serverStatus = 400; + for (const metadata of rudderJobMetadata) { + // handling case if dontBatch is true, and again we got invalid from destination + if (metadata.dontBatch && status === 422) { + responseWithIndividualEvents.push({ + statusCode: 400, + metadata, + error: errorMessage, + }); + } else { + serverStatus = 500; + metadata.dontBatch = true; + responseWithIndividualEvents.push({ + statusCode: 500, + metadata, + error: errorMessage, + }); + } + } + + // sending back 500 for retry + throw new TransformerProxyError( + `ALGOLIA: Error transformer proxy v1 during ALGOLIA response transformation`, + serverStatus, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), + }, + destinationResponse, + getAuthErrCategoryFromStCode(status), + responseWithIndividualEvents, + ); +}; + +function networkHandler() { + this.prepareProxy = prepareProxyRequest; + this.proxy = proxyRequest; + this.processAxiosResponse = processAxiosResponse; + 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..300b5f9676 100644 --- a/src/v1/destinations/campaign_manager/networkHandler.js +++ b/src/v1/destinations/campaign_manager/networkHandler.js @@ -69,7 +69,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 +77,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/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/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/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/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/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/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/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/salesforce/network.ts b/test/integrations/destinations/salesforce/network.ts index 93013cd8db..b422271d36 100644 --- a/test/integrations/destinations/salesforce/network.ts +++ b/test/integrations/destinations/salesforce/network.ts @@ -466,5 +466,5 @@ export const networkCallsData = [ ...tfProxyMocksData, ...transformationMocksData, ...businessMockData, - ...otherMocksData + ...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/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/testTypes.ts b/test/integrations/testTypes.ts index a46277d552..1c5a989f44 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; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index 2abe4c6d9a..7aede97cf7 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', 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); + }); + } + }); + } +});