@@ -6,24 +6,16 @@ import * as path from 'path'
6
6
import { autoPrompt } from '../lib/prompt'
7
7
import { loadDestination } from '../lib/destinations'
8
8
import { DestinationDefinition } from '../lib/destinations'
9
- import { BrowserDestinationDefinition } from '@segment/destinations-manifest '
9
+ import { InputField , BaseActionDefinition } from '@segment/actions-core '
10
10
import {
11
- GlobalSetting ,
12
- isDirective ,
13
- DestinationDefinition as CloudModeDestinationDefinition ,
14
- InputField ,
15
- AudienceDestinationDefinition ,
16
- BaseActionDefinition
17
- } from '@segment/actions-core'
18
- import Chance from 'chance'
19
- import { getRawKeys } from '@segment/actions-core/mapping-kit/value-keys'
20
- import { get , set } from 'lodash'
21
- import { ErrorCondition , GroupCondition , parseFql } from '@segment/destination-subscriptions'
22
- import { reconstructSegmentEvent } from '../lib/event-generator'
11
+ generateSamplePayloadFromMapping ,
12
+ generateDestinationSettings ,
13
+ generateAudienceSettings ,
14
+ addAudienceSettingsToPayload
15
+ } from '../lib/payload-generator/payload'
23
16
24
17
export default class GenerateTestPayload extends Command {
25
18
private spinner : ora . Ora = ora ( )
26
- private chance : Chance . Chance = new Chance ( 'Payload' )
27
19
28
20
static description = `Generates sample test payload curl commands for a cloud mode destination.`
29
21
@@ -157,28 +149,11 @@ export default class GenerateTestPayload extends Command {
157
149
this . spinner . start ( `Generating test payload for action: ${ actionSlug } ` )
158
150
159
151
try {
160
- let settings : unknown
161
- let auth : unknown
162
- if ( ( destination as BrowserDestinationDefinition ) . mode == 'device' ) {
163
- // Generate sample settings based on destination settings schema
164
- const destinationSettings = ( destination as BrowserDestinationDefinition ) . settings
165
- settings = this . generateSampleFromSchema ( destinationSettings || { } )
166
- } else if ( ( destination as CloudModeDestinationDefinition ) . mode == 'cloud' ) {
167
- const destinationSettings = ( destination as CloudModeDestinationDefinition ) . authentication ?. fields
168
- settings = this . generateSampleFromSchema ( destinationSettings || { } )
169
- if ( ( destination as CloudModeDestinationDefinition ) . authentication ?. scheme === 'oauth2' ) {
170
- auth = {
171
- oauth : {
172
- accessToken : 'YOUR_ACCESS_TOKEN' ,
173
- refreshToken : 'YOUR_REFRESH_TOKEN'
174
- }
175
- }
176
- }
177
- }
152
+ // Generate destination settings and auth
153
+ const { settings, auth } = generateDestinationSettings ( destination )
178
154
179
- const audienceSettings = {
180
- ...( destination as AudienceDestinationDefinition ) ?. audienceFields
181
- }
155
+ // Get audience settings
156
+ const audienceSettings = generateAudienceSettings ( destination )
182
157
183
158
// Generate sample mapping based on action fields
184
159
const mapping = { } as Record < string , any >
@@ -197,30 +172,15 @@ export default class GenerateTestPayload extends Command {
197
172
const defaultSubscription = ( action as BaseActionDefinition ) . defaultSubscription
198
173
199
174
// Generate sample payload based on the fields.
200
- const payload = this . generateSamplePayloadFromMapping ( mapping , fields , defaultSubscription )
175
+ let payload = generateSamplePayloadFromMapping ( mapping , fields , defaultSubscription )
201
176
202
- // if audience settings exist, add them to the payload
177
+ // Add audience settings to payload if they exist
203
178
if ( Object . keys ( audienceSettings ) . length > 0 ) {
204
- set ( payload , 'context.personas.audience_settings' , this . generateSampleFromSchema ( audienceSettings || { } ) )
179
+ payload = addAudienceSettingsToPayload ( payload , destination )
205
180
}
206
181
207
182
// Generate final sample request
208
- const sampleRequest = {
209
- settings,
210
- mapping,
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
- }
223
- }
183
+ const sampleRequest = this . generateSampleRequest ( settings , mapping , payload , auth )
224
184
225
185
this . spinner . succeed ( `Generated test payload for action: ${ actionSlug } ` )
226
186
@@ -234,249 +194,31 @@ export default class GenerateTestPayload extends Command {
234
194
}
235
195
}
236
196
237
- generateSampleFromSchema ( schema : Record < string , GlobalSetting > ) : Record < string , any > {
238
- const result : Record < string , any > = { }
239
- for ( const [ propName , setting ] of Object . entries ( schema ) ) {
240
- if ( setting . default !== undefined ) {
241
- result [ propName ] = setting . default
242
- } else {
243
- result [ propName ] = this . generatePlaceholderForSchema ( setting )
244
- }
245
- }
246
-
247
- return result
248
- }
249
-
250
- generatePlaceholderForSchema ( schema : GlobalSetting ) : any {
251
- const type = schema . type
252
-
253
- switch ( type ) {
254
- case 'string' :
255
- if ( schema . choices ) {
256
- return schema . choices [ 0 ]
257
- }
258
- return `<${ schema . label || 'VALUE' } >`
259
- case 'number' :
260
- return 0
261
- case 'boolean' :
262
- return false
263
- case 'password' :
264
- return `<${ schema . label || 'PASSWORD' } >`
265
- default :
266
- return null
267
- }
268
- }
269
-
270
- generateSamplePayloadFromMapping (
197
+ /**
198
+ * Generates a complete test request for a destination action.
199
+ */
200
+ generateSampleRequest (
201
+ settings : unknown ,
271
202
mapping : Record < string , any > ,
272
- fields : Record < string , InputField > ,
273
- defaultSubscription ?: string
203
+ payload : Record < string , any > ,
204
+ auth ?: unknown
274
205
) : Record < string , any > {
275
- const chance = new Chance ( 'payload' )
276
-
277
- const payload : Record < string , any > = {
278
- userId : chance . guid ( ) ,
279
- anonymousId : chance . guid ( ) ,
280
- event : 'Example Event' ,
281
- timestamp : new Date ( ) . toISOString ( ) ,
282
- context : {
283
- ip : chance . ip ( ) ,
284
- userAgent :
285
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36' ,
286
- page : {
287
- path : `/${ chance . word ( ) } ` ,
288
- url : chance . url ( ) ,
289
- referrer : chance . url ( ) ,
290
- title : `${ chance . capitalize ( chance . word ( ) ) } ${ chance . capitalize ( chance . word ( ) ) } `
291
- } ,
292
- locale : chance . locale ( ) ,
293
- library : {
294
- name : 'analytics.js' ,
295
- version : `${ chance . integer ( { min : 1 , max : 5 } ) } .${ chance . integer ( { min : 0 , max : 20 } ) } .${ chance . integer ( {
296
- min : 0 ,
297
- max : 99
298
- } ) } `
299
- }
206
+ return {
207
+ settings,
208
+ mapping,
209
+ payload,
210
+ auth,
211
+ features : {
212
+ feature1 : true ,
213
+ feature2 : false
214
+ } ,
215
+ subscriptionMetadata : {
216
+ actionConfigId : 'YOUR_ACTION_CONFIG_ID' ,
217
+ destinationConfigId : 'YOUR_DESTINATION_CONFIG_ID' ,
218
+ actionId : 'YOUR_ACTION_ID' ,
219
+ sourceId : 'YOUR_SOURCE_ID'
300
220
}
301
221
}
302
-
303
- // Add properties based on mapping with better values
304
- for ( const [ key , value ] of Object . entries ( mapping ) ) {
305
- if ( isDirective ( value ) ) {
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 )
326
- }
327
- }
328
-
329
- return payload
330
- }
331
-
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 ( )
418
-
419
- // Check for common field name patterns
420
- if ( lowerFieldName . includes ( 'email' ) ) {
421
- return this . chance . email ( )
422
- } else if ( lowerFieldName . includes ( 'phone' ) || lowerFieldName . includes ( 'mobile' ) ) {
423
- return `+${ this . chance . phone ( { formatted : false } ) } `
424
- } else if ( lowerFieldName . includes ( 'name' ) ) {
425
- if ( lowerFieldName . includes ( 'first' ) ) {
426
- return this . chance . first ( )
427
- } else if ( lowerFieldName . includes ( 'last' ) ) {
428
- return this . chance . last ( )
429
- } else if ( lowerFieldName . includes ( 'full' ) ) {
430
- return this . chance . name ( )
431
- } else {
432
- return this . chance . name ( )
433
- }
434
- } else if ( lowerFieldName . includes ( 'url' ) || lowerFieldName . includes ( 'link' ) ) {
435
- return this . chance . url ( )
436
- } else if ( lowerFieldName . includes ( 'date' ) ) {
437
- return this . chance . date ( ) . toISOString ( )
438
- } else if ( lowerFieldName . includes ( 'time' ) ) {
439
- return this . chance . date ( ) . toISOString ( )
440
- } else if (
441
- lowerFieldName . includes ( 'price' ) ||
442
- lowerFieldName . includes ( 'amount' ) ||
443
- lowerFieldName . includes ( 'total' )
444
- ) {
445
- return this . chance . floating ( { min : 1 , max : 1000 , fixed : 2 } )
446
- } else if ( lowerFieldName . includes ( 'currency' ) ) {
447
- return this . chance . currency ( ) . code
448
- } else if ( lowerFieldName . includes ( 'country' ) ) {
449
- return this . chance . country ( )
450
- } else if ( lowerFieldName . includes ( 'city' ) ) {
451
- return this . chance . city ( )
452
- } else if ( lowerFieldName . includes ( 'state' ) || lowerFieldName . includes ( 'province' ) ) {
453
- return this . chance . state ( )
454
- } else if ( lowerFieldName . includes ( 'zip' ) || lowerFieldName . includes ( 'postal' ) ) {
455
- return this . chance . zip ( )
456
- } else if ( lowerFieldName . includes ( 'address' ) ) {
457
- return this . chance . address ( )
458
- } else if ( lowerFieldName . includes ( 'company' ) || lowerFieldName . includes ( 'organization' ) ) {
459
- return this . chance . company ( )
460
- } else if ( lowerFieldName . includes ( 'description' ) ) {
461
- return this . chance . paragraph ( )
462
- } else if ( lowerFieldName . includes ( 'id' ) ) {
463
- return this . chance . guid ( )
464
- } else if ( lowerFieldName . includes ( 'quantity' ) || lowerFieldName . includes ( 'count' ) ) {
465
- return this . chance . integer ( { min : 1 , max : 10 } )
466
- } else if ( lowerFieldName . includes ( 'age' ) ) {
467
- return this . chance . age ( )
468
- } else if ( lowerFieldName === 'gender' ) {
469
- return this . chance . gender ( )
470
- } else if (
471
- lowerFieldName . includes ( 'boolean' ) ||
472
- lowerFieldName . includes ( 'enabled' ) ||
473
- lowerFieldName . includes ( 'active' )
474
- ) {
475
- return this . chance . bool ( )
476
- } else {
477
- // Default fallback
478
- return this . chance . word ( )
479
- }
480
222
}
481
223
482
224
async catch ( error : unknown ) {
0 commit comments