Skip to content

Commit

Permalink
Responsys new Action: Send to PET + Adjustments in Send Audience as P…
Browse files Browse the repository at this point in the history
…ET (segmentio#2615)

* - New Action for Responsys: Send to PET, creating the PET if it doesn't exist yet;\n- Adding  as an optional parameter in Settings;\n-  should be a password field;\n- Profile List Name now it's an optional field.

* PET fields can't have more than 30 characteres in their names.

* New Action for Responsys: Send to PET.

* - Having  truly work with sync/async endpoints;\n- For async requests, retrieving the corresponding response;\n- Improving profile lists update criteria.

* Adding unit tests for  action.

* Adding rate limits to all actions that are not marked as legacy.

* With rate limits, 'sendToPet' doesn't require a retry mechanism per parameter.

* Adding default permission status to Responsys mappings;\nRate limits for async request fetching.

* Additional generations.

* Making 'sendAudienceAsPet' to work with async endpoints.

* Unit Tests adjustments.

* Splitting requests in batches of 200 records. Updating unit tests.

* Implementing batches of 200 records for upsertListMember.

* Reversing 'Legacy' naming for 'sendCustomTraits'.

* - Adding verification when PET exists in another folder;
- Forcing US as default region for debug messages.

* On profile list update, splitting batch into multiple record sets, to avoid overwriting empty values in a profile list.

* Updating unit tests for Responsys.

* Updating geerated types.

* Fixing issue with .
  • Loading branch information
seg-leonelsanches authored Dec 17, 2024
1 parent 3ea984b commit 08d2652
Show file tree
Hide file tree
Showing 19 changed files with 1,149 additions and 274 deletions.

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

24 changes: 17 additions & 7 deletions packages/destination-actions/src/destinations/responsys/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sendAudience from './sendAudience'
import upsertListMember from './upsertListMember'

import sendAudienceAsPet from './sendAudienceAsPet'
import sendToPet from './sendToPet'

interface RefreshTokenResponse {
authToken: string
Expand Down Expand Up @@ -45,7 +46,7 @@ const destination: DestinationDefinition<Settings> = {
userPassword: {
label: 'Password',
description: 'Responsys password',
type: 'string',
type: 'password',
required: true
},
baseUrl: {
Expand All @@ -56,15 +57,22 @@ const destination: DestinationDefinition<Settings> = {
format: 'uri',
required: true
},
defaultFolderName: {
label: 'Default Folder Name',
description: 'Name of the folder where the Profile Extension Table is located.',
type: 'string',
required: false
},
profileListName: {
label: 'List Name',
label: 'Default Profile List Name',
description: "Name of the Profile Extension Table's Contact List.",
type: 'string',
required: true
required: false
},
profileExtensionTable: {
label: 'PET Name',
description: 'Profile Extension Table (PET) Name. Required if using the "Send Custom Traits" Action.',
label: 'Default PET Name',
description:
'Default Profile Extension Table (PET) Name. Required if using the "Send Custom Traits" Action. Can be overridden in the mapping.',
type: 'string',
required: false
},
Expand Down Expand Up @@ -150,7 +158,8 @@ const destination: DestinationDefinition<Settings> = {
},
defaultPermissionStatus: {
label: 'Default Permission Status',
description: 'This value must be specified as either OPTIN or OPTOUT. defaults to OPTOUT.',
description:
'This value must be specified as either OPTIN or OPTOUT. Defaults to OPTOUT. Can be overridden in the mapping.',
type: 'string',
required: true,
choices: [
Expand Down Expand Up @@ -220,7 +229,8 @@ const destination: DestinationDefinition<Settings> = {
sendAudience,
sendAudienceAsPet,
sendCustomTraits,
upsertListMember
upsertListMember,
sendToPet
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ActionDefinition } from '@segment/actions-core'
import type { Settings } from '../generated-types'
import type { Payload } from './generated-types'
import { enable_batching, batch_size } from '../shared-properties'
import { sendCustomTraits, getUserDataFieldNames, validateCustomTraits, validateListMemberPayload } from '../utils'
import { use_responsys_async_api, batch_size } from '../shared-properties'
import { sendCustomTraits, getUserDataFieldNames, testConditionsToRetry, validateListMemberPayload } from '../utils'
import { Data } from '../types'

const action: ActionDefinition<Settings, Payload> = {
Expand Down Expand Up @@ -75,7 +75,7 @@ const action: ActionDefinition<Settings, Payload> = {
default: { '@path': '$.context.personas.computation_class' },
choices: [{ label: 'Audience', value: 'audience' }]
},
enable_batching: enable_batching,
enable_batching: use_responsys_async_api,
batch_size: batch_size,
stringify: {
label: 'Stringify Recipient Data',
Expand Down Expand Up @@ -116,7 +116,7 @@ const action: ActionDefinition<Settings, Payload> = {
perform: async (request, data) => {
const { payload, settings, statsContext } = data
const userDataFieldNames: string[] = getUserDataFieldNames(data as unknown as Data)
validateCustomTraits({
testConditionsToRetry({
profileExtensionTable: settings.profileExtensionTable,
timestamp: payload.timestamp,
statsContext: statsContext,
Expand All @@ -130,7 +130,7 @@ const action: ActionDefinition<Settings, Payload> = {
performBatch: async (request, data) => {
const { payload, settings, statsContext } = data
const userDataFieldNames = getUserDataFieldNames(data as unknown as Data)
validateCustomTraits({
testConditionsToRetry({
profileExtensionTable: settings.profileExtensionTable,
timestamp: payload[0].timestamp,
statsContext: statsContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const responsysHost = 'https://123456-api.responsys.ocs.oraclecloud.com'
const profileListName = 'TEST_PROFILE_LIST'
const folderName = 'TEST_FOLDER'

jest.setTimeout(10000)

describe('Responsys.sendAudienceAsPet', () => {
describe('Successful scenarios', () => {
describe('Single event', () => {
Expand Down Expand Up @@ -55,23 +57,30 @@ describe('Responsys.sendAudienceAsPet', () => {
updateOnMatch: 'REPLACE_ALL',
defaultPermissionStatus: 'OPTOUT',
profileListName
},
auth: {
accessToken: 'abcd1234',
refreshToken: 'efgh5678'
}
}

it('sends an event with default mappings + default settings, PET does not exist yet', async () => {
nock(responsysHost).get(`/rest/api/v1.3/lists/${profileListName}/listExtensions`).reply(200, [])

nock(responsysHost)
.post(`/rest/api/v1.3/lists/${profileListName}/listExtensions`)
.reply(200, { results: [{}] })
nock(responsysHost).post(`/rest/api/v1.3/lists/${profileListName}/listExtensions`).reply(200, {})

nock(responsysHost)
.post(`/rest/asyncApi/v1.3/lists/${profileListName}/members`)
.reply(200, { results: [{}] })
nock(responsysHost).post(`/rest/asyncApi/v1.3/lists/${profileListName}/members`).reply(200, {
requestId: '23456'
})

nock(responsysHost)
.post(`/rest/api/v1.3/lists/${profileListName}/listExtensions/${audienceKey}/members`)
.reply(200, { results: [{}] })
.post(`/rest/asyncApi/v1.3/lists/${profileListName}/listExtensions/${audienceKey}/members`)
.reply(200, {
requestId: '34567'
})

nock(responsysHost).get(`/rest/asyncApi/v1.3/requests/23456`).reply(200, {})
nock(responsysHost).get(`/rest/asyncApi/v1.3/requests/34567`).reply(200, {})

const responses = await testDestination.testAction('sendAudienceAsPet', actionPayload)

Expand All @@ -91,13 +100,18 @@ describe('Responsys.sendAudienceAsPet', () => {
}
])

nock(responsysHost)
.post(`/rest/asyncApi/v1.3/lists/${profileListName}/members`)
.reply(200, { results: [{}] })
nock(responsysHost).post(`/rest/asyncApi/v1.3/lists/${profileListName}/members`).reply(200, {
requestId: '23456'
})

nock(responsysHost)
.post(`/rest/api/v1.3/lists/${profileListName}/listExtensions/${audienceKey}/members`)
.reply(200, { results: [{}] })
.post(`/rest/asyncApi/v1.3/lists/${profileListName}/listExtensions/${audienceKey}/members`)
.reply(200, {
requestId: '34567'
})

nock(responsysHost).get(`/rest/asyncApi/v1.3/requests/23456`).reply(200, {})
nock(responsysHost).get(`/rest/asyncApi/v1.3/requests/34567`).reply(200, {})

const responses = await testDestination.testAction('sendAudienceAsPet', actionPayload)

Expand Down Expand Up @@ -186,30 +200,30 @@ describe('Responsys.sendAudienceAsPet', () => {
updateOnMatch: 'REPLACE_ALL',
defaultPermissionStatus: 'OPTOUT',
profileListName
},
auth: {
accessToken: 'abcd1234',
refreshToken: 'efgh5678'
}
}

nock(responsysHost).get(`/rest/api/v1.3/lists/${profileListName}/listExtensions`).reply(200, [])

nock(responsysHost)
.post(`/rest/api/v1.3/lists/${profileListName}/listExtensions`)
.reply(200, { results: [{}] })
nock(responsysHost).post(`/rest/api/v1.3/lists/${profileListName}/listExtensions`).reply(200, {})

nock(responsysHost)
.post(`/rest/asyncApi/v1.3/lists/${profileListName}/members`)
.reply(200, { results: [{}] })
nock(responsysHost).post(`/rest/asyncApi/v1.3/lists/${profileListName}/members`).reply(200, {
requestId: '23456'
})

nock(responsysHost)
.post(`/rest/api/v1.3/lists/${profileListName}/listExtensions/looney_tunes_audience/members`)
.post(`/rest/asyncApi/v1.3/lists/${profileListName}/listExtensions/looney_tunes_audience/members`)
.times(3)
.reply(200, { results: [{}] })
.reply(200, {
requestId: '34567'
})

/* for (const event of events) {
;(event.context as any).personas.audience_settings = {
computation_key: 'looney_tunes_audience',
external_audience_id: '12345'
}
} */
nock(responsysHost).persist().get(`/rest/asyncApi/v1.3/requests/23456`).reply(200, {})
nock(responsysHost).persist().get(`/rest/asyncApi/v1.3/requests/34567`).reply(200, {})

const responses = await testDestination.executeBatch('sendAudienceAsPet', actionPayload)

Expand Down
Loading

0 comments on commit 08d2652

Please sign in to comment.