@@ -5,12 +5,20 @@ import ora from 'ora'
5
5
import * as path from 'path'
6
6
import { autoPrompt } from '../lib/prompt'
7
7
import { loadDestination } from '../lib/destinations'
8
- import type { JSONSchema7 } from 'json-schema'
9
8
import { DestinationDefinition } from '../lib/destinations'
10
9
import { BrowserDestinationDefinition } from '@segment/destinations-manifest'
11
- import type { DestinationDefinition as CloudModeDestinationDefinition , InputField } from '@segment/actions-core'
10
+ import {
11
+ GlobalSetting ,
12
+ isDirective ,
13
+ DestinationDefinition as CloudModeDestinationDefinition ,
14
+ InputField
15
+ } from '@segment/actions-core'
16
+ import Chance from 'chance'
17
+ import { getRawKeys } from '@segment/actions-core/mapping-kit/value-keys'
18
+ import { set } from 'lodash'
12
19
export default class GenerateTestPayload extends Command {
13
20
private spinner : ora . Ora = ora ( )
21
+ private chance : Chance . Chance = new Chance ( 'Payload' )
14
22
15
23
static description = `Generates sample test payload curl commands for a cloud mode destination.`
16
24
@@ -152,6 +160,14 @@ export default class GenerateTestPayload extends Command {
152
160
} else if ( ( destination as CloudModeDestinationDefinition ) . mode == 'cloud' ) {
153
161
const destinationSettings = ( destination as CloudModeDestinationDefinition ) . authentication ?. fields
154
162
settings = this . generateSampleFromSchema ( destinationSettings || { } )
163
+ if ( ( destination as CloudModeDestinationDefinition ) . authentication ?. scheme === 'oauth2' ) {
164
+ settings = {
165
+ oauth : {
166
+ accessToken : 'YOUR_ACCESS_TOKEN' ,
167
+ refreshToken : 'YOUR_REFRESH_TOKEN'
168
+ }
169
+ }
170
+ }
155
171
}
156
172
157
173
// Generate sample mapping based on action fields
@@ -161,8 +177,6 @@ export default class GenerateTestPayload extends Command {
161
177
for ( const [ fieldKey , field ] of Object . entries ( fields ) ) {
162
178
if ( field . default ) {
163
179
mapping [ fieldKey ] = field . default
164
- } else if ( field . required ) {
165
- mapping [ fieldKey ] = this . generatePlaceholderValue ( field )
166
180
}
167
181
}
168
182
@@ -188,133 +202,145 @@ export default class GenerateTestPayload extends Command {
188
202
}
189
203
}
190
204
191
- generateSampleFromSchema ( schema : JSONSchema7 ) : Record < string , any > {
205
+ generateSampleFromSchema ( schema : Record < string , GlobalSetting > ) : Record < string , any > {
192
206
const result : Record < string , any > = { }
193
-
194
- if ( ! schema . properties ) {
195
- return result
196
- }
197
-
198
- for ( const [ propName , propSchema ] of Object . entries ( schema . properties ) ) {
199
- const prop = propSchema as JSONSchema7
200
-
201
- if ( prop . default !== undefined ) {
202
- result [ propName ] = prop . default
203
- } else if ( ( schema . required || [ ] ) . includes ( propName ) ) {
204
- result [ propName ] = this . generatePlaceholderForSchema ( prop )
207
+ for ( const [ propName , setting ] of Object . entries ( schema ) ) {
208
+ if ( setting . default !== undefined ) {
209
+ result [ propName ] = setting . default
210
+ } else if ( setting . required ) {
211
+ result [ propName ] = this . generatePlaceholderForSchema ( setting )
205
212
}
206
213
}
207
214
208
215
return result
209
216
}
210
217
211
- generatePlaceholderForSchema ( schema : JSONSchema7 ) : any {
212
- const type = Array . isArray ( schema . type ) ? schema . type [ 0 ] : schema . type
218
+ generatePlaceholderForSchema ( schema : GlobalSetting ) : any {
219
+ const type = schema . type
213
220
214
221
switch ( type ) {
215
222
case 'string' :
216
- if ( schema . enum && schema . enum . length > 0 ) {
217
- return schema . enum [ 0 ]
223
+ if ( schema . choices ) {
224
+ return schema . choices [ 0 ]
218
225
}
219
- return `YOUR_${ schema . title || 'VALUE' } `
226
+ return `YOUR_${ schema . label || 'VALUE' } `
220
227
case 'number' :
221
- case 'integer' :
222
228
return 0
223
229
case 'boolean' :
224
230
return false
225
- case 'object' :
226
- return this . generateSampleFromSchema ( schema )
227
- case 'array' :
228
- if ( schema . items && ! Array . isArray ( schema . items ) ) {
229
- return [ this . generatePlaceholderForSchema ( schema . items as JSONSchema7 ) ]
230
- }
231
- return [ ]
231
+ case 'password' :
232
+ return `YOUR_${ schema . label || 'PASSWORD' } `
232
233
default :
233
234
return null
234
235
}
235
236
}
236
237
237
- generatePlaceholderValue ( field : any ) : any {
238
- if ( field . type === 'boolean' ) {
239
- return false
240
- } else if ( field . type === 'number' ) {
241
- return 0
242
- } else if ( field . type === 'object' ) {
243
- return { }
244
- } else if ( field . type === 'array' ) {
245
- return [ ]
246
- }
247
-
248
- // For string type
249
- return `YOUR_${ field . label || field . title || 'VALUE' } `
250
- }
251
-
252
238
generateSamplePayloadFromMapping ( mapping : Record < string , any > ) : Record < string , any > {
239
+ const chance = new Chance ( 'payload' )
253
240
const payload : Record < string , any > = {
254
- userId : 'user123' ,
255
- anonymousId : 'anon456' ,
241
+ userId : chance . guid ( ) ,
242
+ anonymousId : chance . guid ( ) ,
256
243
event : 'Example Event' ,
257
244
type : 'track' ,
258
245
timestamp : new Date ( ) . toISOString ( ) ,
259
246
properties : { } ,
260
247
context : {
261
- ip : '127.0.0.1' ,
262
- userAgent : 'Mozilla/5.0' ,
248
+ ip : chance . ip ( ) ,
249
+ userAgent :
250
+ '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' ,
263
251
page : {
264
- path : '/' ,
265
- url : 'https://example.com/' ,
266
- referrer : '' ,
267
- title : 'Example Page'
252
+ path : `/${ chance . word ( ) } ` ,
253
+ url : chance . url ( ) ,
254
+ referrer : chance . url ( ) ,
255
+ title : `${ chance . capitalize ( chance . word ( ) ) } ${ chance . capitalize ( chance . word ( ) ) } `
256
+ } ,
257
+ locale : chance . locale ( ) ,
258
+ library : {
259
+ name : 'analytics.js' ,
260
+ version : `${ chance . integer ( { min : 1 , max : 5 } ) } .${ chance . integer ( { min : 0 , max : 20 } ) } .${ chance . integer ( {
261
+ min : 0 ,
262
+ max : 99
263
+ } ) } `
268
264
}
269
265
}
270
266
}
271
267
272
- // Add properties based on mapping
273
- for ( const [ key , value ] of Object . entries ( mapping ) ) {
274
- payload . properties [ key ] = value
268
+ // Add properties based on mapping with better values
269
+ for ( const [ _ , value ] of Object . entries ( mapping ) ) {
270
+ if ( isDirective ( value ) ) {
271
+ const [ key ] = getRawKeys ( value )
272
+ const path = key . replace ( '$.' , '' )
273
+ set ( payload , path , this . generateValueByFieldName ( path ) )
274
+ }
275
275
}
276
276
277
- // Add properties based on mapping
278
- for ( const [ key , value ] of Object . entries ( mapping ) ) {
279
- if ( typeof value === 'string' && value . startsWith ( '$.' ) ) {
280
- // Handle field value from mapping path
281
- const path = value . substring ( 2 ) . split ( '.' )
282
- if ( path [ 0 ] === 'properties' ) {
283
- const propName = path . slice ( 1 ) . join ( '.' )
284
- payload . properties [ propName ] = `SAMPLE_${ key . toUpperCase ( ) } `
285
- } else if ( path [ 0 ] === 'context' ) {
286
- const nestedPath = path . slice ( 1 )
287
- let current = payload . context
288
-
289
- for ( let i = 0 ; i < nestedPath . length - 1 ; i ++ ) {
290
- if ( ! current [ nestedPath [ i ] ] ) {
291
- current [ nestedPath [ i ] ] = { }
292
- }
293
- current = current [ nestedPath [ i ] ]
294
- }
277
+ return payload
278
+ }
295
279
296
- if ( nestedPath . length > 0 ) {
297
- current [ nestedPath [ nestedPath . length - 1 ] ] = `SAMPLE_${ key . toUpperCase ( ) } `
298
- }
299
- } else if ( path [ 0 ] === 'traits' ) {
300
- if ( ! payload . traits ) {
301
- payload . traits = { }
302
- }
303
- const propName = path . slice ( 1 ) . join ( '.' )
304
- payload . traits [ propName ] = `SAMPLE_${ key . toUpperCase ( ) } `
305
- } else if ( path [ 0 ] === 'userId' || path [ 0 ] === 'anonymousId' || path [ 0 ] === 'event' ) {
306
- // These are already set
307
- } else {
308
- // Handle top-level fields
309
- payload [ path [ 0 ] ] = `SAMPLE_${ key . toUpperCase ( ) } `
310
- }
311
- } else if ( typeof value === 'string' ) {
312
- // Handle literal values from mappings
313
- payload . properties [ key ] = value
280
+ generateValueByFieldName ( fieldName : string ) : any {
281
+ const lowerFieldName = fieldName . toLowerCase ( )
282
+
283
+ // Check for common field name patterns
284
+ if ( lowerFieldName . includes ( 'email' ) ) {
285
+ return this . chance . email ( )
286
+ } else if ( lowerFieldName . includes ( 'phone' ) || lowerFieldName . includes ( 'mobile' ) ) {
287
+ return `+${ this . chance . phone ( { formatted : false } ) } `
288
+ } else if ( lowerFieldName . includes ( 'name' ) ) {
289
+ if ( lowerFieldName . includes ( 'first' ) ) {
290
+ return this . chance . first ( )
291
+ } else if ( lowerFieldName . includes ( 'last' ) ) {
292
+ return this . chance . last ( )
293
+ } else if ( lowerFieldName . includes ( 'full' ) ) {
294
+ return this . chance . name ( )
295
+ } else {
296
+ return this . chance . name ( )
314
297
}
298
+ } else if ( lowerFieldName . includes ( 'url' ) || lowerFieldName . includes ( 'link' ) ) {
299
+ return this . chance . url ( )
300
+ } else if ( lowerFieldName . includes ( 'date' ) ) {
301
+ return this . chance . date ( ) . toISOString ( )
302
+ } else if ( lowerFieldName . includes ( 'time' ) ) {
303
+ return this . chance . date ( ) . toISOString ( )
304
+ } else if (
305
+ lowerFieldName . includes ( 'price' ) ||
306
+ lowerFieldName . includes ( 'amount' ) ||
307
+ lowerFieldName . includes ( 'total' )
308
+ ) {
309
+ return this . chance . floating ( { min : 1 , max : 1000 , fixed : 2 } )
310
+ } else if ( lowerFieldName . includes ( 'currency' ) ) {
311
+ return this . chance . currency ( ) . code
312
+ } else if ( lowerFieldName . includes ( 'country' ) ) {
313
+ return this . chance . country ( )
314
+ } else if ( lowerFieldName . includes ( 'city' ) ) {
315
+ return this . chance . city ( )
316
+ } else if ( lowerFieldName . includes ( 'state' ) || lowerFieldName . includes ( 'province' ) ) {
317
+ return this . chance . state ( )
318
+ } else if ( lowerFieldName . includes ( 'zip' ) || lowerFieldName . includes ( 'postal' ) ) {
319
+ return this . chance . zip ( )
320
+ } else if ( lowerFieldName . includes ( 'address' ) ) {
321
+ return this . chance . address ( )
322
+ } else if ( lowerFieldName . includes ( 'company' ) || lowerFieldName . includes ( 'organization' ) ) {
323
+ return this . chance . company ( )
324
+ } else if ( lowerFieldName . includes ( 'description' ) ) {
325
+ return this . chance . paragraph ( )
326
+ } else if ( lowerFieldName . includes ( 'id' ) ) {
327
+ return this . chance . guid ( )
328
+ } else if ( lowerFieldName . includes ( 'quantity' ) || lowerFieldName . includes ( 'count' ) ) {
329
+ return this . chance . integer ( { min : 1 , max : 10 } )
330
+ } else if ( lowerFieldName . includes ( 'age' ) ) {
331
+ return this . chance . age ( )
332
+ } else if ( lowerFieldName === 'gender' ) {
333
+ return this . chance . gender ( )
334
+ } else if (
335
+ lowerFieldName . includes ( 'boolean' ) ||
336
+ lowerFieldName . includes ( 'enabled' ) ||
337
+ lowerFieldName . includes ( 'active' )
338
+ ) {
339
+ return this . chance . bool ( )
340
+ } else {
341
+ // Default fallback
342
+ return this . chance . word ( )
315
343
}
316
-
317
- return payload
318
344
}
319
345
320
346
async catch ( error : unknown ) {
0 commit comments