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.0DhakaBangladeshc82cbdff-e5be-4009-ac78-cdeea09ab4b1spin_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: {