@@ -12,11 +12,15 @@ import {
12
12
isDirective ,
13
13
DestinationDefinition as CloudModeDestinationDefinition ,
14
14
InputField ,
15
- AudienceDestinationDefinition
15
+ AudienceDestinationDefinition ,
16
+ BaseActionDefinition
16
17
} from '@segment/actions-core'
17
18
import Chance from 'chance'
18
19
import { getRawKeys } from '@segment/actions-core/mapping-kit/value-keys'
19
- import { set } from 'lodash'
20
+ import { get , set } from 'lodash'
21
+ import { ErrorCondition , GroupCondition , parseFql } from '@segment/destination-subscriptions'
22
+ import { reconstructSegmentEvent } from '../lib/event-generator'
23
+
20
24
export default class GenerateTestPayload extends Command {
21
25
private spinner : ora . Ora = ora ( )
22
26
private chance : Chance . Chance = new Chance ( 'Payload' )
@@ -154,6 +158,7 @@ export default class GenerateTestPayload extends Command {
154
158
155
159
try {
156
160
let settings : unknown
161
+ let auth : unknown
157
162
if ( ( destination as BrowserDestinationDefinition ) . mode == 'device' ) {
158
163
// Generate sample settings based on destination settings schema
159
164
const destinationSettings = ( destination as BrowserDestinationDefinition ) . settings
@@ -162,7 +167,7 @@ export default class GenerateTestPayload extends Command {
162
167
const destinationSettings = ( destination as CloudModeDestinationDefinition ) . authentication ?. fields
163
168
settings = this . generateSampleFromSchema ( destinationSettings || { } )
164
169
if ( ( destination as CloudModeDestinationDefinition ) . authentication ?. scheme === 'oauth2' ) {
165
- settings = {
170
+ auth = {
166
171
oauth : {
167
172
accessToken : 'YOUR_ACCESS_TOKEN' ,
168
173
refreshToken : 'YOUR_REFRESH_TOKEN'
@@ -189,19 +194,32 @@ export default class GenerateTestPayload extends Command {
189
194
}
190
195
}
191
196
192
- // Generate sample payload based on the fields
193
- const payload = this . generateSamplePayloadFromMapping ( mapping )
197
+ const defaultSubscription = ( action as BaseActionDefinition ) . defaultSubscription
198
+
199
+ // Generate sample payload based on the fields.
200
+ const payload = this . generateSamplePayloadFromMapping ( mapping , fields , defaultSubscription )
194
201
195
202
// if audience settings exist, add them to the payload
196
- if ( audienceSettings ) {
203
+ if ( Object . keys ( audienceSettings ) . length > 0 ) {
197
204
set ( payload , 'context.personas.audience_settings' , this . generateSampleFromSchema ( audienceSettings || { } ) )
198
205
}
199
206
200
207
// Generate final sample request
201
208
const sampleRequest = {
202
209
settings,
203
210
mapping,
204
- payload
211
+ payload,
212
+ auth,
213
+ features : {
214
+ feature1 : true ,
215
+ feature2 : false
216
+ } ,
217
+ subscriptionMetadata : {
218
+ actionConfigId : 'YOUR_ACTION_CONFIG_ID' ,
219
+ destinationConfigId : 'YOUR_DESTINATION_CONFIG_ID' ,
220
+ actionId : 'YOUR_ACTION_ID' ,
221
+ sourceId : 'YOUR_SOURCE_ID'
222
+ }
205
223
}
206
224
207
225
this . spinner . succeed ( `Generated test payload for action: ${ actionSlug } ` )
@@ -249,15 +267,18 @@ export default class GenerateTestPayload extends Command {
249
267
}
250
268
}
251
269
252
- generateSamplePayloadFromMapping ( mapping : Record < string , any > ) : Record < string , any > {
270
+ generateSamplePayloadFromMapping (
271
+ mapping : Record < string , any > ,
272
+ fields : Record < string , InputField > ,
273
+ defaultSubscription ?: string
274
+ ) : Record < string , any > {
253
275
const chance = new Chance ( 'payload' )
276
+
254
277
const payload : Record < string , any > = {
255
278
userId : chance . guid ( ) ,
256
279
anonymousId : chance . guid ( ) ,
257
280
event : 'Example Event' ,
258
- type : 'track' ,
259
281
timestamp : new Date ( ) . toISOString ( ) ,
260
- properties : { } ,
261
282
context : {
262
283
ip : chance . ip ( ) ,
263
284
userAgent :
@@ -280,19 +301,120 @@ export default class GenerateTestPayload extends Command {
280
301
}
281
302
282
303
// Add properties based on mapping with better values
283
- for ( const [ _ , value ] of Object . entries ( mapping ) ) {
304
+ for ( const [ key , value ] of Object . entries ( mapping ) ) {
284
305
if ( isDirective ( value ) ) {
285
- const [ key ] = getRawKeys ( value )
286
- const path = key . replace ( '$.' , '' )
287
- set ( payload , path , this . generateValueByFieldName ( path ) )
306
+ const [ pathKey ] = getRawKeys ( value )
307
+ const path = pathKey . replace ( '$.' , '' )
308
+ const fieldDefinition = fields [ key ]
309
+ const existingValue = get ( payload , path )
310
+ const newValue = this . setTestData ( fieldDefinition , key )
311
+ if ( typeof existingValue === 'object' && existingValue !== null && ! Array . isArray ( existingValue ) ) {
312
+ set ( payload , path , { ...existingValue , ...newValue } )
313
+ } else {
314
+ set ( payload , path , newValue )
315
+ }
316
+ }
317
+ }
318
+
319
+ if ( defaultSubscription ) {
320
+ const parsed = parseFql ( defaultSubscription )
321
+ if ( ( parsed as ErrorCondition ) . error ) {
322
+ this . log ( chalk . red ( `Failed to parse FQL: ${ ( parsed as ErrorCondition ) . error } ` ) )
323
+ } else {
324
+ const groupCondition = parsed as GroupCondition
325
+ return reconstructSegmentEvent ( groupCondition . children , payload )
288
326
}
289
327
}
290
328
291
329
return payload
292
330
}
293
331
294
- generateValueByFieldName ( fieldName : string ) : any {
295
- const lowerFieldName = fieldName . toLowerCase ( )
332
+ setTestData ( fieldDefinition : Omit < InputField , 'Description' > , fieldName : string ) {
333
+ const chance = this . chance
334
+ const { type, format, choices, multiple } = fieldDefinition
335
+
336
+ if ( Array . isArray ( choices ) ) {
337
+ if ( typeof choices [ 0 ] === 'object' && 'value' in choices [ 0 ] ) {
338
+ return choices [ 0 ] . value
339
+ }
340
+
341
+ return choices [ 0 ]
342
+ }
343
+ let val : any
344
+ switch ( type ) {
345
+ case 'boolean' :
346
+ val = chance . bool ( )
347
+ break
348
+ case 'datetime' :
349
+ val = '2021-02-01T00:00:00.000Z'
350
+ break
351
+ case 'integer' :
352
+ val = chance . integer ( )
353
+ break
354
+ case 'number' :
355
+ val = chance . floating ( { fixed : 2 } )
356
+ break
357
+ case 'text' :
358
+ val = chance . sentence ( )
359
+ break
360
+ case 'object' :
361
+ if ( fieldDefinition . properties ) {
362
+ val = { }
363
+ for ( const [ key , prop ] of Object . entries ( fieldDefinition . properties ) ) {
364
+ val [ key ] = this . setTestData ( prop as Omit < InputField , 'Description' > , key )
365
+ }
366
+ }
367
+ break
368
+ default :
369
+ // covers string
370
+ switch ( format ) {
371
+ case 'date' : {
372
+ const d = chance . date ( )
373
+ val = [ d . getFullYear ( ) , d . getMonth ( ) + 1 , d . getDate ( ) ] . map ( ( v ) => String ( v ) . padStart ( 2 , '0' ) ) . join ( '-' )
374
+ break
375
+ }
376
+ case 'date-time' :
377
+ val = chance . date ( ) . toISOString ( )
378
+ break
379
+ case 'email' :
380
+ val = chance . email ( )
381
+ break
382
+ case 'hostname' :
383
+ val = chance . domain ( )
384
+ break
385
+ case 'ipv4' :
386
+ val = chance . ip ( )
387
+ break
388
+ case 'ipv6' :
389
+ val = chance . ipv6 ( )
390
+ break
391
+ case 'time' : {
392
+ const d = chance . date ( )
393
+ val = [ d . getHours ( ) , d . getMinutes ( ) , d . getSeconds ( ) ] . map ( ( v ) => String ( v ) . padStart ( 2 , '0' ) ) . join ( ':' )
394
+ break
395
+ }
396
+ case 'uri' :
397
+ val = chance . url ( )
398
+ break
399
+ case 'uuid' :
400
+ val = chance . guid ( )
401
+ break
402
+ default :
403
+ val = this . generateValueByFieldName ( fieldName )
404
+ break
405
+ }
406
+ break
407
+ }
408
+
409
+ if ( multiple ) {
410
+ val = [ val ]
411
+ }
412
+
413
+ return val
414
+ }
415
+
416
+ generateValueByFieldName ( fieldKey : string ) : any {
417
+ const lowerFieldName = fieldKey . toLowerCase ( )
296
418
297
419
// Check for common field name patterns
298
420
if ( lowerFieldName . includes ( 'email' ) ) {
0 commit comments