Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const destination: DestinationDefinition<Settings> = {
description: `
This destination allows user to engage audiences using Rokt's Public APIs.
User can connect Rokt Audiences (Actions) as a destination to their Engage Audience in Segment,
which will create/update custom audiences in the Rokt data platform.
which will create/update custom audiences in the Rokt data platform. Audiences can be defined with either
email values or hashed (sha256) email values.
`,
authentication: {
scheme: 'custom',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ const goodEvent = createTestEvent({
}
})

const goodEventHashedEmail = createTestEvent({
context: {
personas: {
computation_class: 'audience',
computation_key: 'aval_test_two_track_only'
},
traits: {
email_sha256: 'd2a904f42a7f3632a0b9df96d33071b7ea31517d38fe3a8b1333f1ccec475f8c'
}
},
traits: {
email_sha256: 'd2a904f42a7f3632a0b9df96d33071b7ea31517d38fe3a8b1333f1ccec475f8c',
aval_test_audience_for_chewy: true
},
properties: {
audience_key: 'aval_test_two_track_only',
aval_test_two_track_only: true
}
})

const badEvent = createTestEvent({
context: {
personas: {
Expand Down Expand Up @@ -56,6 +76,17 @@ describe('RoktAudiences.upsertCustomAudiences', () => {
).resolves.not.toThrowError()
})

it('should not throw an error if the audience creation succeed - hashed emails', async () => {
nock(CONSTANTS.ROKT_API_BASE_URL).post(CONSTANTS.ROKT_API_CUSTOM_AUDIENCE_ENDPOINT).reply(201)

await expect(
testDestination.testAction('upsertCustomAudiences', {
event: goodEventHashedEmail,
useDefaultMappings: true
})
).resolves.not.toThrowError()
})

it('should throw an error if the audience creation failed, bad body', async () => {
nock(CONSTANTS.ROKT_API_BASE_URL).post(CONSTANTS.ROKT_API_CUSTOM_AUDIENCE_ENDPOINT).reply(400)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ type CustomAudienceOperation = {
action: string
list: string
emails: string[]
sha256s: string[]
}

/**
* getCustomAudienceOperations parses event payloads from segment to convert to request object for Rokt api
* getCustomAudienceOperations parses event payloads from segment to convert to request object for Rokt api.
* Each audience receives its own pair of include and exclude lists.
* @payload payload of events
*/

const getCustomAudienceOperations = (payload: Payload[], settings: Settings): CustomAudienceOperation[] => {
let is_hashed = false

// map to handle different audiences in the batch
// this will contain audience_name=>[action=>emails]
const audience_map = new Map<string, Map<string, string[]>>([])
Expand All @@ -49,11 +52,25 @@ const getCustomAudienceOperations = (payload: Payload[], settings: Settings): Cu
}

if (p.traits_or_props[p.custom_audience_name] === true) {
// audience entered 'true', include email to list
action_map.get(CONSTANTS.INCLUDE)?.push(p.email)
// audience entered 'true', include email or hashed email from list
if (p.email_sha256 !== undefined) {
action_map.get(CONSTANTS.INCLUDE)?.push(p.email_sha256)
is_hashed = true
} else {
if (p.email !== undefined) {
action_map.get(CONSTANTS.INCLUDE)?.push(p.email)
}
}
} else if (p.traits_or_props[p.custom_audience_name] === false) {
// audience entered 'false', exclude email from list
action_map.get(CONSTANTS.EXCLUDE)?.push(p.email)
// audience entered 'false', exclude email or hashed email from list
if (p.email_sha256 !== undefined) {
action_map.get(CONSTANTS.EXCLUDE)?.push(p.email_sha256)
is_hashed = true
} else {
if (p.email !== undefined) {
action_map.get(CONSTANTS.EXCLUDE)?.push(p.email)
}
}
}
}

Expand All @@ -64,12 +81,25 @@ const getCustomAudienceOperations = (payload: Payload[], settings: Settings): Cu
// key will be audience list
// value will map of action=>email_list
action_map_values.forEach((emails: string[], action: string) => {
const custom_audience_op: CustomAudienceOperation = {
accountId: settings.accountid,
list: list,
action: action,
emails: emails
let custom_audience_op: CustomAudienceOperation
if (is_hashed) {
custom_audience_op = {
accountId: settings.accountid,
list: list,
action: action,
emails: [],
sha256s: emails
}
} else {
custom_audience_op = {
accountId: settings.accountid,
list: list,
action: action,
emails: emails,
sha256s: []
}
}

custom_audience_ops.push(custom_audience_op)
})
})
Expand All @@ -87,7 +117,7 @@ async function processPayload(request: RequestClient, settings: Settings, events
const promises = []

for (const op of custom_audience_ops) {
if (op.emails.length > 0) {
if (op.emails.length > 0 || op.sha256s.length > 0) {
// if emails are present for action, send to Rokt. Push to list of promises
// There will be max 2 promises for 2 http requests ( include & exclude actions )
promises.push(
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ const action: ActionDefinition<Settings, Payload> = {
},
email: {
label: 'Email',
description: "User's email address for including/excluding from custom audience",
description:
"User's email address to be included/excluded from the custom audience. One of either email_sha256 or email must be specified.",
type: 'string',
format: 'email',
required: true,
required: {
// If emailSHA256 is not provided then email is required
conditions: [{ fieldKey: 'email_sha256', operator: 'is', value: undefined }]
},
default: {
'@if': {
exists: { '@path': '$.context.traits.email' },
Expand All @@ -41,6 +45,24 @@ const action: ActionDefinition<Settings, Payload> = {
}
}
},
email_sha256: {
label: 'Email SHA256',
description:
"User's SHA256-hashed email address to be included/excluded from the custom audience. One of either email_sha256 or email must be specified.",
type: 'string',
format: 'text',
required: {
// If email is not provided then emailSHA256 is required
conditions: [{ fieldKey: 'email', operator: 'is', value: undefined }]
},
default: {
'@if': {
exists: { '@path': '$.context.traits.email_sha256' },
then: { '@path': '$.context.traits.email_sha256' },
else: { '@path': '$.traits.email_sha256' }
}
}
},
traits_or_props: {
label: 'traits or properties object',
description: 'Object which will be computed differently for track and identify events',
Expand Down
Loading