Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: feature flag http dest example #3933

Closed
wants to merge 9 commits into from
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{

Check warning on line 1 in .vscode/settings.json

View workflow job for this annotation

GitHub Actions / Check for formatting & lint errors

File ignored by default.
"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]": {
Expand Down
36 changes: 36 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"component-each": "^0.2.6",
"crypto-js": "^4.2.0",
"dotenv": "^16.0.3",
"feature-flag-sdk": "file:../rudder-control-plane/libs/featureflag-sdk-node",
"flat": "^5.0.2",
"form-data": "^4.0.0",
"get-value": "^3.0.1",
Expand Down Expand Up @@ -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",
Expand Down
14 changes: 14 additions & 0 deletions src/cdk/v2/destinations/http/utils.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -152,5 +165,6 @@ module.exports = {
addPathParams,
excludeMappedFields,
getXMLPayload,
getFORMPayload,
batchSuccessfulEvents,
};
37 changes: 34 additions & 3 deletions src/cdk/v2/destinations/webhook/procWorkflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -33,7 +38,6 @@ steps:
...configHeaders,
...messageHeader
}

- name: deduceEndPoint
template: |
let integrationsObjects = $.getIntegrationsObj(.message, "webhook");
Expand All @@ -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": {
Expand All @@ -77,4 +108,4 @@ steps:
"headers": $.context.finalHeaders,
"params": $.context.params,
"files": {}
})
})
1 change: 1 addition & 0 deletions src/cdk/v2/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {

import logger from '../../logger';


const defTags = {
[tags.TAG_NAMES.IMPLEMENTATION]: tags.IMPLEMENTATIONS.CDK_V2,
};
Expand Down
33 changes: 33 additions & 0 deletions src/v0/destinations/webhook/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,37 @@
const { isFeatureEnabled } = require('feature-flag-sdk');

Check failure on line 1 in src/v0/destinations/webhook/utils.js

View workflow job for this annotation

GitHub Actions / Check for formatting & lint errors

Unable to resolve path to module 'feature-flag-sdk'

Check failure on line 1 in src/v0/destinations/webhook/utils.js

View workflow job for this annotation

GitHub Actions / Code Coverage

Unable to resolve path to module 'feature-flag-sdk'
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, contentTypeSimplified };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return { payload, contentTypeSimplified };
return { getFORMPayload(payload), contentTypeSimplified };

Copy link
Contributor Author

@vinayteki95 vinayteki95 Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have to convert this into FORMPayload ourselves as rudder-server takes care of it.
Follow this link to see what rudder-server is doing: https://github.com/rudderlabs/rudder-server/blob/f22e8f4bb61893af132f3f4386155c83c695fd51/router/network.go#L124

But I see the issue here, we need to flatten the json for rudder-server to properly convert it into form-values

I think a simple flattening will do the job here @krishna2020

default:
return { payload, contentTypeSimplified };
}
};

const getPropertyParams = (message) => {
if (message.type === EventType.IDENTIFY) {
return flattenJson(getFieldValueFromMessage(message, 'traits'));
Expand All @@ -10,4 +41,6 @@

module.exports = {
getPropertyParams,
getFormattedPayload,
isFeatureEnabled,
};
5 changes: 5 additions & 0 deletions test/integrations/destinations/webhook/mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const webhookUtils = require('../../../../src/v0/destinations/webhook/utils');

export const defaultMockFns = (_) => {
jest.spyOn(webhookUtils, 'isFeatureEnabled').mockResolvedValue(true);
};
Loading
Loading