diff --git a/.github/workflows/create-hotfix-branch.yml b/.github/workflows/create-hotfix-branch.yml
index d1397cb608..97611f1eee 100644
--- a/.github/workflows/create-hotfix-branch.yml
+++ b/.github/workflows/create-hotfix-branch.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
# Only allow these users to create new hotfix branch from 'main'
- if: github.ref == 'refs/heads/main' && (github.actor == 'ItsSudip' || github.actor == 'krishna2020' || github.actor == 'saikumarrs' || github.actor == 'sandeepdsvs' || github.actor == 'shrouti1507' || github.actor == 'anantjain45823' || github.actor == 'chandumlg' || github.actor == 'mihir-4116') && (github.triggering_actor == 'ItsSudip' || github.triggering_actor == 'krishna2020' || github.triggering_actor == 'saikumarrs' || github.triggering_actor == 'sandeepdsvs' || github.triggering_actor == 'shrouti1507' || github.triggering_actor == 'anantjain45823' || github.triggering_actor == 'chandumlg' || github.triggering_actor == 'mihir-4116')
+ if: github.ref == 'refs/heads/main' && (github.actor == 'ItsSudip' || github.actor == 'krishna2020' || github.actor == 'saikumarrs' || github.actor == 'sandeepdsvs' || github.actor == 'shrouti1507' || github.actor == 'anantjain45823' || github.actor == 'chandumlg' || github.actor == 'mihir-4116' || github.actor == 'ujjwal-ab') && (github.triggering_actor == 'ItsSudip' || github.triggering_actor == 'krishna2020' || github.triggering_actor == 'saikumarrs' || github.triggering_actor == 'sandeepdsvs' || github.triggering_actor == 'shrouti1507' || github.triggering_actor == 'anantjain45823' || github.triggering_actor == 'chandumlg' || github.triggering_actor == 'mihir-4116' || github.triggering_actor == 'ujjwal-ab)
steps:
- name: Create Branch
uses: peterjgrainger/action-create-branch@v2.4.0
diff --git a/.github/workflows/draft-new-release.yml b/.github/workflows/draft-new-release.yml
index a0a558440a..23e243918f 100644
--- a/.github/workflows/draft-new-release.yml
+++ b/.github/workflows/draft-new-release.yml
@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
# Only allow release stakeholders to initiate releases
- if: (github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/hotfix/')) && (github.actor == 'ItsSudip' || github.actor == 'krishna2020' || github.actor == 'saikumarrs' || github.actor == 'sandeepdsvs' || github.actor == 'shrouti1507' || github.actor == 'anantjain45823' || github.actor == 'chandumlg' || github.actor == 'mihir-4116' || github.actor == 'yashasvibajpai' || github.actor == 'sanpj2292') && (github.triggering_actor == 'ItsSudip' || github.triggering_actor == 'krishna2020' || github.triggering_actor == 'saikumarrs' || github.triggering_actor == 'sandeepdsvs' || github.triggering_actor == 'shrouti1507' || github.triggering_actor == 'anantjain45823' || github.triggering_actor == 'chandumlg' || github.triggering_actor == 'mihir-4116' || github.triggering_actor == 'yashasvibajpai' || github.triggering_actor == 'sanpj2292')
+ if: (github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/hotfix/')) && (github.actor == 'ItsSudip' || github.actor == 'krishna2020' || github.actor == 'saikumarrs' || github.actor == 'sandeepdsvs' || github.actor == 'shrouti1507' || github.actor == 'anantjain45823' || github.actor == 'chandumlg' || github.actor == 'mihir-4116' || github.actor == 'yashasvibajpai' || github.actor == 'sanpj2292' || github.actor == 'ujjwal-ab') && (github.triggering_actor == 'ItsSudip' || github.triggering_actor == 'krishna2020' || github.triggering_actor == 'saikumarrs' || github.triggering_actor == 'sandeepdsvs' || github.triggering_actor == 'shrouti1507' || github.triggering_actor == 'anantjain45823' || github.triggering_actor == 'chandumlg' || github.triggering_actor == 'mihir-4116' || github.triggering_actor == 'yashasvibajpai' || github.triggering_actor == 'sanpj2292' || github.triggering_actor == 'ujjwal-ab')
steps:
- name: Checkout
uses: actions/checkout@v3.5.3
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7d295645f..01631435d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [1.48.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.47.0...v1.48.0) (2023-11-02)
+
+
+### Features
+
+* add support to add custom network policies for specific workspaces in faas pods ([bc1a760](https://github.com/rudderlabs/rudder-transformer/commit/bc1a76066c0aeb43776ded0b266ec48f5e69aa16))
+
## [1.47.0](https://github.com/rudderlabs/rudder-transformer/compare/v1.46.5...v1.47.0) (2023-10-30)
diff --git a/package-lock.json b/package-lock.json
index 4643d1325d..0822a9b42b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "rudder-transformer",
- "version": "1.47.0",
+ "version": "1.48.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "rudder-transformer",
- "version": "1.47.0",
+ "version": "1.48.0",
"license": "ISC",
"dependencies": {
"@amplitude/ua-parser-js": "^0.7.24",
diff --git a/package.json b/package.json
index adc5f0e8f5..46f728664d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rudder-transformer",
- "version": "1.47.0",
+ "version": "1.48.0",
"description": "",
"homepage": "https://github.com/rudderlabs/rudder-transformer#readme",
"bugs": {
diff --git a/src/features.json b/src/features.json
index 8ea03e0417..7c5c3db034 100644
--- a/src/features.json
+++ b/src/features.json
@@ -59,7 +59,8 @@
"OPTIMIZELY_FULLSTACK": true,
"TWITTER_ADS": true,
"CLEVERTAP": true,
- "TIKTOK_AUDIENCE": true,
- "ORTTO": true
+ "ORTTO": true,
+ "ONE_SIGNAL": true,
+ "TIKTOK_AUDIENCE": true
}
}
diff --git a/src/util/customTransformer-faas.js b/src/util/customTransformer-faas.js
index d1fa48d2d1..54d2410313 100644
--- a/src/util/customTransformer-faas.js
+++ b/src/util/customTransformer-faas.js
@@ -1,7 +1,7 @@
const { v4: uuidv4 } = require('uuid');
const crypto = require('crypto');
const NodeCache = require('node-cache');
-const { getMetadata } = require('../v0/util');
+const { getMetadata, getTransformationMetadata } = require('../v0/util');
const stats = require('./stats');
const {
setupFaasFunction,
@@ -82,10 +82,10 @@ async function setOpenFaasUserTransform(
libraryVersionIds,
pregeneratedFnName,
testMode = false,
+ trMetadata = {},
) {
const tags = {
transformerVersionId: userTransformation.versionId,
- language: userTransformation.language,
identifier: 'openfaas',
testMode,
};
@@ -106,6 +106,7 @@ async function setOpenFaasUserTransform(
testMode,
),
testMode,
+ trMetadata,
);
stats.timing('creation_time', setupTime, tags);
@@ -129,16 +130,22 @@ async function runOpenFaasUserTransform(
const metaTags = events[0].metadata ? getMetadata(events[0].metadata) : {};
const tags = {
transformerVersionId: userTransformation.versionId,
- language: userTransformation.language,
identifier: 'openfaas',
testMode,
...metaTags,
};
+ const trMetadata = events[0].metadata ? getTransformationMetadata(events[0].metadata) : {};
// check and deploy faas function if not exists
const functionName = generateFunctionName(userTransformation, libraryVersionIds, testMode);
if (testMode) {
- await setOpenFaasUserTransform(userTransformation, libraryVersionIds, functionName, testMode);
+ await setOpenFaasUserTransform(
+ userTransformation,
+ libraryVersionIds,
+ functionName,
+ testMode,
+ trMetadata,
+ );
}
const invokeTime = new Date();
@@ -156,6 +163,7 @@ async function runOpenFaasUserTransform(
testMode,
),
testMode,
+ trMetadata,
);
stats.timing('run_time', invokeTime, tags);
return result;
diff --git a/src/util/openfaas/index.js b/src/util/openfaas/index.js
index 60ad316e1b..f80aa01c23 100644
--- a/src/util/openfaas/index.js
+++ b/src/util/openfaas/index.js
@@ -23,6 +23,8 @@ const CONFIG_BACKEND_URL = process.env.CONFIG_BACKEND_URL || 'https://api.rudder
const GEOLOCATION_URL = process.env.GEOLOCATION_URL || '';
const FAAS_AST_VID = 'ast';
const FAAS_AST_FN_NAME = 'fn-ast';
+const CUSTOM_NETWORK_POLICY_WORKSPACE_IDS = process.env.CUSTOM_NETWORK_POLICY_WORKSPACE_IDS || '';
+const customNetworkPolicyWorkspaceIds = CUSTOM_NETWORK_POLICY_WORKSPACE_IDS.split(',');
// Initialise node cache
const functionListCache = new NodeCache();
@@ -111,7 +113,14 @@ const invalidateFnCache = () => {
functionListCache.set(FUNC_LIST_KEY, []);
};
-const deployFaasFunction = async (functionName, code, versionId, libraryVersionIDs, testMode) => {
+const deployFaasFunction = async (
+ functionName,
+ code,
+ versionId,
+ libraryVersionIDs,
+ testMode,
+ trMetadata = {},
+) => {
try {
logger.debug('[Faas] Deploying a faas function');
let envProcess = 'python index.py';
@@ -132,6 +141,22 @@ const deployFaasFunction = async (functionName, code, versionId, libraryVersionI
if (GEOLOCATION_URL) {
envVars.geolocation_url = GEOLOCATION_URL;
}
+ // labels
+ const labels = {
+ 'openfaas-fn': 'true',
+ 'parent-component': 'openfaas',
+ 'com.openfaas.scale.max': FAAS_MAX_PODS_IN_TEXT,
+ 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT,
+ transformationId: trMetadata.transformationId,
+ workspaceId: trMetadata.workspaceId,
+ };
+ if (
+ trMetadata.workspaceId &&
+ customNetworkPolicyWorkspaceIds.includes(trMetadata.workspaceId)
+ ) {
+ labels['custom-network-policy'] = 'true';
+ }
+
// TODO: investigate and add more required labels and annotations
const payload = {
service: functionName,
@@ -139,12 +164,7 @@ const deployFaasFunction = async (functionName, code, versionId, libraryVersionI
image: FAAS_BASE_IMG,
envProcess,
envVars,
- labels: {
- 'openfaas-fn': 'true',
- 'parent-component': 'openfaas',
- 'com.openfaas.scale.max': FAAS_MAX_PODS_IN_TEXT,
- 'com.openfaas.scale.min': FAAS_MIN_PODS_IN_TEXT,
- },
+ labels,
annotations: {
'prometheus.io.scrape': 'true',
},
@@ -175,14 +195,28 @@ const deployFaasFunction = async (functionName, code, versionId, libraryVersionI
}
};
-async function setupFaasFunction(functionName, code, versionId, libraryVersionIDs, testMode) {
+async function setupFaasFunction(
+ functionName,
+ code,
+ versionId,
+ libraryVersionIDs,
+ testMode,
+ trMetadata = {},
+) {
try {
if (!testMode && isFunctionDeployed(functionName)) {
logger.debug(`[Faas] Function ${functionName} already deployed`);
return;
}
// deploy faas function
- await deployFaasFunction(functionName, code, versionId, libraryVersionIDs, testMode);
+ await deployFaasFunction(
+ functionName,
+ code,
+ versionId,
+ libraryVersionIDs,
+ testMode,
+ trMetadata,
+ );
// This api call is only used to check if function is spinned correctly
await awaitFunctionReadiness(functionName);
@@ -201,6 +235,7 @@ const executeFaasFunction = async (
versionId,
libraryVersionIDs,
testMode,
+ trMetadata = {},
) => {
try {
logger.debug('[Faas] Invoking faas function');
@@ -217,7 +252,14 @@ const executeFaasFunction = async (
error.message.includes(`error finding function ${functionName}`)
) {
removeFunctionFromCache(functionName);
- await setupFaasFunction(functionName, null, versionId, libraryVersionIDs, testMode);
+ await setupFaasFunction(
+ functionName,
+ null,
+ versionId,
+ libraryVersionIDs,
+ testMode,
+ trMetadata,
+ );
throw new RetryRequestError(`${functionName} not found`);
}
diff --git a/src/v0/destinations/adobe_analytics/transform.js b/src/v0/destinations/adobe_analytics/transform.js
index 54806bf578..67bb66310a 100644
--- a/src/v0/destinations/adobe_analytics/transform.js
+++ b/src/v0/destinations/adobe_analytics/transform.js
@@ -11,6 +11,7 @@ const {
isDefinedAndNotNull,
isDefinedAndNotNullAndNotEmpty,
getIntegrationsObj,
+ removeUndefinedAndNullValues,
simpleProcessRouterDest,
} = require('../../util');
const {
@@ -394,7 +395,7 @@ const handleTrack = (message, destinationConfig) => {
break;
}
- return payload;
+ return removeUndefinedAndNullValues(payload);
};
const process = async (event) => {
diff --git a/src/v0/destinations/customerio/transform.js b/src/v0/destinations/customerio/transform.js
index 5f953ee2f0..984fb7e67f 100644
--- a/src/v0/destinations/customerio/transform.js
+++ b/src/v0/destinations/customerio/transform.js
@@ -12,6 +12,7 @@ const {
adduserIdFromExternalId,
getFieldValueFromMessage,
handleRtTfSingleEventError,
+ validateEventName,
} = require('../../util');
const logger = require('../../../logger');
@@ -101,6 +102,7 @@ function processSingleMessage(message, destination) {
break;
case EventType.TRACK:
evType = 'event';
+ validateEventName(message.event);
evName = message.event;
break;
case EventType.ALIAS:
@@ -113,6 +115,7 @@ function processSingleMessage(message, destination) {
logger.error(`could not determine type ${messageType}`);
throw new InstrumentationError(`could not determine type ${messageType}`);
}
+ evName = evName ? String(evName) : evName;
const response = responseBuilder(message, evType, evName, destination, messageType);
// replace default domain with EU data center domainc for EU based account
diff --git a/src/v0/destinations/customerio/util.js b/src/v0/destinations/customerio/util.js
index 2e7f000fba..6b4dbc0e11 100644
--- a/src/v0/destinations/customerio/util.js
+++ b/src/v0/destinations/customerio/util.js
@@ -10,7 +10,6 @@ const {
defaultDeleteRequestConfig,
isAppleFamily,
validateEmail,
- validateEventType,
} = require('../../util');
const { EventType, SpecedTraits, TraitsMapping } = require('../../../constants');
@@ -288,7 +287,6 @@ const defaultResponseBuilder = (message, evName, userId, evType, destination, me
// 100 - len(`Viewed Screen`) = 86
trimmedEvName = `Viewed ${truncate(message.event || message.properties.name, 86)} Screen`;
} else {
- validateEventType(evName);
trimmedEvName = truncate(evName, 100);
}
// anonymous_id needs to be sent for anon track calls to provide information on which anon user is being tracked
diff --git a/src/v0/destinations/freshmarketer/config.js b/src/v0/destinations/freshmarketer/config.js
index f1018d439c..a0d6449c3a 100644
--- a/src/v0/destinations/freshmarketer/config.js
+++ b/src/v0/destinations/freshmarketer/config.js
@@ -4,23 +4,23 @@ const CONFIG_CATEGORIES = {
IDENTIFY: {
name: 'FRESHMARKETERIdentifyConfig',
type: 'identify',
- baseUrl: '.myfreshworks.com/crm/sales/api/contacts/upsert',
+ baseUrl: '/crm/sales/api/contacts/upsert',
},
GROUP: {
name: 'FRESHMARKETERGroupConfig',
type: 'group',
- baseUrlAccount: '.myfreshworks.com/crm/sales/api/sales_accounts/upsert',
- baseUrlList: '.myfreshworks.com/crm/sales/api/lists',
+ baseUrlAccount: '/crm/sales/api/sales_accounts/upsert',
+ baseUrlList: '/crm/sales/api/lists',
},
SALES_ACTIVITY: {
name: 'SalesActivityConfig',
- baseUrlCreate: '.myfreshworks.com/crm/sales/api/sales_activities',
- baseUrlListAll: '.myfreshworks.com/crm/sales/api/selector/sales_activity_types',
+ baseUrlCreate: '/crm/sales/api/sales_activities',
+ baseUrlListAll: '/crm/sales/api/selector/sales_activity_types',
},
};
-const DELETE_ENDPOINT = '.myfreshworks.com/crm/sales/api/contacts/';
-const LIFECYCLE_STAGE_ENDPOINT = '.myfreshworks.com/crm/sales/api/selector/lifecycle_stages';
+const DELETE_ENDPOINT = '/crm/sales/api/contacts/';
+const LIFECYCLE_STAGE_ENDPOINT = '/crm/sales/api/selector/lifecycle_stages';
const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname);
module.exports = {
diff --git a/src/v0/destinations/freshmarketer/utils.js b/src/v0/destinations/freshmarketer/utils.js
index 5b47bb9170..f7dcc46b06 100644
--- a/src/v0/destinations/freshmarketer/utils.js
+++ b/src/v0/destinations/freshmarketer/utils.js
@@ -203,7 +203,7 @@ const updateAccountWOContact = (payload, Config) => {
*/
const updateContactWithList = (userId, listId, Config) => {
const response = defaultRequestConfig();
- response.endpoint = `https://${Config.domain}.myfreshworks.com/crm/sales/api/lists/${listId}/add_contacts`;
+ response.endpoint = `https://${Config.domain}/crm/sales/api/lists/${listId}/add_contacts`;
response.headers = getHeaders(Config.apiKey);
response.body.JSON = {
ids: [userId],
diff --git a/src/v0/destinations/freshsales/config.js b/src/v0/destinations/freshsales/config.js
index c6ae4b8165..62f54c1297 100644
--- a/src/v0/destinations/freshsales/config.js
+++ b/src/v0/destinations/freshsales/config.js
@@ -4,23 +4,23 @@ const CONFIG_CATEGORIES = {
IDENTIFY: {
name: 'identifyConfig',
type: 'identify',
- baseUrl: '.myfreshworks.com/crm/sales/api/contacts/upsert',
+ baseUrl: '/crm/sales/api/contacts/upsert',
method: 'POST',
},
GROUP: {
name: 'groupConfig',
type: 'group',
- baseUrlAccount: '.myfreshworks.com/crm/sales/api/sales_accounts/upsert',
+ baseUrlAccount: '/crm/sales/api/sales_accounts/upsert',
method: 'POST',
},
SALES_ACTIVITY: {
name: 'SalesActivityConfig',
- baseUrlCreate: '.myfreshworks.com/crm/sales/api/sales_activities',
- baseUrlListAll: '.myfreshworks.com/crm/sales/api/selector/sales_activity_types',
+ baseUrlCreate: '/crm/sales/api/sales_activities',
+ baseUrlListAll: '/crm/sales/api/selector/sales_activity_types',
},
};
-const LIFECYCLE_STAGE_ENDPOINT = '.myfreshworks.com/crm/sales/api/selector/lifecycle_stages';
+const LIFECYCLE_STAGE_ENDPOINT = '/crm/sales/api/selector/lifecycle_stages';
const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname);
module.exports = {
diff --git a/src/v0/destinations/freshsales/transform.js b/src/v0/destinations/freshsales/transform.js
index cd7518a101..c1e18482ed 100644
--- a/src/v0/destinations/freshsales/transform.js
+++ b/src/v0/destinations/freshsales/transform.js
@@ -8,7 +8,7 @@ const {
defaultPostRequestConfig,
getValidDynamicFormConfig,
simpleProcessRouterDest,
- validateEventType,
+ validateEventName,
} = require('../../util');
const { InstrumentationError, TransformationError } = require('../../util/errorTypes');
const { CONFIG_CATEGORIES, MAPPING_CONFIG } = require('./config');
@@ -67,7 +67,6 @@ const identifyResponseBuilder = (message, { Config }) => {
* @returns
*/
const trackResponseBuilder = async (message, { Config }, event) => {
- validateEventType(event);
let payload;
const response = defaultRequestConfig();
@@ -125,9 +124,6 @@ const groupResponseBuilder = async (message, { Config }) => {
// Checks if there are any mapping events for the track event and returns them
function eventMappingHandler(message, destination) {
const event = get(message, 'event');
- if (!event) {
- throw new InstrumentationError('Event name is required');
- }
let { rudderEventsToFreshsalesEvents } = destination.Config;
const mappedEvents = new Set();
@@ -161,6 +157,7 @@ const processEvent = async (message, destination) => {
response = identifyResponseBuilder(message, destination);
break;
case EventType.TRACK: {
+ validateEventName(message.event);
const mappedEvents = eventMappingHandler(message, destination);
if (mappedEvents.length > 0) {
const respList = await Promise.all(
diff --git a/src/v0/destinations/klaviyo/util.js b/src/v0/destinations/klaviyo/util.js
index 4304edd78f..b31dafd78b 100644
--- a/src/v0/destinations/klaviyo/util.js
+++ b/src/v0/destinations/klaviyo/util.js
@@ -17,7 +17,6 @@ const { handleHttpRequest } = require('../../../adapters/network');
const { JSON_MIME_TYPE, HTTP_STATUS_CODES } = require('../../util/constant');
const { NetworkError, InstrumentationError } = require('../../util/errorTypes');
const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils');
-const { client: errNotificationClient } = require('../../../util/errorNotifier');
const { BASE_ENDPOINT, MAPPING_CONFIG, CONFIG_CATEGORIES, MAX_BATCH_SIZE } = require('./config');
const REVISION_CONSTANT = '2023-02-22';
@@ -69,11 +68,6 @@ const getIdFromNewOrExistingProfile = async (endpoint, payload, requestOptions)
let statusCode = resp.status;
if (resp.status === 201 || resp.status === 409) {
// retryable error if the profile id is not found in the response
- errNotificationClient.notify(
- new Error('Klaviyo: ProfileId not found'),
- 'Profile Id not Found in the response',
- JSON.stringify(resp.response),
- );
statusCode = 500;
}
diff --git a/src/v0/destinations/monday/transform.js b/src/v0/destinations/monday/transform.js
index 34ada34780..37ee835e50 100644
--- a/src/v0/destinations/monday/transform.js
+++ b/src/v0/destinations/monday/transform.js
@@ -8,7 +8,7 @@ const {
removeUndefinedAndNullValues,
simpleProcessRouterDest,
getDestinationExternalID,
- validateEventType,
+ validateEventName,
} = require('../../util');
const {
ConfigurationError,
@@ -42,7 +42,7 @@ const trackResponseBuilder = async (message, { Config }) => {
const { apiToken } = Config;
let boardId = getDestinationExternalID(message, 'boardId');
const event = get(message, 'event');
- validateEventType(event);
+ validateEventName(event);
if (!boardId) {
boardId = Config.boardId;
}
diff --git a/src/v0/sources/revenuecat/mapping.json b/src/v0/sources/revenuecat/mapping.json
new file mode 100644
index 0000000000..541568b71b
--- /dev/null
+++ b/src/v0/sources/revenuecat/mapping.json
@@ -0,0 +1,10 @@
+[
+ {
+ "sourceKeys": "event.type",
+ "destKeys": "event"
+ },
+ {
+ "sourceKeys": "event.id",
+ "destKeys": "messageId"
+ }
+]
diff --git a/src/v0/sources/revenuecat/transform.js b/src/v0/sources/revenuecat/transform.js
new file mode 100644
index 0000000000..36944e10fa
--- /dev/null
+++ b/src/v0/sources/revenuecat/transform.js
@@ -0,0 +1,47 @@
+const { camelCase } = require('lodash');
+const moment = require('moment');
+const { removeUndefinedAndNullValues, isDefinedAndNotNull } = require('../../util');
+const Message = require('../message');
+
+function process(event) {
+ const message = new Message(`RevenueCat`);
+
+ // we are setting event type as track always
+ message.setEventType('track');
+
+ const properties = {};
+ // dump all event properties to message.properties after converting them to camelCase
+ if (event.event) {
+ Object.keys(event.event).forEach((key) => {
+ properties[camelCase(key)] = event.event[key];
+ });
+ message.setProperty('properties', properties);
+ }
+
+ // setting up app_user_id to externalId : revenuecatAppUserId
+ if (event?.event?.app_user_id) {
+ message.context.externalId = [
+ {
+ type: 'revenuecatAppUserId',
+ id: event?.event?.app_user_id,
+ },
+ ];
+ }
+
+ if (
+ isDefinedAndNotNull(event?.event?.event_timestamp_ms) &&
+ moment(event?.event?.event_timestamp_ms).isValid()
+ ) {
+ const validTimestamp = new Date(event.event.event_timestamp_ms).toISOString();
+ message.setProperty('originalTimestamp', validTimestamp);
+ message.setProperty('sentAt', validTimestamp);
+ }
+ message.event = event?.event?.type;
+ message.messageId = event?.event?.id;
+
+ // removing undefined and null values from message
+ removeUndefinedAndNullValues(message);
+ return message;
+}
+
+module.exports = { process };
diff --git a/src/v0/util/index.js b/src/v0/util/index.js
index ea08d08c8a..d6f6621220 100644
--- a/src/v0/util/index.js
+++ b/src/v0/util/index.js
@@ -1403,6 +1403,12 @@ const getMetadata = (metadata) => ({
destinationType: metadata.destinationType,
k8_namespace: metadata.namespace,
});
+
+const getTransformationMetadata = (metadata) => ({
+ transformationId: metadata.transformationId,
+ workspaceId: metadata.workspaceId,
+});
+
// checks if array 2 is a subset of array 1
function checkSubsetOfArray(array1, array2) {
const result = array2.every((val) => array1.includes(val));
@@ -2063,7 +2069,7 @@ const isValidInteger = (value) => {
// Use a regular expression to check if the string is a valid integer or a valid floating-point number
return typeof value === 'string' ? /^-?\d+$/.test(value) : false;
};
-const validateEventType = (event) => {
+const validateEventName = (event) => {
if (!event || typeof event !== 'string') {
throw new InstrumentationError('Event is a required field and should be a string');
}
@@ -2123,6 +2129,7 @@ module.exports = {
getIntegrationsObj,
getMappingConfig,
getMetadata,
+ getTransformationMetadata,
getParsedIP,
getStringValueOfJSON,
getSuccessRespEvents,
@@ -2170,7 +2177,7 @@ module.exports = {
getDestAuthCacheInstance,
refinePayload,
validateEmail,
- validateEventType,
+ validateEventName,
validatePhoneWithCountryCode,
getEventReqMetadata,
isHybridModeEnabled,
diff --git a/test/__tests__/data/adobe_analytics.json b/test/__tests__/data/adobe_analytics.json
index cfffccb8da..6361f92640 100644
--- a/test/__tests__/data/adobe_analytics.json
+++ b/test/__tests__/data/adobe_analytics.json
@@ -1010,7 +1010,6 @@
},
"messageId": "1578564113557-af022c68-429e-4af4-b99b-2b9174056383",
"properties": {
- "order_id": "1234",
"affiliation": "Apple Store",
"value": 20,
"revenue": 15.0,
@@ -1019,6 +1018,7 @@
"discount": 1.5,
"coupon": "ImagePro",
"currency": "USD",
+ "purchaseId": "p101",
"products": [
{
"product_id": "123",
@@ -1205,7 +1205,7 @@
"JSON": {},
"JSON_ARRAY": {},
"XML": {
- "payload": "17941080sales campaignwebUSD127.0.0.1en-US12341234Dalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerroottval001RudderLabs JavaScript SDKocheckout startedhttps://www.estore.com/best-seller/12020-01-09T10:01:53.558Zmktcloudid001scCheckoutGames;Monopoly;1;14.00,Games;UNO;2;6.90footlockerrudderstackpoc"
+ "payload": "17941080sales campaignwebUSD127.0.0.1en-USDalvik/2.1.0 (Linux; U; Android 9; Android SDK built for x86 Build/PSR1.180720.075)https://www.google.com/search?q=estore+bestsellerroottval001RudderLabs JavaScript SDKocheckout startedhttps://www.estore.com/best-seller/12020-01-09T10:01:53.558Zmktcloudid001p101scCheckoutGames;Monopoly;1;14.00,Games;UNO;2;6.90footlockerrudderstackpoc"
},
"FORM": {}
},
diff --git a/test/__tests__/data/freshmarketer.json b/test/__tests__/data/freshmarketer.json
index 3d30841b30..390c0fb44e 100644
--- a/test/__tests__/data/freshmarketer.json
+++ b/test/__tests__/data/freshmarketer.json
@@ -5,7 +5,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"message": {
@@ -94,7 +94,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"message": {
@@ -183,7 +183,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"message": {
@@ -248,7 +248,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"message": {
@@ -312,7 +312,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -422,7 +422,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -491,7 +491,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -558,7 +558,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -627,7 +627,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -760,7 +760,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -843,7 +843,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -927,7 +927,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1019,7 +1019,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1127,7 +1127,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1228,7 +1228,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1315,7 +1315,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1406,7 +1406,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1496,7 +1496,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1586,7 +1586,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1696,7 +1696,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1757,7 +1757,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1788,7 +1788,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1825,7 +1825,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1892,7 +1892,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1937,7 +1937,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1976,7 +1976,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -2043,7 +2043,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -2145,7 +2145,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -2232,7 +2232,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -2334,7 +2334,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -2427,7 +2427,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder",
+ "domain": "domain-rudder.myfreshworks.com",
"rudderEventsToFreshmarketerEvents": [
{
"from": "test_activity",
diff --git a/test/__tests__/data/freshmarketer_router_input.json b/test/__tests__/data/freshmarketer_router_input.json
index 0e05c7f5f8..2cc5ce58de 100644
--- a/test/__tests__/data/freshmarketer_router_input.json
+++ b/test/__tests__/data/freshmarketer_router_input.json
@@ -3,7 +3,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"metadata": {
@@ -59,7 +59,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"metadata": {
@@ -115,7 +115,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"metadata": {
diff --git a/test/__tests__/data/freshmarketer_router_output.json b/test/__tests__/data/freshmarketer_router_output.json
index 01740cb626..3525e4bb16 100644
--- a/test/__tests__/data/freshmarketer_router_output.json
+++ b/test/__tests__/data/freshmarketer_router_output.json
@@ -43,7 +43,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
}
},
@@ -91,7 +91,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
}
},
@@ -156,7 +156,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
}
diff --git a/test/__tests__/data/freshsales.json b/test/__tests__/data/freshsales.json
index 2527e37b90..55193532f4 100644
--- a/test/__tests__/data/freshsales.json
+++ b/test/__tests__/data/freshsales.json
@@ -69,7 +69,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -90,7 +90,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"message": {
@@ -179,7 +179,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"message": {
@@ -268,7 +268,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"message": {
@@ -356,7 +356,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "rudderstack-476952domain3105"
+ "domain": "rudderstack-476952domain3105.myfreshworks.com"
}
},
"message": {
@@ -421,7 +421,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -531,7 +531,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -600,7 +600,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -667,7 +667,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -736,7 +736,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
},
"message": {
@@ -869,7 +869,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -952,7 +952,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1036,7 +1036,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1128,7 +1128,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder",
+ "domain": "domain-rudder.myfreshworks.com",
"rudderEventsToFreshsalesEvents": [
{
"from": "test",
@@ -1244,7 +1244,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1345,7 +1345,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1432,7 +1432,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1523,7 +1523,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1613,7 +1613,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1703,7 +1703,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1813,7 +1813,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1874,7 +1874,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -1966,7 +1966,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -2053,7 +2053,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
@@ -2155,7 +2155,7 @@
"destination": {
"Config": {
"apiKey": "dummyApiKey",
- "domain": "domain-rudder"
+ "domain": "domain-rudder.myfreshworks.com"
}
}
},
diff --git a/test/__tests__/data/freshsales_router_input.json b/test/__tests__/data/freshsales_router_input.json
index 856a127f51..ea4b9887dd 100644
--- a/test/__tests__/data/freshsales_router_input.json
+++ b/test/__tests__/data/freshsales_router_input.json
@@ -66,7 +66,7 @@
},
"Config": {
"apiKey": "hrkjfergeferf",
- "domain": "rudderstack-479541159204968909"
+ "domain": "rudderstack-479541159204968909.myfreshworks.com"
},
"Enabled": true,
"Transformations": [],
diff --git a/test/__tests__/data/freshsales_router_output.json b/test/__tests__/data/freshsales_router_output.json
index 449d6eb45a..69d259ff00 100644
--- a/test/__tests__/data/freshsales_router_output.json
+++ b/test/__tests__/data/freshsales_router_output.json
@@ -72,7 +72,7 @@
},
"Config": {
"apiKey": "hrkjfergeferf",
- "domain": "rudderstack-479541159204968909"
+ "domain": "rudderstack-479541159204968909.myfreshworks.com"
},
"Enabled": true,
"Transformations": [],
diff --git a/test/integrations/sources/revenuecat/data.ts b/test/integrations/sources/revenuecat/data.ts
new file mode 100644
index 0000000000..4963781763
--- /dev/null
+++ b/test/integrations/sources/revenuecat/data.ts
@@ -0,0 +1,286 @@
+export const data = [
+ {
+ name: 'revenuecat',
+ description: 'Simple track call',
+ module: 'source',
+ version: 'v0',
+ input: {
+ request: {
+ body: [
+ {
+ api_version: '1.0',
+ event: {
+ aliases: [
+ 'f8e14f51-0c76-49ba-8d67-c229f1875dd9',
+ '389ad6dd-bb40-4c03-9471-1353da2d55ec',
+ ],
+ app_user_id: 'f8e14f51-0c76-49ba-8d67-c229f1875dd9',
+ commission_percentage: null,
+ country_code: 'US',
+ currency: null,
+ entitlement_id: null,
+ entitlement_ids: null,
+ environment: 'SANDBOX',
+ event_timestamp_ms: 1698617217232,
+ expiration_at_ms: 1698624417232,
+ id: '8CF0CD6C-CAF3-41FB-968A-661938235AF0',
+ is_family_share: null,
+ offer_code: null,
+ original_app_user_id: 'f8e14f51-0c76-49ba-8d67-c229f1875dd9',
+ original_transaction_id: null,
+ period_type: 'NORMAL',
+ presented_offering_id: null,
+ price: null,
+ price_in_purchased_currency: null,
+ product_id: 'test_product',
+ purchased_at_ms: 1698617217232,
+ store: 'APP_STORE',
+ subscriber_attributes: {
+ $displayName: {
+ updated_at_ms: 1698617217232,
+ value: 'Mister Mistoffelees',
+ },
+ $email: {
+ updated_at_ms: 1698617217232,
+ value: 'tuxedo@revenuecat.com',
+ },
+ $phoneNumber: {
+ updated_at_ms: 1698617217232,
+ value: '+19795551234',
+ },
+ my_custom_attribute_1: {
+ updated_at_ms: 1698617217232,
+ value: 'catnip',
+ },
+ },
+ takehome_percentage: null,
+ tax_percentage: null,
+ transaction_id: null,
+ type: 'TEST',
+ },
+ },
+ ],
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ },
+ pathSuffix: '',
+ },
+ output: {
+ response: {
+ status: 200,
+ body: [
+ {
+ output: {
+ batch: [
+ {
+ context: {
+ library: {
+ name: 'unknown',
+ version: 'unknown',
+ },
+ integration: {
+ name: 'RevenueCat',
+ },
+ externalId: [
+ {
+ type: 'revenuecatAppUserId',
+ id: 'f8e14f51-0c76-49ba-8d67-c229f1875dd9',
+ },
+ ],
+ },
+ integrations: {
+ RevenueCat: false,
+ },
+ type: 'track',
+ properties: {
+ aliases: [
+ 'f8e14f51-0c76-49ba-8d67-c229f1875dd9',
+ '389ad6dd-bb40-4c03-9471-1353da2d55ec',
+ ],
+ appUserId: 'f8e14f51-0c76-49ba-8d67-c229f1875dd9',
+ commissionPercentage: null,
+ countryCode: 'US',
+ currency: null,
+ entitlementId: null,
+ entitlementIds: null,
+ environment: 'SANDBOX',
+ eventTimestampMs: 1698617217232,
+ expirationAtMs: 1698624417232,
+ id: '8CF0CD6C-CAF3-41FB-968A-661938235AF0',
+ isFamilyShare: null,
+ offerCode: null,
+ originalAppUserId: 'f8e14f51-0c76-49ba-8d67-c229f1875dd9',
+ originalTransactionId: null,
+ periodType: 'NORMAL',
+ presentedOfferingId: null,
+ price: null,
+ priceInPurchasedCurrency: null,
+ productId: 'test_product',
+ purchasedAtMs: 1698617217232,
+ store: 'APP_STORE',
+ subscriberAttributes: {
+ $displayName: {
+ updated_at_ms: 1698617217232,
+ value: 'Mister Mistoffelees',
+ },
+ $email: {
+ updated_at_ms: 1698617217232,
+ value: 'tuxedo@revenuecat.com',
+ },
+ $phoneNumber: {
+ updated_at_ms: 1698617217232,
+ value: '+19795551234',
+ },
+ my_custom_attribute_1: {
+ updated_at_ms: 1698617217232,
+ value: 'catnip',
+ },
+ },
+ takehomePercentage: null,
+ taxPercentage: null,
+ transactionId: null,
+ type: 'TEST',
+ },
+ event: 'TEST',
+ messageId: '8CF0CD6C-CAF3-41FB-968A-661938235AF0',
+ originalTimestamp: '2023-10-29T22:06:57.232Z',
+ sentAt: '2023-10-29T22:06:57.232Z',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ name: 'revenuecat',
+ description: 'Initial purchase event',
+ module: 'source',
+ version: 'v0',
+ input: {
+ request: {
+ body: [
+ {
+ api_version: '1.0',
+ event: {
+ aliases: ['yourCustomerAliasedID', 'yourCustomerAliasedID'],
+ app_id: 'yourAppID',
+ app_user_id: 'yourCustomerAppUserID',
+ commission_percentage: 0.3,
+ country_code: 'US',
+ currency: 'USD',
+ entitlement_id: 'pro_cat',
+ entitlement_ids: ['pro_cat'],
+ environment: 'PRODUCTION',
+ event_timestamp_ms: 1591121855319,
+ expiration_at_ms: 1591726653000,
+ id: 'UniqueIdentifierOfEvent',
+ is_family_share: false,
+ offer_code: 'free_month',
+ original_app_user_id: 'OriginalAppUserID',
+ original_transaction_id: '1530648507000',
+ period_type: 'NORMAL',
+ presented_offering_id: 'OfferingID',
+ price: 2.49,
+ price_in_purchased_currency: 2.49,
+ product_id: 'onemonth_no_trial',
+ purchased_at_ms: 1591121853000,
+ store: 'APP_STORE',
+ subscriber_attributes: {
+ '$Favorite Cat': {
+ updated_at_ms: 1581121853000,
+ value: 'Garfield',
+ },
+ },
+ takehome_percentage: 0.7,
+ tax_percentage: 0.3,
+ transaction_id: '170000869511114',
+ type: 'INITIAL_PURCHASE',
+ },
+ },
+ ],
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ },
+ pathSuffix: '',
+ },
+ output: {
+ response: {
+ status: 200,
+ body: [
+ {
+ output: {
+ batch: [
+ {
+ context: {
+ library: {
+ name: 'unknown',
+ version: 'unknown',
+ },
+ integration: {
+ name: 'RevenueCat',
+ },
+ externalId: [
+ {
+ type: 'revenuecatAppUserId',
+ id: 'yourCustomerAppUserID',
+ },
+ ],
+ },
+ integrations: {
+ RevenueCat: false,
+ },
+ type: 'track',
+ properties: {
+ aliases: ['yourCustomerAliasedID', 'yourCustomerAliasedID'],
+ appId: 'yourAppID',
+ appUserId: 'yourCustomerAppUserID',
+ commissionPercentage: 0.3,
+ countryCode: 'US',
+ currency: 'USD',
+ entitlementId: 'pro_cat',
+ entitlementIds: ['pro_cat'],
+ environment: 'PRODUCTION',
+ eventTimestampMs: 1591121855319,
+ expirationAtMs: 1591726653000,
+ id: 'UniqueIdentifierOfEvent',
+ isFamilyShare: false,
+ offerCode: 'free_month',
+ originalAppUserId: 'OriginalAppUserID',
+ originalTransactionId: '1530648507000',
+ periodType: 'NORMAL',
+ presentedOfferingId: 'OfferingID',
+ price: 2.49,
+ priceInPurchasedCurrency: 2.49,
+ productId: 'onemonth_no_trial',
+ purchasedAtMs: 1591121853000,
+ store: 'APP_STORE',
+ subscriberAttributes: {
+ '$Favorite Cat': {
+ updated_at_ms: 1581121853000,
+ value: 'Garfield',
+ },
+ },
+ takehomePercentage: 0.7,
+ taxPercentage: 0.3,
+ transactionId: '170000869511114',
+ type: 'INITIAL_PURCHASE',
+ },
+ event: 'INITIAL_PURCHASE',
+ messageId: 'UniqueIdentifierOfEvent',
+ originalTimestamp: '2020-06-02T18:17:35.319Z',
+ sentAt: '2020-06-02T18:17:35.319Z',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ },
+];