Skip to content

Commit e6aca42

Browse files
update payload generator
1 parent 84e0db1 commit e6aca42

File tree

1 file changed

+120
-94
lines changed

1 file changed

+120
-94
lines changed

packages/cli/src/commands/generate-test-payload.ts

Lines changed: 120 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@ import ora from 'ora'
55
import * as path from 'path'
66
import { autoPrompt } from '../lib/prompt'
77
import { loadDestination } from '../lib/destinations'
8-
import type { JSONSchema7 } from 'json-schema'
98
import { DestinationDefinition } from '../lib/destinations'
109
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'
1219
export default class GenerateTestPayload extends Command {
1320
private spinner: ora.Ora = ora()
21+
private chance: Chance.Chance = new Chance('Payload')
1422

1523
static description = `Generates sample test payload curl commands for a cloud mode destination.`
1624

@@ -152,6 +160,14 @@ export default class GenerateTestPayload extends Command {
152160
} else if ((destination as CloudModeDestinationDefinition).mode == 'cloud') {
153161
const destinationSettings = (destination as CloudModeDestinationDefinition).authentication?.fields
154162
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+
}
155171
}
156172

157173
// Generate sample mapping based on action fields
@@ -161,8 +177,6 @@ export default class GenerateTestPayload extends Command {
161177
for (const [fieldKey, field] of Object.entries(fields)) {
162178
if (field.default) {
163179
mapping[fieldKey] = field.default
164-
} else if (field.required) {
165-
mapping[fieldKey] = this.generatePlaceholderValue(field)
166180
}
167181
}
168182

@@ -188,133 +202,145 @@ export default class GenerateTestPayload extends Command {
188202
}
189203
}
190204

191-
generateSampleFromSchema(schema: JSONSchema7): Record<string, any> {
205+
generateSampleFromSchema(schema: Record<string, GlobalSetting>): Record<string, any> {
192206
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)
205212
}
206213
}
207214

208215
return result
209216
}
210217

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
213220

214221
switch (type) {
215222
case 'string':
216-
if (schema.enum && schema.enum.length > 0) {
217-
return schema.enum[0]
223+
if (schema.choices) {
224+
return schema.choices[0]
218225
}
219-
return `YOUR_${schema.title || 'VALUE'}`
226+
return `YOUR_${schema.label || 'VALUE'}`
220227
case 'number':
221-
case 'integer':
222228
return 0
223229
case 'boolean':
224230
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'}`
232233
default:
233234
return null
234235
}
235236
}
236237

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-
252238
generateSamplePayloadFromMapping(mapping: Record<string, any>): Record<string, any> {
239+
const chance = new Chance('payload')
253240
const payload: Record<string, any> = {
254-
userId: 'user123',
255-
anonymousId: 'anon456',
241+
userId: chance.guid(),
242+
anonymousId: chance.guid(),
256243
event: 'Example Event',
257244
type: 'track',
258245
timestamp: new Date().toISOString(),
259246
properties: {},
260247
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',
263251
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+
})}`
268264
}
269265
}
270266
}
271267

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+
}
275275
}
276276

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+
}
295279

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()
314297
}
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()
315343
}
316-
317-
return payload
318344
}
319345

320346
async catch(error: unknown) {

0 commit comments

Comments
 (0)