@@ -2,7 +2,7 @@ import nock from 'nock'
2
2
import { createTestEvent , createTestIntegration , SegmentEvent , PayloadValidationError } from '@segment/actions-core'
3
3
import Definition from '../../index'
4
4
import { Settings } from '../../generated-types'
5
- import { validatePhone } from '../utils'
5
+ import { validatePhone , toDateFormat } from '../utils'
6
6
7
7
let testDestination = createTestIntegration ( Definition )
8
8
@@ -58,10 +58,12 @@ const mapping = {
58
58
external_audience_id : { '@path' : '$.context.personas.external_audience_id' } ,
59
59
segment_audience_key : { '@path' : '$.context.personas.computation_key' } ,
60
60
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
+ } ,
65
67
user_attributes : {
66
68
first_name : { '@path' : '$.traits.first_name' } ,
67
69
last_name : { '@path' : '$.traits.last_name' } ,
@@ -72,11 +74,14 @@ const mapping = {
72
74
country : { '@path' : '$.traits.country' } ,
73
75
postal_code : { '@path' : '$.traits.postal_code' }
74
76
} ,
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' } ,
76
80
enable_batching : true ,
77
81
batch_size : 200
78
82
}
79
- const addExpectedPayload = {
83
+
84
+ const upsertAddExpectedPayload = {
80
85
list_ids : [ 'sg_audience_id_12345' ] ,
81
86
contacts : [
82
87
{
@@ -98,7 +103,7 @@ describe('SendgridAudiences.syncAudience', () => {
98
103
it ( 'should upsert a single Contact and add it to a Sendgrid list correctly' , async ( ) => {
99
104
const event = createTestEvent ( addPayload )
100
105
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 , { } )
102
107
const responses = await testDestination . testAction ( 'syncAudience' , {
103
108
event,
104
109
settings,
@@ -122,18 +127,22 @@ describe('SendgridAudiences.syncAudience', () => {
122
127
state : 'CA' ,
123
128
country : 'US' ,
124
129
postal_code : "N88EU" ,
125
- custom_fields : {
130
+ custom_text_fields : {
126
131
custom_field_1 : 'custom_field_1_value' ,
127
- custom_field_2 : 2345 ,
128
- custom_field_3 : '2024-01-01T00:00:00.000Z' ,
129
132
custom_field_4 : false , // should be removed
130
133
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' ,
131
140
}
132
141
}
133
142
} )
134
143
135
144
const addExpectedPayloadWithAttributes = {
136
- ...addExpectedPayload ,
145
+ ...upsertAddExpectedPayload ,
137
146
contacts : [
138
147
{
139
148
@@ -151,7 +160,7 @@ describe('SendgridAudiences.syncAudience', () => {
151
160
custom_fields : {
152
161
custom_field_1 : 'custom_field_1_value' ,
153
162
custom_field_2 : 2345 ,
154
- custom_field_3 : '2024-01-01T00:00:00.000Z'
163
+ custom_field_3 : "01/01/2024"
155
164
}
156
165
}
157
166
]
@@ -179,7 +188,7 @@ describe('SendgridAudiences.syncAudience', () => {
179
188
addPayloadEmailOnly . anonymousId = undefined
180
189
181
190
const addExpectedPayloadEmailOnly = {
182
- ...addExpectedPayload ,
191
+ ...upsertAddExpectedPayload ,
183
192
contacts : [
184
193
{
185
194
@@ -203,7 +212,7 @@ describe('SendgridAudiences.syncAudience', () => {
203
212
it ( 'should upsert multiple Contacts and add them to a Sendgrid list correctly' , async ( ) => {
204
213
const events = [ createTestEvent ( addPayload ) , createTestEvent ( addPayload2 ) ]
205
214
206
- const addBatchExpectedPayload = {
215
+ const upsertAddBatchExpectedPayload = {
207
216
list_ids : [ 'sg_audience_id_12345' ] ,
208
217
contacts : [
209
218
{
@@ -221,7 +230,7 @@ describe('SendgridAudiences.syncAudience', () => {
221
230
]
222
231
}
223
232
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 , { } )
225
234
const responses = await testDestination . testBatchAction ( 'syncAudience' , {
226
235
events,
227
236
settings,
@@ -235,6 +244,18 @@ describe('SendgridAudiences.syncAudience', () => {
235
244
it ( 'should remove a single Contact from a Sendgrid list correctly' , async ( ) => {
236
245
const event = createTestEvent ( removePayload )
237
246
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
+
238
259
const deletePath = `/v3/marketing/lists/sg_audience_id_12345/contacts?contact_ids=contact_1_id`
239
260
240
261
const searchQuery =
@@ -244,6 +265,8 @@ describe('SendgridAudiences.syncAudience', () => {
244
265
result : [ { id : 'contact_1_id' } ]
245
266
}
246
267
268
+ nock ( 'https://api.sendgrid.com' ) . put ( '/v3/marketing/contacts' , upsertNoAddExpectedPayload ) . reply ( 200 , { } )
269
+
247
270
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery ) . reply ( 200 , searchResponse )
248
271
249
272
nock ( 'https://api.sendgrid.com' ) . delete ( deletePath ) . reply ( 200 , { } )
@@ -255,14 +278,33 @@ describe('SendgridAudiences.syncAudience', () => {
255
278
mapping
256
279
} )
257
280
258
- expect ( responses . length ) . toBe ( 2 )
281
+ expect ( responses . length ) . toBe ( 3 )
259
282
expect ( responses [ 0 ] . status ) . toBe ( 200 )
260
283
expect ( responses [ 1 ] . status ) . toBe ( 200 )
284
+ expect ( responses [ 2 ] . status ) . toBe ( 200 )
261
285
} )
262
286
263
287
it ( 'should remove multiple Contacts from a Sendgrid list correctly' , async ( ) => {
264
288
const events = [ createTestEvent ( removePayload ) , createTestEvent ( removePayload2 ) ]
265
289
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
+
266
308
const deletePath = `/v3/marketing/lists/sg_audience_id_12345/contacts?contact_ids=contact_1_id,contact_2_id`
267
309
268
310
const searchQuery =
@@ -272,6 +314,8 @@ describe('SendgridAudiences.syncAudience', () => {
272
314
result : [ { id : 'contact_1_id' } , { id : 'contact_2_id' } ]
273
315
}
274
316
317
+ nock ( 'https://api.sendgrid.com' ) . put ( '/v3/marketing/contacts' , upsertNoAddBatchExpectedPayload ) . reply ( 200 , { } )
318
+
275
319
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery ) . reply ( 200 , searchResponse )
276
320
277
321
nock ( 'https://api.sendgrid.com' ) . delete ( deletePath ) . reply ( 200 , { } )
@@ -283,9 +327,10 @@ describe('SendgridAudiences.syncAudience', () => {
283
327
mapping
284
328
} )
285
329
286
- expect ( responses . length ) . toBe ( 2 )
330
+ expect ( responses . length ) . toBe ( 3 )
287
331
expect ( responses [ 0 ] . status ) . toBe ( 200 )
288
332
expect ( responses [ 1 ] . status ) . toBe ( 200 )
333
+ expect ( responses [ 2 ] . status ) . toBe ( 200 )
289
334
} )
290
335
291
336
it ( 'should add and remove multiple Contacts from a Sendgrid list correctly' , async ( ) => {
@@ -305,7 +350,7 @@ describe('SendgridAudiences.syncAudience', () => {
305
350
result : [ { id : 'contact_1_id' } , { id : 'contact_2_id' } ]
306
351
}
307
352
308
- const addBatchExpectedPayload = {
353
+ const upsertAddBatchExpectedPayload = {
309
354
list_ids : [ 'sg_audience_id_12345' ] ,
310
355
contacts : [
311
356
{
@@ -323,7 +368,27 @@ describe('SendgridAudiences.syncAudience', () => {
323
368
]
324
369
}
325
370
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 , { } )
327
392
328
393
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery ) . reply ( 200 , searchResponse )
329
394
@@ -336,10 +401,11 @@ describe('SendgridAudiences.syncAudience', () => {
336
401
mapping
337
402
} )
338
403
339
- expect ( responses . length ) . toBe ( 3 )
404
+ expect ( responses . length ) . toBe ( 4 )
340
405
expect ( responses [ 0 ] . status ) . toBe ( 200 )
341
406
expect ( responses [ 1 ] . status ) . toBe ( 200 )
342
407
expect ( responses [ 2 ] . status ) . toBe ( 200 )
408
+ expect ( responses [ 3 ] . status ) . toBe ( 200 )
343
409
} )
344
410
345
411
it ( 'should retry upserting to add Contacts after Sendgrid initially rejects some emails' , async ( ) => {
@@ -351,7 +417,7 @@ describe('SendgridAudiences.syncAudience', () => {
351
417
352
418
const events = [ createTestEvent ( addPayload ) , createTestEvent ( badPayload ) ]
353
419
354
- const addBatchExpectedPayload = {
420
+ const upsertAddBatchExpectedPayload = {
355
421
list_ids : [ 'sg_audience_id_12345' ] ,
356
422
contacts : [
357
423
{
@@ -388,7 +454,7 @@ describe('SendgridAudiences.syncAudience', () => {
388
454
]
389
455
}
390
456
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 )
392
458
nock ( 'https://api.sendgrid.com' ) . put ( '/v3/marketing/contacts' , addBatchExpectedPayload2 ) . reply ( 200 , { } )
393
459
394
460
const responses = await testDestination . testBatchAction ( 'syncAudience' , {
@@ -419,7 +485,7 @@ describe('SendgridAudiences.syncAudience', () => {
419
485
mapping
420
486
} )
421
487
) . 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 ` )
423
489
)
424
490
} )
425
491
@@ -456,6 +522,18 @@ describe('SendgridAudiences.syncAudience', () => {
456
522
anonymousId : `some_anonymous_id_${ i + 1 } `
457
523
} ) )
458
524
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
+
459
537
const events = payloads . map ( createTestEvent )
460
538
461
539
const searchQuery =
@@ -471,6 +549,8 @@ describe('SendgridAudiences.syncAudience', () => {
471
549
472
550
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`
473
551
552
+ nock ( 'https://api.sendgrid.com' ) . put ( '/v3/marketing/contacts' , upsertNoAddBatchExpectedPayload ) . reply ( 200 , { } )
553
+
474
554
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery ) . reply ( 200 , searchResponse )
475
555
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery2 ) . reply ( 200 , searchResponse2 )
476
556
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery3 ) . reply ( 200 , searchResponse3 )
@@ -484,11 +564,12 @@ describe('SendgridAudiences.syncAudience', () => {
484
564
mapping
485
565
} )
486
566
487
- expect ( responses . length ) . toBe ( 4 )
567
+ expect ( responses . length ) . toBe ( 5 )
488
568
expect ( responses [ 0 ] . status ) . toBe ( 200 )
489
569
expect ( responses [ 1 ] . status ) . toBe ( 200 )
490
570
expect ( responses [ 2 ] . status ) . toBe ( 200 )
491
571
expect ( responses [ 3 ] . status ) . toBe ( 200 )
572
+ expect ( responses [ 4 ] . status ) . toBe ( 200 )
492
573
} )
493
574
494
575
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', () => {
503
584
anonymousId : null
504
585
} ) )
505
586
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
+
506
596
const events = payloads . map ( createTestEvent )
507
597
508
598
const searchQuery =
@@ -525,6 +615,8 @@ describe('SendgridAudiences.syncAudience', () => {
525
615
( _ , i ) => `contact_${ i + 101 } _id`
526
616
) . join ( ',' ) } `
527
617
618
+ nock ( 'https://api.sendgrid.com' ) . put ( '/v3/marketing/contacts' , upsertNoAddBatchExpectedPayload ) . reply ( 200 , { } )
619
+
528
620
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery ) . reply ( 200 , searchResponse )
529
621
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery2 ) . reply ( 200 , searchResponse2 )
530
622
nock ( 'https://api.sendgrid.com' ) . post ( '/v3/marketing/contacts/search' , searchQuery3 ) . reply ( 200 , searchResponse3 )
@@ -539,12 +631,13 @@ describe('SendgridAudiences.syncAudience', () => {
539
631
mapping
540
632
} )
541
633
542
- expect ( responses . length ) . toBe ( 5 )
634
+ expect ( responses . length ) . toBe ( 6 )
543
635
expect ( responses [ 0 ] . status ) . toBe ( 200 )
544
636
expect ( responses [ 1 ] . status ) . toBe ( 200 )
545
637
expect ( responses [ 2 ] . status ) . toBe ( 200 )
546
638
expect ( responses [ 3 ] . status ) . toBe ( 200 )
547
639
expect ( responses [ 4 ] . status ) . toBe ( 200 )
640
+ expect ( responses [ 5 ] . status ) . toBe ( 200 )
548
641
} )
549
642
550
643
it ( 'phone number should be E.164' , async ( ) => {
@@ -556,4 +649,25 @@ describe('SendgridAudiences.syncAudience', () => {
556
649
expect ( validatePhone ( badPhone ) ) . toBe ( false )
557
650
expect ( validatePhone ( badPhone2 ) ) . toBe ( false )
558
651
} )
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
+
559
673
} )
0 commit comments