Skip to content

Commit

Permalink
feat: add json-data type support in redis (#3336)
Browse files Browse the repository at this point in the history
  • Loading branch information
sanpj2292 authored May 21, 2024
1 parent ffd4958 commit 0196f20
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/v0/destinations/redis/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const lodash = require('lodash');
const flatten = require('flat');

const { InstrumentationError } = require('@rudderstack/integrations-lib');
const { isEmpty, isObject } = require('../../util');
const { isEmpty, isObject, getFieldValueFromMessage } = require('../../util');
const { EventType } = require('../../../constants');

// processValues:
Expand Down Expand Up @@ -46,6 +46,19 @@ const transformSubEventTypeProfiles = (message, workspaceId, destinationId) => {
};
};

const getJSONValue = (message) => {
const eventType = message.type.toLowerCase();
if (eventType === EventType.IDENTIFY) {
return getFieldValueFromMessage(message, 'traits');
}
return {};
};

const getTransformedPayloadForJSON = ({ key, path, value, userId }) => ({
message: { key, path, value },
userId,
});

const process = (event) => {
const { message, destination, metadata } = event;
const messageType = message && message.type && message.type.toLowerCase();
Expand All @@ -58,15 +71,35 @@ const process = (event) => {
throw new InstrumentationError('Blank userId passed in identify event');
}

const { prefix } = destination.Config;
const { prefix, useJSONModule } = destination.Config;
const destinationId = destination.ID;
const keyPrefix = isEmpty(prefix) ? '' : `${prefix.trim()}:`;

const jsonValue = getJSONValue(message);

if (isSubEventTypeProfiles(message)) {
const { workspaceId } = metadata;
if (useJSONModule) {
// If redis should store information as JSON type
return getTransformedPayloadForJSON({
key: `${workspaceId}:${destinationId}:${message.context.sources.profiles_entity}:${message.context.sources.profiles_id_type}:${message.userId}`,
path: message.context.sources.profiles_model,
value: jsonValue,
userId: message.userId,
});
}
return transformSubEventTypeProfiles(message, workspaceId, destinationId);
}

if (useJSONModule) {
// If redis should store information as JSON type
return getTransformedPayloadForJSON({
key: `${keyPrefix}user:${lodash.toString(message.userId)}`,
value: jsonValue,
userId: message.userId,
});
}

const hmap = {
key: `${keyPrefix}user:${lodash.toString(message.userId)}`,
fields: {},
Expand Down
205 changes: 205 additions & 0 deletions test/integrations/destinations/redis/processor/data.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,207 @@
import { RedisTc, SupportedEventTypes, TraitsLocation } from '../types';

const getRedisTestCase = (tc: RedisTc) => {
const context = {
app: {
build: '1.0.0',
name: 'RudderLabs JavaScript SDK',
namespace: 'com.rudderlabs.javascript',
version: '1.0.5',
},
ip: '0.0.0.0',
library: {
name: 'RudderLabs JavaScript SDK',
version: '1.0.5',
},
locale: 'en-GB',
os: {
name: '',
version: '',
},
screen: {
density: 2,
},
traits: {},
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36',
};
return {
name: 'redis',
description: tc.description,
feature: 'processor',
module: 'destination',
version: 'v0',
input: {
request: {
body: [
{
metadata: {
workspaceId: tc.workspaceId || 'wspId',
},
destination: {
Config: {
address: 'localhost:6379',
database: 'test',
prefix: ' ',
...tc.destinationConfig,
},
DestinationDefinition: {
DisplayName: 'Redis',
ID: '1WhbSZ6uA3H5ChVifHpfL2H6sie',
Name: 'REDIS',
},
Enabled: true,
ID: tc.destId || '1WhcOCGgj9asZu850HvugU2C3Aq',
Name: 'Redis',

Transformations: [],
},
message: {
anonymousId: 'e6ab2c5e-2cda-44a9-a962-e2f67df78bca',
channel: 'web',
...(tc.traitsLocation === TraitsLocation.Traits ? { traits: tc.traits } : {}),
context: {
...context,
...(tc.traitsLocation === TraitsLocation.ContextTraits
? { traits: tc.traits }
: {}),
...(tc.profilesContext ?? {}),
},
integrations: {
All: true,
},
messageId: '2536eda4-d638-4c93-8014-8ffe3f083214',
originalTimestamp: '2020-01-24T06:29:02.362Z',
receivedAt: '2020-01-24T11:59:02.403+05:30',
request_ip: '[::1]:53708',
sentAt: '2020-01-24T06:29:02.363Z',
timestamp: '2020-01-24T11:59:02.402+05:30',
type: tc.eventType,
userId: tc.userId || 'myUserId',
},
},
],
},
},
output: {
response: {
status: 200,
body: [
{
metadata: {
workspaceId: tc.workspaceId || 'wspId',
},
output: tc.expectedResponse,
statusCode: tc.expectedStatusCode,
},
],
},
},
};
};

const jsonTestCases: RedisTc[] = [
{
description:
'Test 6: [Event-stream] when useJSONModule is enabled with traits in context.traits, should transform successfully',
destinationConfig: {
useJSONModule: true,
},
traitsLocation: TraitsLocation.ContextTraits,
traits: {
prop1: 'k',
prop2: 2,
prop3: {
p4: 'a',
},
},
eventType: SupportedEventTypes.Identify,
userId: 'user-1',
expectedResponse: {
message: {
key: 'user:user-1',
value: {
prop1: 'k',
prop2: 2,
prop3: {
p4: 'a',
},
},
},
userId: 'user-1',
},
expectedStatusCode: 200,
},
{
description:
'Test 7: [Event-stream] when useJSONModule is enabled with traits in traits, should transform successfully',
destinationConfig: {
useJSONModule: true,
},
traitsLocation: TraitsLocation.Traits,
traits: {
prop1: 'k',
prop2: 2,
prop3: {
p4: 'a',
},
},
eventType: SupportedEventTypes.Identify,
userId: 'user-1',
expectedResponse: {
message: {
key: 'user:user-1',
value: {
prop1: 'k',
prop2: 2,
prop3: {
p4: 'a',
},
},
},
userId: 'user-1',
},
expectedStatusCode: 200,
},
{
description:
'Test 8: [Profiles] when useJSONModule is enabled with traits in traits, should transform successfully',
destinationConfig: {
useJSONModule: true,
},
destId: 'd1',
workspaceId: 'w1',
traitsLocation: TraitsLocation.Traits,
traits: {
field: 'k',
model_name: 'analytics_app',
},
profilesContext: {
sources: {
profiles_entity: 'e1',
profiles_model: 'model1',
profiles_id_type: 'id-1',
},
},
eventType: SupportedEventTypes.Identify,
userId: 'user-2',
expectedResponse: {
message: {
key: 'w1:d1:e1:id-1:user-2',
path: 'model1',
value: {
field: 'k',
model_name: 'analytics_app',
},
},
userId: 'user-2',
},
expectedStatusCode: 200,
},
];

const jsonTcs = jsonTestCases.map((tc) => getRedisTestCase(tc));

export const data = [
{
name: 'redis',
Expand Down Expand Up @@ -658,4 +862,5 @@ export const data = [
},
},
},
...jsonTcs,
];
39 changes: 39 additions & 0 deletions test/integrations/destinations/redis/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export type RedisConfig = Partial<{
address: string;
database: string;
prefix: string;
useJSONModule: boolean;
}>;

type RedisJSONOutput = {
message: Record<string, any>;
userId: string;
};

export enum TraitsLocation {
ContextTraits = 'context.traits',
Traits = 'traits',
}
export enum SupportedEventTypes {
Identify = 'identify',
}

export type RedisTc = {
description: string;
destId?: string;
workspaceId?: string;
destinationConfig: RedisConfig;
traitsLocation: TraitsLocation;
traits: Record<string, any>;
eventType: SupportedEventTypes;
userId?: string;
expectedResponse: RedisJSONOutput;
expectedStatusCode: number;
profilesContext?: {
sources: {
profiles_entity: string;
profiles_id_type: string;
profiles_model: string;
};
};
};

0 comments on commit 0196f20

Please sign in to comment.