diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..4f4c5fff17 --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +registry=https://registry.npmjs.com/ +@rudderlabs:registry=https://rudderlabs-422074288268.d.codeartifact.us-east-1.amazonaws.com/npm/control-plane/ +//rudderlabs-422074288268.d.codeartifact.us-east-1.amazonaws.com/npm/control-plane/:_authToken=${REGISTRY_TOKEN} +//rudderlabs-422074288268.d.codeartifact.us-east-1.amazonaws.com/npm/control-plane/:always-auth=true diff --git a/.vscode/settings.json b/.vscode/settings.json index 8d723306a9..cbca78c2e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,11 +2,11 @@ "prettier.requireConfig": true, "prettier.configPath": ".prettierrc", "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "vscode.typescript-language-features", "editor.formatOnSave": true }, "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "vscode.typescript-language-features", "editor.formatOnSave": true }, "[jsonc]": { diff --git a/package-lock.json b/package-lock.json index 5f9e7229f6..8102eb1e12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", + "@rudderlabs/featureflag-sdk-node": "^0.1.3", "@rudderstack/integrations-lib": "^0.2.13", "@rudderstack/json-template-engine": "^0.18.0", "@rudderstack/workflow-engine": "^0.8.13", @@ -64,6 +65,7 @@ "parse-static-imports": "^1.1.0", "prom-client": "^14.2.0", "qs": "^6.11.1", + "querystring": "^0.2.1", "rs-jsonpath": "^1.1.2", "set-value": "^4.1.0", "sha256": "^0.2.0", @@ -124,6 +126,28 @@ "typescript": "^5.0.4" } }, + "../rudder-control-plane/libs/featureflag-sdk-node": { + "name": "feature-flag-sdk", + "version": "1.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "async-rwlock": "^1.1.1", + "axios": "^1.1.3", + "flagsmith-nodejs": "^4.0.0" + }, + "devDependencies": { + "@types/jest": "^29.2.0", + "@types/node": "^18.11.9", + "@typescript-eslint/eslint-plugin": "^5.42.0", + "@typescript-eslint/parser": "^5.42.0", + "eslint": "^8.26.0", + "jest": "^29.2.2", + "rimraf": "^3.0.2", + "ts-jest": "^29.0.3", + "typescript": "^4.8.4" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "license": "MIT", @@ -6601,6 +6625,17 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/@rudderlabs/featureflag-sdk-node": { + "version": "0.1.3", + "resolved": "https://rudderlabs-422074288268.d.codeartifact.us-east-1.amazonaws.com/npm/control-plane/@rudderlabs/featureflag-sdk-node/-/featureflag-sdk-node-0.1.3.tgz", + "integrity": "sha512-FViCBY9kKWNamS3yZqhQYJAIBRWs9Lcf5g+B32hoNu9Y98gCtqVYTxEzpQ4gwix2Ttxd66mZ5c068qsStkI9jg==", + "dependencies": { + "async-rwlock": "^1.1.1", + "axios": "^1.1.3", + "flagsmith-nodejs": "^4.0.0", + "require-json5": "^1.3.0" + } + }, "node_modules/@rudderstack/integrations-lib": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/@rudderstack/integrations-lib/-/integrations-lib-0.2.13.tgz", @@ -8109,6 +8144,18 @@ "version": "1.1.1", "license": "ISC" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "license": "MIT", @@ -8455,6 +8502,15 @@ "version": "3.2.5", "license": "MIT" }, + "node_modules/async-rwlock": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/async-rwlock/-/async-rwlock-1.1.1.tgz", + "integrity": "sha512-K4ecpHLAc0Jul4dMb1KLpukblQmHxD5/HNgkTuO3sQ9oLLtVENCJVk7+b0wMj1K89cqnjGkTsAvOb83lCfoKwA==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "license": "MIT" @@ -8467,6 +8523,15 @@ "node": ">= 4.0.0" } }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "license": "MIT", @@ -12105,6 +12170,15 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "dev": true, @@ -12245,6 +12319,15 @@ "node": ">=10.0" } }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "dev": true, @@ -12449,6 +12532,26 @@ "node": ">= 8" } }, + "node_modules/flagsmith-nodejs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/flagsmith-nodejs/-/flagsmith-nodejs-4.0.0.tgz", + "integrity": "sha512-ejid/hPo7CjMpkySGe74UemQNfaTVR1q95U3/jeZnlcaA6A4I8sT9e+DtMJMmpgOgWq89ic6rOf68WKIO1JOjg==", + "license": "MIT", + "dependencies": { + "pino": "^8.8.0", + "semver": "^7.3.7", + "undici-types": "^6.19.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/flagsmith-nodejs/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, "node_modules/flat": { "version": "5.0.2", "license": "BSD-3-Clause", @@ -16234,7 +16337,6 @@ }, "node_modules/json5": { "version": "2.2.3", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -18453,6 +18555,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "license": "MIT", @@ -18874,6 +18985,102 @@ "node": ">=0.10.0" } }, + "node_modules/pino": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.7.0", + "thread-stream": "^2.6.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "license": "MIT", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/pino-abstract-transport/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", + "license": "MIT" + }, "node_modules/pirates": { "version": "4.0.6", "dev": true, @@ -19390,11 +19597,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "dev": true, "license": "MIT" }, + "node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "license": "MIT" + }, "node_modules/progress": { "version": "2.0.3", "license": "MIT", @@ -19515,6 +19737,15 @@ "node": ">=0.10.0" } }, + "node_modules/querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "funding": [ @@ -19533,6 +19764,12 @@ ], "license": "MIT" }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, "node_modules/quick-lru": { "version": "4.0.1", "dev": true, @@ -19722,6 +19959,15 @@ "dev": true, "license": "MIT" }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/redent": { "version": "3.0.0", "dev": true, @@ -19822,6 +20068,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-json5": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/require-json5/-/require-json5-1.3.0.tgz", + "integrity": "sha512-FkOrdR0kqHFwIqrlifaXNg6fdg2YcUL5lX9bYlaENKLlWp+g0GO/tRMAvoWIM2pYzAGp57oF/jgkVLwxGk7KyQ==", + "license": "MIT", + "dependencies": { + "json5": "^2.x" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "dev": true, @@ -20339,6 +20594,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/sonic-boom": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/sort-keys": { "version": "2.0.0", "dev": true, @@ -21331,6 +21595,15 @@ "version": "0.2.0", "license": "MIT" }, + "node_modules/thread-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/through": { "version": "2.3.8", "license": "MIT" diff --git a/package.json b/package.json index b9064fac2c..c059a99bee 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@koa/router": "^12.0.0", "@ndhoule/extend": "^2.0.0", "@pyroscope/nodejs": "^0.2.9", + "@rudderlabs/featureflag-sdk-node": "^0.1.3", "@rudderstack/integrations-lib": "^0.2.13", "@rudderstack/json-template-engine": "^0.18.0", "@rudderstack/workflow-engine": "^0.8.13", @@ -109,6 +110,7 @@ "parse-static-imports": "^1.1.0", "prom-client": "^14.2.0", "qs": "^6.11.1", + "querystring": "^0.2.1", "rs-jsonpath": "^1.1.2", "set-value": "^4.1.0", "sha256": "^0.2.0", diff --git a/src/cdk/v2/destinations/http/utils.js b/src/cdk/v2/destinations/http/utils.js index 355eb03487..7b68645c76 100644 --- a/src/cdk/v2/destinations/http/utils.js +++ b/src/cdk/v2/destinations/http/utils.js @@ -1,6 +1,7 @@ const { toXML } = require('jstoxml'); const { groupBy } = require('lodash'); const { createHash } = require('crypto'); +const querystring = require('querystring'); const { ConfigurationError } = require('@rudderstack/integrations-lib'); const { BatchUtils } = require('@rudderstack/workflow-engine'); const { @@ -84,6 +85,18 @@ const getXMLPayload = (payload) => header: true, }); +/** + * Converts JSON payload to application/x-www-form-urlencoded format. + * @param {Object} payload - The JSON payload to be converted. + * @returns {string} - The payload in application/x-www-form-urlencoded format. + */ +const getFORMPayload = (payload) => { + if (!payload) { + throw new ConfigurationError('Invalid payload for FORM format'); + } + return querystring.stringify(payload); +} + const getMergedEvents = (batch) => { const events = []; batch.forEach((event) => { @@ -152,5 +165,6 @@ module.exports = { addPathParams, excludeMappedFields, getXMLPayload, + getFORMPayload, batchSuccessfulEvents, }; diff --git a/src/cdk/v2/destinations/webhook/procWorkflow.yaml b/src/cdk/v2/destinations/webhook/procWorkflow.yaml index bfe7bd4c13..edc3c2b2e6 100644 --- a/src/cdk/v2/destinations/webhook/procWorkflow.yaml +++ b/src/cdk/v2/destinations/webhook/procWorkflow.yaml @@ -3,6 +3,11 @@ bindings: path: ../../../../constants - path: ../../bindings/jsontemplate exportAll: true + - name: getPropertyParams + - path: ../../../../v0/destinations/webhook/utils + - name: getFormattedPayload + - path: ../../../../v0/destinations/webhook/utils + - name: isFeatureEnabled - path: ../../../../v0/destinations/webhook/utils - name: getHashFromArray path: ../../../../v0/util @@ -33,7 +38,6 @@ steps: ...configHeaders, ...messageHeader } - - name: deduceEndPoint template: | let integrationsObjects = $.getIntegrationsObj(.message, "webhook"); @@ -60,7 +64,34 @@ steps: payload.context.ip = ip; ) $.context.payload = $.removeUndefinedAndNullValues(payload) - - name: buildResponseForProcessTransformation + + - name: buildResponseForProcessTransformationWithBodyFormatting + condition: await $.isFeatureEnabled(.metadata.workspaceId, 'dest_transformer_webhook_form_support') + template: | + const finalPayload = { + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + userId: ^.message.anonymousId, + type: 'REST', + method: $.context.method, + endpoint: $.context.finalEndpoint, + headers: $.context.finalHeaders, + params: $.context.params, + files: {}, + }; + + const formattedPayload = $.getFormattedPayload($.context.finalHeaders, $.context.payload); + finalPayload.body[formattedPayload.contentTypeSimplified] = formattedPayload.payload || {}; + $.context.payload = finalPayload; + $.context.payload + + - name: buildResponseForProcessTransformationWithDefaultBodyFormat + condition: await $.isFeatureEnabled(.metadata.workspaceId, 'dest_transformer_webhook_form_support') !== true template: | $.context.payload.({ "body": { @@ -77,4 +108,4 @@ steps: "headers": $.context.finalHeaders, "params": $.context.params, "files": {} - }) + }) \ No newline at end of file diff --git a/src/cdk/v2/handler.ts b/src/cdk/v2/handler.ts index 74ebb716e6..512a9e2ade 100644 --- a/src/cdk/v2/handler.ts +++ b/src/cdk/v2/handler.ts @@ -19,6 +19,7 @@ import { import logger from '../../logger'; + const defTags = { [tags.TAG_NAMES.IMPLEMENTATION]: tags.IMPLEMENTATIONS.CDK_V2, }; diff --git a/src/v0/destinations/webhook/utils.js b/src/v0/destinations/webhook/utils.js index b7d166ad7f..7a6e3028b0 100644 --- a/src/v0/destinations/webhook/utils.js +++ b/src/v0/destinations/webhook/utils.js @@ -1,6 +1,37 @@ +const { isFeatureEnabled } = require('@rudderlabs/featureflag-sdk-node'); +const { getXMLPayload } = require('../../../cdk/v2/destinations/http/utils'); const { EventType } = require('../../../constants'); const { getFieldValueFromMessage, flattenJson } = require('../../util'); +const JSON = 'JSON'; +const XML = 'XML'; +const FORM = 'FORM'; + +const ContentTypeConstants = { + 'application/json': JSON, + 'application/xml': XML, + 'text/xml': XML, + 'application/x-www-form-urlencoded': FORM, +}; + +const getFormattedPayload = (headers, payload) => { + const normalizedHeaders = Object.keys(headers).reduce((acc, key) => { + acc[key.toLowerCase()] = headers[key]; + return acc; + }, {}); + const contentType = normalizedHeaders['content-type']; + const contentTypeSimplified = ContentTypeConstants[contentType] || JSON; + + switch (contentTypeSimplified) { + case XML: + return { payload: { payload: getXMLPayload(payload) }, contentTypeSimplified }; + case FORM: + return { payload: flattenJson(payload), contentTypeSimplified }; + default: + return { payload, contentTypeSimplified }; + } +}; + const getPropertyParams = (message) => { if (message.type === EventType.IDENTIFY) { return flattenJson(getFieldValueFromMessage(message, 'traits')); @@ -10,4 +41,6 @@ const getPropertyParams = (message) => { module.exports = { getPropertyParams, + getFormattedPayload, + isFeatureEnabled, }; diff --git a/test/integrations/destinations/webhook/mocks.ts b/test/integrations/destinations/webhook/mocks.ts new file mode 100644 index 0000000000..993549673d --- /dev/null +++ b/test/integrations/destinations/webhook/mocks.ts @@ -0,0 +1,5 @@ +const webhookUtils = require('../../../../src/v0/destinations/webhook/utils'); + +export const defaultMockFns = (_) => { + jest.spyOn(webhookUtils, 'isFeatureEnabled').mockResolvedValue(true); +}; diff --git a/test/integrations/destinations/webhook/processor/data.ts b/test/integrations/destinations/webhook/processor/data.ts index 7720bc683c..2c998e4016 100644 --- a/test/integrations/destinations/webhook/processor/data.ts +++ b/test/integrations/destinations/webhook/processor/data.ts @@ -1,6 +1,408 @@ -import { head } from 'lodash'; +import { defaultMockFns } from '../mocks'; export const data = [ + { + name: 'webhook', + description: + 'Feature Flag is enabled for formatted body based on content-type | Content type: json', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + context: { + device: { + id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + }, + network: { + carrier: 'Banglalink', + }, + os: { + name: 'android', + version: '8.1.0', + }, + traits: { + address: { + city: 'Dhaka', + country: 'Bangladesh', + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + additional_bet_index: 0, + battle_id: 'N/A', + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + user_properties: { + coin_balance: 9466052, + current_module_name: 'CasinoGameModule', + }, + }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { + from: '', + to: '', + }, + { + from: 'test2', + to: 'value2', + }, + { + from: 'content-type', + to: 'application/json', + }, + ], + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + context: { + device: { + id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + }, + network: { + carrier: 'Banglalink', + }, + os: { + name: 'android', + version: '8.1.0', + }, + traits: { + address: { + city: 'Dhaka', + country: 'Bangladesh', + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + additional_bet_index: 0, + battle_id: 'N/A', + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + user_properties: { + coin_balance: 9466052, + current_module_name: 'CasinoGameModule', + }, + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + version: '1', + userId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + type: 'REST', + method: 'POST', + endpoint: 'http://6b0e6a60.ngrok.io', + headers: { + 'content-type': 'application/json', + test2: 'value2', + }, + params: {}, + files: {}, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'webhook', + description: + 'Feature Flag is enabled for formatted body based on content-type | Content type: x-www-form-urlencoded', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + context: { + device: { + id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + }, + network: { + carrier: 'Banglalink', + }, + os: { + name: 'android', + version: '8.1.0', + }, + traits: { + address: { + city: 'Dhaka', + country: 'Bangladesh', + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + additional_bet_index: 0, + battle_id: 'N/A', + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + user_properties: { + coin_balance: 9466052, + current_module_name: 'CasinoGameModule', + }, + }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { + from: '', + to: '', + }, + { + from: 'test2', + to: 'value2', + }, + { + from: 'content-type', + to: 'application/x-www-form-urlencoded', + }, + ], + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: {}, + FORM: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + 'context.device.id': 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + 'context.network.carrier': 'Banglalink', + 'context.os.name': 'android', + 'context.os.version': '8.1.0', + 'context.traits.address.city': 'Dhaka', + 'context.traits.address.country': 'Bangladesh', + 'context.traits.anonymousId': 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + event: 'spin_result', + 'integrations.All': true, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + 'properties.additional_bet_index': 0, + 'properties.battle_id': 'N/A', + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + 'user_properties.coin_balance': 9466052, + 'user_properties.current_module_name': 'CasinoGameModule', + }, + }, + version: '1', + userId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + type: 'REST', + method: 'POST', + endpoint: 'http://6b0e6a60.ngrok.io', + headers: { + 'content-type': 'application/x-www-form-urlencoded', + test2: 'value2', + }, + params: {}, + files: {}, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'webhook', + description: + 'Feature Flag is enabled for formatted body based on content-type | Content type: xml', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + context: { + device: { + id: 'df16bffa-5c3d-4fbb-9bce-3bab098129a7R', + }, + network: { + carrier: 'Banglalink', + }, + os: { + name: 'android', + version: '8.1.0', + }, + traits: { + address: { + city: 'Dhaka', + country: 'Bangladesh', + }, + anonymousId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + }, + }, + event: 'spin_result', + integrations: { + All: true, + }, + message_id: 'a80f82be-9bdc-4a9f-b2a5-15621ee41df8', + properties: { + additional_bet_index: 0, + battle_id: 'N/A', + }, + timestamp: '2019-09-01T15:46:51.693229+05:30', + type: 'track', + user_properties: { + coin_balance: 9466052, + current_module_name: 'CasinoGameModule', + }, + }, + destination: { + Config: { + webhookUrl: 'http://6b0e6a60.ngrok.io', + headers: [ + { + from: '', + to: '', + }, + { + from: 'test2', + to: 'value2', + }, + { + from: 'content-type', + to: 'application/xml', + }, + ], + }, + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + body: { + JSON: {}, + JSON_ARRAY: {}, + XML: { + payload: + 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1df16bffa-5c3d-4fbb-9bce-3bab098129a7RBanglalinkandroid8.1.0
DhakaBangladesh
c82cbdff-e5be-4009-ac78-cdeea09ab4b1
spin_resulttruea80f82be-9bdc-4a9f-b2a5-15621ee41df80N/A2019-09-01T15:46:51.693229+05:30track9466052CasinoGameModule', + }, + FORM: {}, + }, + version: '1', + userId: 'c82cbdff-e5be-4009-ac78-cdeea09ab4b1', + type: 'REST', + method: 'POST', + endpoint: 'http://6b0e6a60.ngrok.io', + headers: { + 'content-type': 'application/xml', + test2: 'value2', + }, + params: {}, + files: {}, + }, + metadata: { + destinationId: 'destId', + workspaceId: 'wspId', + }, + statusCode: 200, + }, + ], + }, + }, + mockFns: defaultMockFns, + }, { name: 'webhook', description: 'Test 0', @@ -3313,6 +3715,7 @@ export const data = [ }, metadata: { destinationId: '1234', + workspaceId: 'wspId', }, }, ], @@ -3325,6 +3728,7 @@ export const data = [ { metadata: { destinationId: '1234', + workspaceId: 'wspId', }, output: { body: { diff --git a/test/integrations/destinations/webhook/router/data.ts b/test/integrations/destinations/webhook/router/data.ts index 6c738ee8a7..305e78fdbc 100644 --- a/test/integrations/destinations/webhook/router/data.ts +++ b/test/integrations/destinations/webhook/router/data.ts @@ -90,7 +90,7 @@ export const data = [ versionSessionCount: 2, }, }, - metadata: { jobId: 2, userId: 'u1' }, + metadata: { jobId: 2, userId: 'u1', workspaceId: 'wkspid' }, destination: { Config: { webhookUrl: 'http://6b0e6a60.ngrok.io', @@ -183,7 +183,7 @@ export const data = [ versionSessionCount: 2, }, }, - metadata: { jobId: 3, userId: 'u1' }, + metadata: { jobId: 3, userId: 'u1', workspaceId: 'wkspid' }, destination: { Config: { webhookUrl: 'https://6b0e6a60.ngrok.io/n' }, DestinationDefinition: { Config: { cdkV2Enabled: true } }, @@ -296,7 +296,7 @@ export const data = [ type: 'REST', method: 'POST', }, - metadata: [{ jobId: 2, userId: 'u1' }], + metadata: [{ jobId: 2, userId: 'u1', workspaceId: 'wkspid' }], batched: false, statusCode: 200, destination: { @@ -406,7 +406,7 @@ export const data = [ type: 'REST', method: 'POST', }, - metadata: [{ jobId: 3, userId: 'u1' }], + metadata: [{ jobId: 3, userId: 'u1', workspaceId: 'wkspid' }], batched: false, statusCode: 200, destination: { @@ -505,7 +505,7 @@ export const data = [ type: 'identify', userId: 'sample_user_id', }, - metadata: { jobId: 2, userId: 'u1' }, + metadata: { jobId: 2, userId: 'u1', workspaceId: 'wkspid' }, destination: { Config: { webhookUrl: 'http://6b0e6a60.ngrok.io', @@ -558,7 +558,7 @@ export const data = [ type: 'identify', userId: 'sample_user_id', }, - metadata: { jobId: 3, userId: 'u1' }, + metadata: { jobId: 3, userId: 'u1', workspaceId: 'wkspid' }, destination: { Config: { webhookUrl: 'https://6b0e6a60.ngrok.io/n' }, DestinationDefinition: { Config: { cdkV2Enabled: true } }, @@ -576,7 +576,7 @@ export const data = [ type: 'identify', userId: 'sample_user_id', }, - metadata: { jobId: 4, userId: 'u1' }, + metadata: { jobId: 4, userId: 'u1', workspaceId: 'wkspid' }, destination: { Config: { webhookUrl: 'https://6b0e6a60.ngrok.io/n' }, DestinationDefinition: { Config: { cdkV2Enabled: true } }, @@ -684,7 +684,7 @@ export const data = [ type: 'REST', method: 'POST', }, - metadata: [{ jobId: 2, userId: 'u1' }], + metadata: [{ jobId: 2, userId: 'u1', workspaceId: 'wkspid' }], batched: false, statusCode: 200, destination: { @@ -754,7 +754,7 @@ export const data = [ type: 'REST', method: 'POST', }, - metadata: [{ jobId: 3, userId: 'u1' }], + metadata: [{ jobId: 3, userId: 'u1', workspaceId: 'wkspid' }], batched: false, statusCode: 200, destination: { @@ -791,7 +791,7 @@ export const data = [ type: 'REST', method: 'POST', }, - metadata: [{ jobId: 4, userId: 'u1' }], + metadata: [{ jobId: 4, userId: 'u1', workspaceId: 'wkspid' }], batched: false, statusCode: 200, destination: {