Skip to content

Commit 19054ff

Browse files
[Sendgrid Lists] - update to sendgrid lists destination (#2648)
* udpate to sendgrid lists destination * fixing tests * another test
1 parent f32cad9 commit 19054ff

File tree

8 files changed

+480
-226
lines changed

8 files changed

+480
-226
lines changed

packages/destination-actions/src/destinations/sendgrid-audiences/syncAudience/__tests__/index.test.ts

Lines changed: 140 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import nock from 'nock'
22
import { createTestEvent, createTestIntegration, SegmentEvent, PayloadValidationError } from '@segment/actions-core'
33
import Definition from '../../index'
44
import { Settings } from '../../generated-types'
5-
import { validatePhone } from '../utils'
5+
import { validatePhone, toDateFormat } from '../utils'
66

77
let testDestination = createTestIntegration(Definition)
88

@@ -58,10 +58,12 @@ const mapping = {
5858
external_audience_id: { '@path': '$.context.personas.external_audience_id' },
5959
segment_audience_key: { '@path': '$.context.personas.computation_key' },
6060
traits_or_props: { '@path': '$.traits' },
61-
email: { '@path': '$.traits.email' },
62-
anonymous_id: { '@path': '$.anonymousId' },
63-
external_id: { '@path': '$.traits.external_id' },
64-
phone_number_id: { '@path': '$.traits.phone' },
61+
identifiers: {
62+
email: { '@path': '$.traits.email' },
63+
anonymous_id: { '@path': '$.anonymousId' },
64+
external_id: { '@path': '$.traits.external_id' },
65+
phone_number_id: { '@path': '$.traits.phone' }
66+
},
6567
user_attributes: {
6668
first_name: { '@path': '$.traits.first_name' },
6769
last_name: { '@path': '$.traits.last_name' },
@@ -72,11 +74,14 @@ const mapping = {
7274
country: { '@path': '$.traits.country' },
7375
postal_code: { '@path': '$.traits.postal_code' }
7476
},
75-
custom_fields: { '@path': '$.traits.custom_fields' },
77+
custom_text_fields: { '@path': '$.traits.custom_text_fields' },
78+
custom_number_fields: { '@path': '$.traits.custom_number_fields' },
79+
custom_date_fields: { '@path': '$.traits.custom_date_fields' },
7680
enable_batching: true,
7781
batch_size: 200
7882
}
79-
const addExpectedPayload = {
83+
84+
const upsertAddExpectedPayload = {
8085
list_ids: ['sg_audience_id_12345'],
8186
contacts: [
8287
{
@@ -98,7 +103,7 @@ describe('SendgridAudiences.syncAudience', () => {
98103
it('should upsert a single Contact and add it to a Sendgrid list correctly', async () => {
99104
const event = createTestEvent(addPayload)
100105

101-
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', addExpectedPayload).reply(200, {})
106+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertAddExpectedPayload).reply(200, {})
102107
const responses = await testDestination.testAction('syncAudience', {
103108
event,
104109
settings,
@@ -122,18 +127,22 @@ describe('SendgridAudiences.syncAudience', () => {
122127
state: 'CA',
123128
country: 'US',
124129
postal_code: "N88EU",
125-
custom_fields: {
130+
custom_text_fields: {
126131
custom_field_1: 'custom_field_1_value',
127-
custom_field_2: 2345,
128-
custom_field_3: '2024-01-01T00:00:00.000Z',
129132
custom_field_4: false, // should be removed
130133
custom_field_5: null // should be removed
134+
},
135+
custom_number_fields: {
136+
custom_field_2: 2345
137+
},
138+
custom_date_fields: {
139+
custom_field_3: '2024-01-01T00:00:00.000Z',
131140
}
132141
}
133142
})
134143

135144
const addExpectedPayloadWithAttributes = {
136-
...addExpectedPayload,
145+
...upsertAddExpectedPayload,
137146
contacts: [
138147
{
139148
@@ -151,7 +160,7 @@ describe('SendgridAudiences.syncAudience', () => {
151160
custom_fields: {
152161
custom_field_1: 'custom_field_1_value',
153162
custom_field_2: 2345,
154-
custom_field_3: '2024-01-01T00:00:00.000Z'
163+
custom_field_3: "01/01/2024"
155164
}
156165
}
157166
]
@@ -179,7 +188,7 @@ describe('SendgridAudiences.syncAudience', () => {
179188
addPayloadEmailOnly.anonymousId = undefined
180189

181190
const addExpectedPayloadEmailOnly = {
182-
...addExpectedPayload,
191+
...upsertAddExpectedPayload,
183192
contacts: [
184193
{
185194
@@ -203,7 +212,7 @@ describe('SendgridAudiences.syncAudience', () => {
203212
it('should upsert multiple Contacts and add them to a Sendgrid list correctly', async () => {
204213
const events = [createTestEvent(addPayload), createTestEvent(addPayload2)]
205214

206-
const addBatchExpectedPayload = {
215+
const upsertAddBatchExpectedPayload = {
207216
list_ids: ['sg_audience_id_12345'],
208217
contacts: [
209218
{
@@ -221,7 +230,7 @@ describe('SendgridAudiences.syncAudience', () => {
221230
]
222231
}
223232

224-
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', addBatchExpectedPayload).reply(200, {})
233+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertAddBatchExpectedPayload).reply(200, {})
225234
const responses = await testDestination.testBatchAction('syncAudience', {
226235
events,
227236
settings,
@@ -235,6 +244,18 @@ describe('SendgridAudiences.syncAudience', () => {
235244
it('should remove a single Contact from a Sendgrid list correctly', async () => {
236245
const event = createTestEvent(removePayload)
237246

247+
const upsertNoAddExpectedPayload = {
248+
list_ids: [],
249+
contacts: [
250+
{
251+
252+
external_id: 'some_external_id',
253+
phone_number_id: '+353123456789',
254+
anonymous_id: 'some_anonymous_id'
255+
}
256+
]
257+
}
258+
238259
const deletePath = `/v3/marketing/lists/sg_audience_id_12345/contacts?contact_ids=contact_1_id`
239260

240261
const searchQuery =
@@ -244,6 +265,8 @@ describe('SendgridAudiences.syncAudience', () => {
244265
result: [{ id: 'contact_1_id' }]
245266
}
246267

268+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertNoAddExpectedPayload).reply(200, {})
269+
247270
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery).reply(200, searchResponse)
248271

249272
nock('https://api.sendgrid.com').delete(deletePath).reply(200, {})
@@ -255,14 +278,33 @@ describe('SendgridAudiences.syncAudience', () => {
255278
mapping
256279
})
257280

258-
expect(responses.length).toBe(2)
281+
expect(responses.length).toBe(3)
259282
expect(responses[0].status).toBe(200)
260283
expect(responses[1].status).toBe(200)
284+
expect(responses[2].status).toBe(200)
261285
})
262286

263287
it('should remove multiple Contacts from a Sendgrid list correctly', async () => {
264288
const events = [createTestEvent(removePayload), createTestEvent(removePayload2)]
265289

290+
const upsertNoAddBatchExpectedPayload = {
291+
list_ids: [],
292+
contacts: [
293+
{
294+
295+
external_id: 'some_external_id',
296+
phone_number_id: '+353123456789',
297+
anonymous_id: 'some_anonymous_id'
298+
},
299+
{
300+
301+
external_id: 'some_external_id2',
302+
phone_number_id: '+353123456789',
303+
anonymous_id: 'some_anonymous_id2'
304+
}
305+
]
306+
}
307+
266308
const deletePath = `/v3/marketing/lists/sg_audience_id_12345/contacts?contact_ids=contact_1_id,contact_2_id`
267309

268310
const searchQuery =
@@ -272,6 +314,8 @@ describe('SendgridAudiences.syncAudience', () => {
272314
result: [{ id: 'contact_1_id' }, { id: 'contact_2_id' }]
273315
}
274316

317+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertNoAddBatchExpectedPayload).reply(200, {})
318+
275319
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery).reply(200, searchResponse)
276320

277321
nock('https://api.sendgrid.com').delete(deletePath).reply(200, {})
@@ -283,9 +327,10 @@ describe('SendgridAudiences.syncAudience', () => {
283327
mapping
284328
})
285329

286-
expect(responses.length).toBe(2)
330+
expect(responses.length).toBe(3)
287331
expect(responses[0].status).toBe(200)
288332
expect(responses[1].status).toBe(200)
333+
expect(responses[2].status).toBe(200)
289334
})
290335

291336
it('should add and remove multiple Contacts from a Sendgrid list correctly', async () => {
@@ -305,7 +350,7 @@ describe('SendgridAudiences.syncAudience', () => {
305350
result: [{ id: 'contact_1_id' }, { id: 'contact_2_id' }]
306351
}
307352

308-
const addBatchExpectedPayload = {
353+
const upsertAddBatchExpectedPayload = {
309354
list_ids: ['sg_audience_id_12345'],
310355
contacts: [
311356
{
@@ -323,7 +368,27 @@ describe('SendgridAudiences.syncAudience', () => {
323368
]
324369
}
325370

326-
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', addBatchExpectedPayload).reply(200, {})
371+
const upsertNoAddBatchExpectedPayload = {
372+
list_ids: [],
373+
contacts: [
374+
{
375+
376+
external_id: 'some_external_id',
377+
phone_number_id: '+353123456789',
378+
anonymous_id: 'some_anonymous_id'
379+
},
380+
{
381+
382+
external_id: 'some_external_id2',
383+
phone_number_id: '+353123456789',
384+
anonymous_id: 'some_anonymous_id2'
385+
}
386+
]
387+
}
388+
389+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertAddBatchExpectedPayload).reply(200, {})
390+
391+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertNoAddBatchExpectedPayload).reply(200, {})
327392

328393
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery).reply(200, searchResponse)
329394

@@ -336,10 +401,11 @@ describe('SendgridAudiences.syncAudience', () => {
336401
mapping
337402
})
338403

339-
expect(responses.length).toBe(3)
404+
expect(responses.length).toBe(4)
340405
expect(responses[0].status).toBe(200)
341406
expect(responses[1].status).toBe(200)
342407
expect(responses[2].status).toBe(200)
408+
expect(responses[3].status).toBe(200)
343409
})
344410

345411
it('should retry upserting to add Contacts after Sendgrid initially rejects some emails', async () => {
@@ -351,7 +417,7 @@ describe('SendgridAudiences.syncAudience', () => {
351417

352418
const events = [createTestEvent(addPayload), createTestEvent(badPayload)]
353419

354-
const addBatchExpectedPayload = {
420+
const upsertAddBatchExpectedPayload = {
355421
list_ids: ['sg_audience_id_12345'],
356422
contacts: [
357423
{
@@ -388,7 +454,7 @@ describe('SendgridAudiences.syncAudience', () => {
388454
]
389455
}
390456

391-
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', addBatchExpectedPayload).reply(400, responseError)
457+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertAddBatchExpectedPayload).reply(400, responseError)
392458
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', addBatchExpectedPayload2).reply(200, {})
393459

394460
const responses = await testDestination.testBatchAction('syncAudience', {
@@ -419,7 +485,7 @@ describe('SendgridAudiences.syncAudience', () => {
419485
mapping
420486
})
421487
).rejects.toThrowError(
422-
new PayloadValidationError(`At least one of email, anonymous_id, external_id or phone_number_id is required`)
488+
new PayloadValidationError(`No valid payloads found`)
423489
)
424490
})
425491

@@ -456,6 +522,18 @@ describe('SendgridAudiences.syncAudience', () => {
456522
anonymousId: `some_anonymous_id_${i + 1}`
457523
}))
458524

525+
const upsertNoAddBatchExpectedPayload = {
526+
list_ids: [],
527+
contacts: Array.from({ length: 30 }, (_, i) => (
528+
{
529+
email: `test${i + 1}@gmail.com`,
530+
external_id: `some_external_id_${i + 1}`,
531+
phone_number_id: `+35312345678${i + 1}`,
532+
anonymous_id: `some_anonymous_id_${i + 1}`
533+
}
534+
))
535+
}
536+
459537
const events = payloads.map(createTestEvent)
460538

461539
const searchQuery =
@@ -471,6 +549,8 @@ describe('SendgridAudiences.syncAudience', () => {
471549

472550
const deletePath = `/v3/marketing/lists/sg_audience_id_12345/contacts?contact_ids=contact_1_id,contact_2_id,contact_3_id,contact_4_id,contact_5_id,contact_6_id,contact_7_id,contact_8_id,contact_9_id,contact_10_id,contact_11_id,contact_12_id,contact_13_id,contact_14_id,contact_15_id,contact_16_id,contact_17_id,contact_18_id,contact_19_id,contact_20_id,contact_21_id,contact_22_id,contact_23_id,contact_24_id,contact_25_id,contact_26_id,contact_27_id,contact_28_id,contact_29_id,contact_30_id`
473551

552+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertNoAddBatchExpectedPayload).reply(200, {})
553+
474554
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery).reply(200, searchResponse)
475555
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery2).reply(200, searchResponse2)
476556
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery3).reply(200, searchResponse3)
@@ -484,11 +564,12 @@ describe('SendgridAudiences.syncAudience', () => {
484564
mapping
485565
})
486566

487-
expect(responses.length).toBe(4)
567+
expect(responses.length).toBe(5)
488568
expect(responses[0].status).toBe(200)
489569
expect(responses[1].status).toBe(200)
490570
expect(responses[2].status).toBe(200)
491571
expect(responses[3].status).toBe(200)
572+
expect(responses[4].status).toBe(200)
492573
})
493574

494575
it('should do multiple search and a multiple remove requests for large batch with many identifiers and more than 100 contacts', async () => {
@@ -503,6 +584,15 @@ describe('SendgridAudiences.syncAudience', () => {
503584
anonymousId: null
504585
}))
505586

587+
const upsertNoAddBatchExpectedPayload = {
588+
list_ids: [],
589+
contacts: Array.from({ length: 120 }, (_, i) => (
590+
{
591+
email: `test${i + 1}@gmail.com`
592+
}
593+
))
594+
}
595+
506596
const events = payloads.map(createTestEvent)
507597

508598
const searchQuery =
@@ -525,6 +615,8 @@ describe('SendgridAudiences.syncAudience', () => {
525615
(_, i) => `contact_${i + 101}_id`
526616
).join(',')}`
527617

618+
nock('https://api.sendgrid.com').put('/v3/marketing/contacts', upsertNoAddBatchExpectedPayload).reply(200, {})
619+
528620
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery).reply(200, searchResponse)
529621
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery2).reply(200, searchResponse2)
530622
nock('https://api.sendgrid.com').post('/v3/marketing/contacts/search', searchQuery3).reply(200, searchResponse3)
@@ -539,12 +631,13 @@ describe('SendgridAudiences.syncAudience', () => {
539631
mapping
540632
})
541633

542-
expect(responses.length).toBe(5)
634+
expect(responses.length).toBe(6)
543635
expect(responses[0].status).toBe(200)
544636
expect(responses[1].status).toBe(200)
545637
expect(responses[2].status).toBe(200)
546638
expect(responses[3].status).toBe(200)
547639
expect(responses[4].status).toBe(200)
640+
expect(responses[5].status).toBe(200)
548641
})
549642

550643
it('phone number should be E.164', async () => {
@@ -556,4 +649,25 @@ describe('SendgridAudiences.syncAudience', () => {
556649
expect(validatePhone(badPhone)).toBe(false)
557650
expect(validatePhone(badPhone2)).toBe(false)
558651
})
652+
653+
it('dates are formatted correctly to Sendgrid', async () => {
654+
const goodDate = "01-01-2024"
655+
const goodDate2 = "01-24-2024"
656+
const goodDate3 = '2024-01-01T00:00:00.000Z'
657+
const goodDate4 = "01/24/2024" // default for ambiguous dates is US ISO 8601 format MM/DD/YYYY
658+
const badDate = "13-01-2024"
659+
const badDate2 = "24/01/2024"
660+
const badDate3 = '+3531234567890678'
661+
662+
expect(toDateFormat(goodDate)).toBe("01/01/2024")
663+
expect(toDateFormat(goodDate2)).toBe("01/24/2024")
664+
expect(toDateFormat(goodDate3)).toBe("01/01/2024")
665+
expect(toDateFormat(goodDate4)).toBe("01/24/2024")
666+
expect(toDateFormat(badDate)).toBe(undefined)
667+
expect(toDateFormat(badDate2)).toBe(undefined)
668+
expect(toDateFormat(badDate3)).toBe(undefined)
669+
})
670+
671+
672+
559673
})

0 commit comments

Comments
 (0)