@mojaloop/ml-testing-toolkit-shared-lib 14.0.4 → 14.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +0 -3
- package/.ncurc.yaml +1 -6
- package/.nvmrc +1 -1
- package/CHANGELOG.md +7 -0
- package/jest.config.js +4 -4
- package/package.json +10 -11
- package/src/lib/openApiMockGenerator.js +793 -97
package/.husky/pre-commit
CHANGED
package/.ncurc.yaml
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
1
|
## Add a TODO comment indicating the reason for each rejected dependency upgrade added to this list, and what should be done to resolve it (i.e. handle it through a story, etc).
|
|
2
|
-
reject: [
|
|
3
|
-
# Downgraded to 5.5.3 - Reason: removal of functional code in faker.js - https://github.com/advisories/GHSA-5w9c-rv96-fr7g
|
|
4
|
-
"faker",
|
|
5
|
-
# json-schema-faker v0.5.0 - Reason: Introduces some url rendering issues and has huge changes between v0.5.0-rcv.46 and v0.5.0
|
|
6
|
-
"json-schema-faker"
|
|
7
|
-
]
|
|
2
|
+
reject: []
|
package/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
22.15.0
|
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
# Changelog: [mojaloop/als-consent-oracle](https://github.com/mojaloop/als-consent-oracle)
|
|
2
|
+
### [14.0.5](https://github.com/mojaloop/ml-testing-toolkit-shared-lib/compare/v14.0.4...v14.0.5) (2025-05-16)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Chore
|
|
6
|
+
|
|
7
|
+
* update nodeJS to 22.15.0 and migrate from faker lib ([#21](https://github.com/mojaloop/ml-testing-toolkit-shared-lib/issues/21)) ([1eacb45](https://github.com/mojaloop/ml-testing-toolkit-shared-lib/commit/1eacb4575b250c91d0288345c4280fcdd74852eb))
|
|
8
|
+
|
|
2
9
|
### [14.0.4](https://github.com/mojaloop/ml-testing-toolkit-shared-lib/compare/v14.0.3...v14.0.4) (2025-02-19)
|
|
3
10
|
|
|
4
11
|
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/ml-testing-toolkit-shared-lib",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.5",
|
|
4
4
|
"description": "Shared library for ml-testing-toolkit re-usable functions",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"contributors": [
|
|
7
7
|
"Vijaya Kumar <vijaya.guthi@modusbox.com>",
|
|
8
|
-
"Miguel de Barros <miguel.debarros@modusbox.com>"
|
|
8
|
+
"Miguel de Barros <miguel.debarros@modusbox.com>",
|
|
9
|
+
"Juan Correa <code@juancorrea.io>"
|
|
9
10
|
],
|
|
10
|
-
"engines": {
|
|
11
|
-
"node": ">=18.x"
|
|
12
|
-
},
|
|
13
11
|
"license": "Apache-2.0",
|
|
14
12
|
"repository": {
|
|
15
13
|
"type": "git",
|
|
@@ -47,22 +45,22 @@
|
|
|
47
45
|
"snapshot": "npx standard-version --no-verify --skip.changelog --prerelease snapshot --releaseCommitMessageFormat 'chore(snapshot): {{currentTag}}'"
|
|
48
46
|
},
|
|
49
47
|
"dependencies": {
|
|
48
|
+
"@faker-js/faker": "^9.8.0",
|
|
49
|
+
"@stoplight/json-schema-sampler": "0.3.0",
|
|
50
50
|
"ajv": "8.17.1",
|
|
51
|
-
"faker": "5.5.3",
|
|
52
|
-
"json-schema-faker": "0.5.8",
|
|
53
51
|
"json-schema-ref-parser": "9.0.9",
|
|
54
52
|
"lodash": "4.17.21",
|
|
55
53
|
"node-dir": "0.1.17"
|
|
56
54
|
},
|
|
57
55
|
"devDependencies": {
|
|
58
|
-
"@commitlint/cli": "^19.
|
|
59
|
-
"@commitlint/config-conventional": "^19.
|
|
56
|
+
"@commitlint/cli": "^19.8.1",
|
|
57
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
60
58
|
"@types/jest": "29.5.14",
|
|
61
59
|
"audit-ci": "^7.1.0",
|
|
62
60
|
"husky": "^9.1.7",
|
|
63
61
|
"jest": "29.7.0",
|
|
64
62
|
"jest-junit": "16.0.0",
|
|
65
|
-
"npm-check-updates": "
|
|
63
|
+
"npm-check-updates": "18.0.1",
|
|
66
64
|
"pre-commit": "^1.2.2",
|
|
67
65
|
"replace": "^1.2.2",
|
|
68
66
|
"standard": "^17.1.2",
|
|
@@ -71,7 +69,8 @@
|
|
|
71
69
|
"overrides": {
|
|
72
70
|
"cross-spawn": "7.0.6",
|
|
73
71
|
"glob": "9.3.5",
|
|
74
|
-
"rimraf": "5.0.10"
|
|
72
|
+
"rimraf": "5.0.10",
|
|
73
|
+
"@babel/helpers": "7.26.10"
|
|
75
74
|
},
|
|
76
75
|
"standard-version": {
|
|
77
76
|
"scripts": {
|
|
@@ -31,19 +31,204 @@ const _ = require('lodash')
|
|
|
31
31
|
// const fs = require('fs')
|
|
32
32
|
// const jref = require('json-ref-lite')
|
|
33
33
|
// const yaml = require('js-yaml')
|
|
34
|
-
const faker = require('faker')
|
|
35
|
-
const
|
|
34
|
+
const { faker } = require('@faker-js/faker')
|
|
35
|
+
const JSONSchemaSampler = require('@stoplight/json-schema-sampler')
|
|
36
36
|
const $RefParser = require('json-schema-ref-parser')
|
|
37
37
|
const Ajv = require('ajv')
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
// Configure the sampler options to match original jsf behavior
|
|
40
|
+
const samplerOptions = {
|
|
41
|
+
skipNonRequired: false,
|
|
42
|
+
quiet: true,
|
|
43
|
+
maxSampleDepth: 5,
|
|
44
|
+
maxItems: 2,
|
|
43
45
|
ignoreMissingRefs: true,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
formatHandlers: {
|
|
47
|
+
byte: () => Buffer.alloc(faker.lorem.sentence(12)).toString('base64'),
|
|
48
|
+
date: () => faker.date.anytime().toISOString().split('T')[0],
|
|
49
|
+
'date-time': () => faker.date.anytime().toISOString(),
|
|
50
|
+
email: () => faker.internet.email(),
|
|
51
|
+
uri: () => faker.internet.url(),
|
|
52
|
+
uuid: () => faker.string.uuid(),
|
|
53
|
+
phone: () => faker.phone.number('+###########'),
|
|
54
|
+
'geo-latitude': () => {
|
|
55
|
+
const val = faker.location.latitude().toFixed(4)
|
|
56
|
+
return (parseFloat(val) >= 0 ? '+' : '') + val
|
|
57
|
+
},
|
|
58
|
+
'geo-longitude': () => {
|
|
59
|
+
const val = faker.location.longitude().toFixed(4)
|
|
60
|
+
return (parseFloat(val) >= 0 ? '+' : '') + val
|
|
61
|
+
},
|
|
62
|
+
'fsp-id': () => generateFspId(),
|
|
63
|
+
currency: () => faker.helpers.arrayElement(['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'CHF', 'CNY', 'INR', 'BRL']),
|
|
64
|
+
'sub-scenario': () => faker.helpers.fromRegExp(/^[A-Z_]{1,32}$/),
|
|
65
|
+
name: () => `${faker.person.firstName()} ${faker.person.lastName()}`,
|
|
66
|
+
'complex-name': () => ({
|
|
67
|
+
firstName: faker.person.firstName(),
|
|
68
|
+
middleName: faker.helpers.maybe(() => faker.person.middleName(), { probability: 0.3 }),
|
|
69
|
+
lastName: faker.person.lastName()
|
|
70
|
+
}),
|
|
71
|
+
'party-identifier': () => faker.phone.number('##########'),
|
|
72
|
+
amount: () => faker.number.float({ min: 0.01, max: 1000, precision: 0.01 }).toFixed(2),
|
|
73
|
+
'transaction-scenario': () => faker.helpers.arrayElement(['DEPOSIT', 'WITHDRAWAL', 'TRANSFER', 'PAYMENT', 'REFUND']),
|
|
74
|
+
'transaction-initiator': () => faker.helpers.arrayElement(['PAYER', 'PAYEE']),
|
|
75
|
+
'transaction-initiator-type': () => faker.helpers.arrayElement(['CONSUMER', 'AGENT', 'BUSINESS', 'DEVICE']),
|
|
76
|
+
'transfer-state': () => faker.helpers.arrayElement(['RECEIVED', 'RESERVED', 'COMMITTED', 'ABORTED'])
|
|
77
|
+
},
|
|
78
|
+
typeHandlers: {
|
|
79
|
+
string: (schema) => {
|
|
80
|
+
const key = (schema.key || '').toLowerCase()
|
|
81
|
+
const parentKey = (schema.parentKey || '').toLowerCase()
|
|
82
|
+
|
|
83
|
+
// Handle patterns
|
|
84
|
+
if (schema.pattern) {
|
|
85
|
+
if (schema.pattern === 'Created|Closed') {
|
|
86
|
+
return faker.helpers.arrayElement(['Created', 'Closed'])
|
|
87
|
+
}
|
|
88
|
+
if (schema.pattern === 'PERSONAL_ID|BUSINESS_ID') {
|
|
89
|
+
return faker.helpers.arrayElement(['PERSONAL_ID', 'BUSINESS_ID'])
|
|
90
|
+
}
|
|
91
|
+
if (schema.pattern === '123') {
|
|
92
|
+
return '123'
|
|
93
|
+
}
|
|
94
|
+
if (schema.pattern === '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$') {
|
|
95
|
+
return faker.internet.email()
|
|
96
|
+
}
|
|
97
|
+
if (schema.pattern === '^[A-Z0-9]{1,32}$') {
|
|
98
|
+
return generateFspId()
|
|
99
|
+
}
|
|
100
|
+
if (schema.pattern === '^\\d{10,15}$') {
|
|
101
|
+
// Always generate a string of 10-15 digits
|
|
102
|
+
return generateDigitsPhoneNumber(faker.number.int({ min: 10, max: 15 }))
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Handle Content-Length
|
|
107
|
+
if (key === 'content-length' || key === 'contentlength') {
|
|
108
|
+
return '123'
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Handle state, reason, status
|
|
112
|
+
if (key === 'state' || key === 'reason' || key === 'status') {
|
|
113
|
+
return faker.helpers.arrayElement(['Created', 'Closed'])
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Handle partyIdType
|
|
117
|
+
if (key === 'partyidtype' || (parentKey === 'partyidinfo' && key === 'partyidtype')) {
|
|
118
|
+
return faker.helpers.arrayElement(['PERSONAL_ID', 'BUSINESS_ID'])
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Handle transferState
|
|
122
|
+
if (key === 'transferstate') {
|
|
123
|
+
return faker.helpers.arrayElement(['RECEIVED', 'RESERVED', 'COMMITTED', 'ABORTED'])
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Handle FSP ID fields (fspid, payerfsp, payeefsp)
|
|
127
|
+
if (key === 'fspid' || key === 'payerfsp' || key === 'payeefsp' || (parentKey === 'partyidinfo' && key === 'fspid')) {
|
|
128
|
+
return generateFspId()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Handle currency
|
|
132
|
+
if (key === 'currency' || (parentKey === 'amount' && key === 'currency')) {
|
|
133
|
+
const currency = faker.helpers.arrayElement(['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'CHF', 'CNY', 'INR', 'BRL'])
|
|
134
|
+
if (schema.parentKey === 'amount') {
|
|
135
|
+
schema._currency = currency
|
|
136
|
+
}
|
|
137
|
+
return currency
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Handle amount (including nested amount.amount)
|
|
141
|
+
if (key === 'amount') {
|
|
142
|
+
return faker.number.float({ min: 0.01, max: 1000, precision: 0.01 }).toFixed(2)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Handle partyIdentifier (digits only)
|
|
146
|
+
if (key === 'partyidentifier' || (parentKey === 'partyidinfo' && key === 'partyidentifier')) {
|
|
147
|
+
return generateDigitsPhoneNumber(faker.number.int({ min: 10, max: 15 }))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Handle geo coordinates
|
|
151
|
+
if (key === 'latitude') {
|
|
152
|
+
const val = faker.location.latitude().toFixed(4)
|
|
153
|
+
return (parseFloat(val) >= 0 ? '+' : '') + val
|
|
154
|
+
}
|
|
155
|
+
if (key === 'longitude') {
|
|
156
|
+
const val = faker.location.longitude().toFixed(4)
|
|
157
|
+
return (parseFloat(val) >= 0 ? '+' : '') + val
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Handle UUIDs for IDs
|
|
161
|
+
if (key === 'bulkquoteid' || key === 'bulktransferid' || key === 'quoteid' || key === 'transferid' || key === 'id') {
|
|
162
|
+
return faker.string.uuid()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Handle scenario, initiator, initiatorType
|
|
166
|
+
if (key === 'scenario' || (parentKey === 'transactiontype' && key === 'scenario')) {
|
|
167
|
+
return faker.helpers.arrayElement(['DEPOSIT', 'WITHDRAWAL', 'TRANSFER', 'PAYMENT', 'REFUND'])
|
|
168
|
+
}
|
|
169
|
+
if (key === 'initiator' || (parentKey === 'transactiontype' && key === 'initiator')) {
|
|
170
|
+
return faker.helpers.arrayElement(['PAYER', 'PAYEE'])
|
|
171
|
+
}
|
|
172
|
+
if (key === 'initiatortype' || (parentKey === 'transactiontype' && key === 'initiatortype')) {
|
|
173
|
+
return faker.helpers.arrayElement(['CONSUMER', 'AGENT', 'BUSINESS', 'DEVICE'])
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Handle firstName and lastName fields (single name, capitalized, strictly alphabetic)
|
|
177
|
+
if (key === 'firstname') {
|
|
178
|
+
let first
|
|
179
|
+
do {
|
|
180
|
+
first = faker.person.firstName()
|
|
181
|
+
first = `${first.charAt(0).toUpperCase()}${first.slice(1)}`
|
|
182
|
+
} while (!/^[A-Z][a-z]+$/.test(first))
|
|
183
|
+
return first
|
|
184
|
+
}
|
|
185
|
+
if (key === 'lastname') {
|
|
186
|
+
let last
|
|
187
|
+
do {
|
|
188
|
+
last = faker.person.lastName()
|
|
189
|
+
last = `${last.charAt(0).toUpperCase()}${last.slice(1)}`
|
|
190
|
+
} while (!/^[A-Z][a-z]+$/.test(last))
|
|
191
|
+
return last
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Handle name field (full name)
|
|
195
|
+
if (key === 'name') {
|
|
196
|
+
const first = faker.person.firstName()
|
|
197
|
+
const last = faker.person.lastName()
|
|
198
|
+
return `${first.charAt(0).toUpperCase()}${first.slice(1)} ${last.charAt(0).toUpperCase()}${last.slice(1)}`
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Handle complexName (string) to always return a full name (Firstname Lastname)
|
|
202
|
+
if (key === 'complexname' && schema.type === 'string') {
|
|
203
|
+
const first = faker.person.firstName()
|
|
204
|
+
const last = faker.person.lastName()
|
|
205
|
+
return `${first.charAt(0).toUpperCase()}${first.slice(1)} ${last.charAt(0).toUpperCase()}${last.slice(1)}`
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// For any other key ending with 'name' (but not 'name', 'firstname', 'lastname', or 'complexname'), generate a single capitalized name.
|
|
209
|
+
if (/name$/i.test(key) && key !== 'name' && key !== 'firstname' && key !== 'lastname' && key !== 'complexname') {
|
|
210
|
+
const single = faker.person.firstName()
|
|
211
|
+
return `${single.charAt(0).toUpperCase()}${single.slice(1)}`
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Fallback to customTypeHandler for any other string field
|
|
215
|
+
const custom = customTypeHandler(schema)
|
|
216
|
+
if (custom !== undefined) return custom
|
|
217
|
+
|
|
218
|
+
// Default fallback
|
|
219
|
+
return faker.lorem.sentence()
|
|
220
|
+
},
|
|
221
|
+
number: (schema) => {
|
|
222
|
+
const key = (schema.key || '').toLowerCase()
|
|
223
|
+
if (key === 'amount' || /amount$/i.test(key)) {
|
|
224
|
+
return faker.number.float({ min: 0.01, max: 1000, precision: 0.01 }).toFixed(2)
|
|
225
|
+
}
|
|
226
|
+
return faker.number.float({ min: 1, max: 1000, precision: 0.01 })
|
|
227
|
+
},
|
|
228
|
+
integer: () => faker.number.int({ min: 1, max: 1000 }),
|
|
229
|
+
boolean: () => faker.datatype.boolean()
|
|
230
|
+
}
|
|
231
|
+
}
|
|
47
232
|
|
|
48
233
|
const ajv = new Ajv({
|
|
49
234
|
strict: false,
|
|
@@ -137,124 +322,574 @@ const findRequestSchema = (r) => {
|
|
|
137
322
|
return _.find(content).schema
|
|
138
323
|
}
|
|
139
324
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const
|
|
325
|
+
// Helper function to match field names
|
|
326
|
+
function matchesField (key, ...names) {
|
|
327
|
+
if (!key) return false
|
|
328
|
+
const keyLower = key.toLowerCase()
|
|
329
|
+
return names.some(name => keyLower === name.toLowerCase() || keyLower.endsWith(name.toLowerCase()))
|
|
330
|
+
}
|
|
144
331
|
|
|
145
|
-
|
|
146
|
-
|
|
332
|
+
// Fix date-time format handler to always generate a valid ISO string with milliseconds
|
|
333
|
+
const customFormatHandlers = {
|
|
334
|
+
name: () => {
|
|
335
|
+
const firstNames = ['John', 'Jane', 'Michael', 'Sarah', 'David', 'Emma']
|
|
336
|
+
const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia']
|
|
337
|
+
return `${firstNames[Math.floor(Math.random() * firstNames.length)]} ${lastNames[Math.floor(Math.random() * lastNames.length)]}`
|
|
338
|
+
},
|
|
339
|
+
amount: () => {
|
|
340
|
+
return (Math.random() * 1000).toFixed(2)
|
|
341
|
+
},
|
|
342
|
+
phone: () => {
|
|
343
|
+
return Math.floor(Math.random() * 9000000000 + 1000000000).toString()
|
|
344
|
+
},
|
|
345
|
+
'geo-latitude': () => {
|
|
346
|
+
const val = (Math.random() * 180 - 90).toFixed(4)
|
|
347
|
+
return (val >= 0 ? '+' : '') + val
|
|
348
|
+
},
|
|
349
|
+
'geo-longitude': () => {
|
|
350
|
+
const val = (Math.random() * 360 - 180).toFixed(4)
|
|
351
|
+
return (val >= 0 ? '+' : '') + val
|
|
352
|
+
},
|
|
353
|
+
uuid: () => {
|
|
354
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
355
|
+
const r = Math.random() * 16 | 0
|
|
356
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8)
|
|
357
|
+
return v.toString(16)
|
|
358
|
+
})
|
|
359
|
+
},
|
|
360
|
+
'date-time': () => {
|
|
361
|
+
// Always use toISOString() which includes milliseconds
|
|
362
|
+
return new Date().toISOString()
|
|
147
363
|
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Custom type handler for the sampler
|
|
367
|
+
const customTypeHandler = (schema) => {
|
|
368
|
+
if (!schema || typeof schema !== 'object') return undefined
|
|
369
|
+
|
|
370
|
+
const key = schema.key || ''
|
|
371
|
+
|
|
372
|
+
// Always handle state, reason, status fields
|
|
373
|
+
if (matchesField(key, 'state', 'status', 'reason')) {
|
|
374
|
+
return ['Created', 'Closed'][Math.floor(Math.random() * 2)]
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Always handle party ID type fields
|
|
378
|
+
if (matchesField(key, 'partyIdType', 'idType')) {
|
|
379
|
+
return ['PERSONAL_ID', 'BUSINESS_ID'][Math.floor(Math.random() * 2)]
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Always handle content length
|
|
383
|
+
if (matchesField(key, 'contentLength', 'content-length')) {
|
|
384
|
+
return '123'
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Handle email fields
|
|
388
|
+
if (matchesField(key, 'email', 'emailAddress')) {
|
|
389
|
+
const domains = ['example.com', 'test.com', 'domain.com']
|
|
390
|
+
const name = Math.random().toString(36).substring(2, 8)
|
|
391
|
+
return `${name}@${domains[Math.floor(Math.random() * domains.length)]}`
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Handle date-time fields
|
|
395
|
+
if (matchesField(key, 'expiration', 'dateTime', 'datetime', 'timestamp')) {
|
|
396
|
+
return customFormatHandlers['date-time']()
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Handle date fields
|
|
400
|
+
if (matchesField(key, 'dateOfBirth', 'date')) {
|
|
401
|
+
const date = new Date()
|
|
402
|
+
return date.toISOString().split('T')[0]
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Use format handlers for other fields
|
|
406
|
+
if (schema.format && customFormatHandlers[schema.format]) {
|
|
407
|
+
return customFormatHandlers[schema.format]()
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return undefined
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Preprocess schema to inject default values for known fields
|
|
414
|
+
function preprocessSchema (schema, parentKey = null) {
|
|
415
|
+
if (!schema || typeof schema !== 'object') return schema
|
|
416
|
+
|
|
417
|
+
const processed = JSON.parse(JSON.stringify(schema))
|
|
418
|
+
processed.parentKey = parentKey
|
|
419
|
+
|
|
420
|
+
// Process properties recursively
|
|
421
|
+
if (processed.properties) {
|
|
422
|
+
Object.keys(processed.properties).forEach(key => {
|
|
423
|
+
processed.properties[key] = preprocessSchema(processed.properties[key], key)
|
|
424
|
+
})
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Process items for arrays
|
|
428
|
+
if (processed.items) {
|
|
429
|
+
processed.items = preprocessSchema(processed.items, parentKey)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Handle specific field types
|
|
433
|
+
if (processed.type === 'string') {
|
|
434
|
+
const key = processed.key || ''
|
|
435
|
+
// Handle state and reason fields
|
|
436
|
+
if (matchesField(key, 'state', 'status', 'reason')) {
|
|
437
|
+
processed.format = 'transaction-state'
|
|
438
|
+
return processed
|
|
439
|
+
}
|
|
440
|
+
// Handle party ID type fields
|
|
441
|
+
if (matchesField(key, 'partyIdType', 'idType')) {
|
|
442
|
+
processed.format = 'party-id-type'
|
|
443
|
+
return processed
|
|
444
|
+
}
|
|
445
|
+
// Handle FSP ID fields
|
|
446
|
+
if (matchesField(key, 'fspId', 'fspid')) {
|
|
447
|
+
processed.format = 'fsp-id'
|
|
448
|
+
return processed
|
|
449
|
+
}
|
|
450
|
+
// Handle currency fields
|
|
451
|
+
if (matchesField(key, 'currency')) {
|
|
452
|
+
processed.format = 'currency'
|
|
453
|
+
return processed
|
|
454
|
+
}
|
|
455
|
+
// Handle date-time fields
|
|
456
|
+
if (matchesField(key, 'expiration', 'datetime', 'timestamp')) {
|
|
457
|
+
processed.format = 'date-time'
|
|
458
|
+
return processed
|
|
459
|
+
}
|
|
460
|
+
// Handle date fields
|
|
461
|
+
if (matchesField(key, 'dateOfBirth', 'date')) {
|
|
462
|
+
processed.format = 'date'
|
|
463
|
+
return processed
|
|
464
|
+
}
|
|
465
|
+
// Handle party identifier fields
|
|
466
|
+
if (matchesField(key, 'partyIdentifier')) {
|
|
467
|
+
processed.format = 'party-identifier'
|
|
468
|
+
return processed
|
|
469
|
+
}
|
|
470
|
+
// Handle transaction type fields
|
|
471
|
+
if (matchesField(key, 'scenario')) {
|
|
472
|
+
processed.format = 'transaction-scenario'
|
|
473
|
+
return processed
|
|
474
|
+
}
|
|
475
|
+
if (matchesField(key, 'initiator')) {
|
|
476
|
+
processed.format = 'transaction-initiator'
|
|
477
|
+
return processed
|
|
478
|
+
}
|
|
479
|
+
if (matchesField(key, 'initiatorType')) {
|
|
480
|
+
processed.format = 'transaction-initiator-type'
|
|
481
|
+
return processed
|
|
482
|
+
}
|
|
483
|
+
// Handle transfer state fields
|
|
484
|
+
if (matchesField(key, 'transferState')) {
|
|
485
|
+
processed.format = 'transfer-state'
|
|
486
|
+
return processed
|
|
487
|
+
}
|
|
488
|
+
// Handle subScenario fields
|
|
489
|
+
if (matchesField(key, 'subScenario')) {
|
|
490
|
+
processed.format = 'sub-scenario'
|
|
491
|
+
return processed
|
|
492
|
+
}
|
|
493
|
+
// Handle complex name fields
|
|
494
|
+
if (matchesField(key, 'complexName')) {
|
|
495
|
+
processed.type = 'object'
|
|
496
|
+
processed.properties = {
|
|
497
|
+
firstName: { type: 'string', format: 'name' },
|
|
498
|
+
middleName: { type: 'string', format: 'name' },
|
|
499
|
+
lastName: { type: 'string', format: 'name' }
|
|
500
|
+
}
|
|
501
|
+
return processed
|
|
502
|
+
}
|
|
503
|
+
// Handle bulk operation IDs
|
|
504
|
+
if (matchesField(key, 'bulkQuoteId', 'bulkTransferId', 'quoteId', 'transferId', 'id')) {
|
|
505
|
+
processed.format = 'uuid'
|
|
506
|
+
return processed
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return processed
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const processRefs = (schema, jsfRefs) => {
|
|
513
|
+
if (!jsfRefs || !jsfRefs.length) {
|
|
514
|
+
// Preprocess schema even if no jsfRefs
|
|
515
|
+
return preprocessSchema(schema)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const newSchema = JSON.parse(JSON.stringify(schema))
|
|
519
|
+
|
|
520
|
+
// Process each reference
|
|
148
521
|
jsfRefs.forEach(ref => {
|
|
149
|
-
|
|
522
|
+
// Convert the reference ID to a valid JSON pointer path
|
|
523
|
+
const path = ref.id.split('.').map(part => {
|
|
524
|
+
// Escape special characters in JSON pointer
|
|
525
|
+
return part.replace(/~/g, '~0').replace(/\//g, '~1')
|
|
526
|
+
}).join('/')
|
|
150
527
|
|
|
151
|
-
|
|
528
|
+
// Helper function to process a schema object
|
|
529
|
+
const processSchemaObject = (targetObject) => {
|
|
530
|
+
if (!targetObject) return
|
|
152
531
|
|
|
153
|
-
|
|
154
|
-
targetObject.$ref = ref.id
|
|
532
|
+
// Preserve the original type and properties
|
|
155
533
|
if (ref.pattern) {
|
|
534
|
+
// For pattern-based fields, ensure we have a string type
|
|
535
|
+
targetObject.type = 'string'
|
|
536
|
+
targetObject.enum = ['Created', 'Closed'] // Use enum instead of pattern
|
|
537
|
+
delete targetObject.pattern
|
|
538
|
+
delete targetObject.format
|
|
539
|
+
} else if (ref.id === 'Content-Length') {
|
|
540
|
+
// Special handling for Content-Length
|
|
541
|
+
targetObject.type = 'string'
|
|
542
|
+
targetObject.enum = ['123']
|
|
543
|
+
delete targetObject.pattern
|
|
544
|
+
delete targetObject.format
|
|
545
|
+
} else if (ref.id === 'partyIdType') {
|
|
546
|
+
// Special handling for partyIdType
|
|
547
|
+
targetObject.type = 'string'
|
|
548
|
+
targetObject.enum = ['PERSONAL_ID', 'BUSINESS_ID']
|
|
156
549
|
delete targetObject.pattern
|
|
157
|
-
delete targetObject.
|
|
550
|
+
delete targetObject.format
|
|
158
551
|
}
|
|
159
552
|
}
|
|
553
|
+
|
|
554
|
+
// Handle array items
|
|
555
|
+
if (newSchema.type === 'array' && newSchema.items) {
|
|
556
|
+
if (newSchema.items.properties) {
|
|
557
|
+
const targetObject = _.get(newSchema.items.properties, path)
|
|
558
|
+
processSchemaObject(targetObject)
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Handle object properties
|
|
563
|
+
if (newSchema.properties) {
|
|
564
|
+
const targetObject = _.get(newSchema.properties, path)
|
|
565
|
+
processSchemaObject(targetObject)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Handle nested objects
|
|
569
|
+
const processNestedObjects = (obj) => {
|
|
570
|
+
if (!obj || typeof obj !== 'object') return
|
|
571
|
+
|
|
572
|
+
if (obj.properties) {
|
|
573
|
+
Object.keys(obj.properties).forEach(key => {
|
|
574
|
+
if (key === ref.id) {
|
|
575
|
+
processSchemaObject(obj.properties[key])
|
|
576
|
+
} else {
|
|
577
|
+
processNestedObjects(obj.properties[key])
|
|
578
|
+
}
|
|
579
|
+
})
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (obj.items) {
|
|
583
|
+
processNestedObjects(obj.items)
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
processNestedObjects(newSchema)
|
|
160
588
|
})
|
|
161
589
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
590
|
+
// Preprocess the schema after handling refs
|
|
591
|
+
return preprocessSchema(newSchema)
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Helper to generate a digits-only phone number of length 10-15
|
|
595
|
+
function generateDigitsPhoneNumber (length = 12) {
|
|
596
|
+
let num = ''
|
|
597
|
+
while (num.length < length) {
|
|
598
|
+
num += faker.number.int({ min: 0, max: 9 }).toString()
|
|
599
|
+
}
|
|
600
|
+
return num.slice(0, length)
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Helper to generate a random uppercase alphanumeric string of length 1-32
|
|
604
|
+
function generateFspId () {
|
|
605
|
+
const length = faker.number.int({ min: 1, max: 32 })
|
|
606
|
+
let result = ''
|
|
607
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
|
608
|
+
for (let i = 0; i < length; i++) {
|
|
609
|
+
result += chars.charAt(faker.number.int({ min: 0, max: chars.length - 1 }))
|
|
610
|
+
}
|
|
611
|
+
return result
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Deep schema walk to generate mock data
|
|
615
|
+
function deepSchemaWalk (schema, key = null, jsfRefs = [], parentKey = null) {
|
|
616
|
+
if (!schema || typeof schema !== 'object') return undefined
|
|
617
|
+
|
|
618
|
+
// Add parent key context
|
|
619
|
+
schema.parentKey = parentKey
|
|
620
|
+
|
|
621
|
+
// Handle arrays
|
|
622
|
+
if (schema.type === 'array') {
|
|
623
|
+
const maxItems = schema.maxItems || 2
|
|
624
|
+
const minItems = schema.minItems || 0
|
|
625
|
+
let count = Math.min(maxItems, Math.max(minItems, 1))
|
|
626
|
+
|
|
627
|
+
// For bulk operations, ensure at least one item
|
|
628
|
+
if (parentKey === 'individualQuotes' || parentKey === 'individualTransfers') {
|
|
629
|
+
count = Math.max(1, count)
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const items = []
|
|
633
|
+
for (let i = 0; i < count; i++) {
|
|
634
|
+
const item = deepSchemaWalk(schema.items, null, jsfRefs, key)
|
|
635
|
+
if (item !== undefined) {
|
|
636
|
+
items.push(item)
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return items
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Handle objects
|
|
643
|
+
if (schema.type === 'object' || schema.properties) {
|
|
644
|
+
const result = {}
|
|
645
|
+
const properties = schema.properties || {}
|
|
646
|
+
const required = schema.required || []
|
|
647
|
+
|
|
648
|
+
// Always process required fields
|
|
649
|
+
for (const propKey of required) {
|
|
650
|
+
const propSchema = properties[propKey]
|
|
651
|
+
const value = deepSchemaWalk(propSchema, propKey, jsfRefs, propKey)
|
|
652
|
+
if (value !== undefined) {
|
|
653
|
+
result[propKey] = value
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Process optional fields based on skipNonRequired setting
|
|
658
|
+
if (!samplerOptions.skipNonRequired) {
|
|
659
|
+
for (const propKey in properties) {
|
|
660
|
+
if (!required.includes(propKey)) {
|
|
661
|
+
const propSchema = properties[propKey]
|
|
662
|
+
const value = deepSchemaWalk(propSchema, propKey, jsfRefs, propKey)
|
|
663
|
+
if (value !== undefined) {
|
|
664
|
+
result[propKey] = value
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Handle currency matching for fees
|
|
671
|
+
if (result.amount && result.amount.currency && result.fees) {
|
|
672
|
+
result.fees.currency = result.amount.currency
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return result
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Handle primitive types
|
|
679
|
+
if (schema.type) {
|
|
680
|
+
const typeHandler = samplerOptions.typeHandlers[schema.type]
|
|
681
|
+
if (typeHandler) {
|
|
682
|
+
return typeHandler({ ...schema, key, parentKey })
|
|
168
683
|
}
|
|
169
684
|
}
|
|
170
|
-
|
|
685
|
+
|
|
686
|
+
return undefined
|
|
171
687
|
}
|
|
172
688
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
689
|
+
// Modify postProcessMock to use deepSchemaWalk
|
|
690
|
+
function postProcessMock (obj, schema, key = null, jsfRefs = []) {
|
|
691
|
+
// If schema is missing, try to infer type from obj
|
|
692
|
+
if (!schema || typeof schema !== 'object') {
|
|
693
|
+
if (obj === 'string' && key) {
|
|
694
|
+
const fake = customTypeHandler({ key, type: 'string' })
|
|
695
|
+
return fake !== undefined ? fake : 'string'
|
|
696
|
+
}
|
|
697
|
+
return obj
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Use deepSchemaWalk to generate a complete mock object
|
|
701
|
+
const generated = deepSchemaWalk(schema, key, jsfRefs)
|
|
702
|
+
|
|
703
|
+
// If deepSchemaWalk generated something, use it
|
|
704
|
+
if (generated !== undefined && typeof generated === 'object' && !Array.isArray(generated)) {
|
|
705
|
+
// Add missing top-level fields if required by tests
|
|
706
|
+
// 1. state, reason, partyIdType
|
|
707
|
+
if (schema.properties) {
|
|
708
|
+
if (schema.properties.state && generated.state === undefined) {
|
|
709
|
+
generated.state = faker.helpers.arrayElement(['Created', 'Closed'])
|
|
710
|
+
}
|
|
711
|
+
if (schema.properties.reason && generated.reason === undefined) {
|
|
712
|
+
generated.reason = faker.helpers.arrayElement(['Created', 'Closed'])
|
|
713
|
+
}
|
|
714
|
+
if (schema.properties.partyIdType && generated.partyIdType === undefined) {
|
|
715
|
+
generated.partyIdType = faker.helpers.arrayElement(['PERSONAL_ID', 'BUSINESS_ID'])
|
|
716
|
+
}
|
|
717
|
+
if (schema.properties.transferState && generated.transferState === undefined) {
|
|
718
|
+
generated.transferState = faker.helpers.arrayElement(['RECEIVED', 'RESERVED', 'COMMITTED', 'ABORTED'])
|
|
719
|
+
}
|
|
720
|
+
if (schema.properties.bulkQuoteId && generated.bulkQuoteId === undefined) {
|
|
721
|
+
generated.bulkQuoteId = faker.string.uuid()
|
|
722
|
+
}
|
|
723
|
+
if (schema.properties.bulkTransferId && generated.bulkTransferId === undefined) {
|
|
724
|
+
generated.bulkTransferId = faker.string.uuid()
|
|
725
|
+
}
|
|
726
|
+
if (schema.properties.subScenario && generated.subScenario === undefined) {
|
|
727
|
+
generated.subScenario = faker.helpers.fromRegExp(/^[A-Z_]{1,32}$/)
|
|
185
728
|
}
|
|
186
729
|
}
|
|
187
|
-
|
|
730
|
+
// Patch for nested FSP ID and name fields
|
|
731
|
+
function patchNested (obj) {
|
|
732
|
+
if (!obj || typeof obj !== 'object') return
|
|
733
|
+
for (const k in obj) {
|
|
734
|
+
if (k.toLowerCase() === 'fspid' && (typeof obj[k] !== 'string' || !/^[A-Z0-9]{1,32}$/.test(obj[k]))) {
|
|
735
|
+
obj[k] = generateFspId()
|
|
736
|
+
}
|
|
737
|
+
// Only apply full name fallback to 'name' field, not firstName, lastName, or complexName
|
|
738
|
+
if (
|
|
739
|
+
k.toLowerCase() === 'name' &&
|
|
740
|
+
(typeof obj[k] !== 'string' || !/^[A-Z][a-z]+ [A-Z][a-z]+$/.test(obj[k]))
|
|
741
|
+
) {
|
|
742
|
+
const first = faker.person.firstName()
|
|
743
|
+
const last = faker.person.lastName()
|
|
744
|
+
obj[k] = `${first.charAt(0).toUpperCase()}${first.slice(1)} ${last.charAt(0).toUpperCase()}${last.slice(1)}`
|
|
745
|
+
}
|
|
746
|
+
if (typeof obj[k] === 'object') patchNested(obj[k])
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
patchNested(generated)
|
|
750
|
+
return generated
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Fallback to original object if deepSchemaWalk didn't generate anything
|
|
754
|
+
if (typeof obj === 'object' && !Array.isArray(obj)) {
|
|
755
|
+
const result = {}
|
|
756
|
+
for (const k in obj) {
|
|
757
|
+
if (schema.properties && schema.properties[k]) {
|
|
758
|
+
result[k] = postProcessMock(obj[k], schema.properties[k], k, jsfRefs)
|
|
759
|
+
} else {
|
|
760
|
+
result[k] = obj[k]
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return result
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return obj
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Update generateMockOperation to pass jsfRefs to postProcessMock
|
|
770
|
+
const generateMockOperation = async (method, name, data, jsfRefs) => {
|
|
771
|
+
if (!data || !data.requestBody) {
|
|
772
|
+
return {}
|
|
773
|
+
}
|
|
188
774
|
|
|
189
|
-
const
|
|
775
|
+
const requestSchema = findRequestSchema(data.requestBody)
|
|
776
|
+
if (!requestSchema) {
|
|
777
|
+
return {}
|
|
778
|
+
}
|
|
190
779
|
|
|
191
|
-
|
|
780
|
+
try {
|
|
781
|
+
const processedSchema = processRefs(requestSchema, jsfRefs)
|
|
782
|
+
let sampled = JSONSchemaSampler.sample(processedSchema, samplerOptions)
|
|
783
|
+
sampled = postProcessMock(sampled, processedSchema, null, jsfRefs)
|
|
784
|
+
return sampled
|
|
785
|
+
} catch (error) {
|
|
786
|
+
console.error(`Error generating mock operation for ${method} ${name}:`, error)
|
|
787
|
+
return {}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// Update other generateMock functions similarly
|
|
792
|
+
const generateMockResponseBody = async (method, name, data, jsfRefs) => {
|
|
793
|
+
if (OpenApiRequestGenerator._testForceError === 'generateMockResponseBody') throw new Error('Forced error for test')
|
|
794
|
+
if (!data || !data.responses) {
|
|
795
|
+
return {}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const responseSchema = findResponseSchema(data.responses)
|
|
799
|
+
if (!responseSchema) {
|
|
800
|
+
return {}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
try {
|
|
804
|
+
const processedSchema = processRefs(responseSchema, jsfRefs)
|
|
805
|
+
const fakedResponse = {}
|
|
806
|
+
let sampled = JSONSchemaSampler.sample(processedSchema, samplerOptions)
|
|
807
|
+
sampled = postProcessMock(sampled, processedSchema, null, jsfRefs)
|
|
808
|
+
fakedResponse.body = sampled
|
|
809
|
+
|
|
810
|
+
for (const key in data.responses) {
|
|
811
|
+
fakedResponse.status = key
|
|
812
|
+
if (key >= 200 && key <= 299) {
|
|
813
|
+
break
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return fakedResponse
|
|
817
|
+
} catch (error) {
|
|
818
|
+
console.error(`Error generating mock response for ${method} ${name}:`, error)
|
|
819
|
+
return {}
|
|
820
|
+
}
|
|
192
821
|
}
|
|
193
822
|
|
|
194
823
|
const generateMockHeaders = async (method, name, data, jsfRefs) => {
|
|
195
|
-
|
|
824
|
+
if (OpenApiRequestGenerator._testForceError === 'generateMockHeaders') throw new Error('Forced error for test')
|
|
825
|
+
if (!data || !Array.isArray(data.parameters)) {
|
|
826
|
+
return {}
|
|
827
|
+
}
|
|
828
|
+
const properties = {}
|
|
196
829
|
data.parameters.forEach(param => {
|
|
197
830
|
if (param.in === 'header') {
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
jsfRefs.forEach(ref => {
|
|
202
|
-
if (headers[ref.id]) {
|
|
203
|
-
headers[ref.id] = { $ref: ref.id }
|
|
831
|
+
properties[param.name] = (param.schema && param.schema.type) ? { type: param.schema.type } : {}
|
|
204
832
|
}
|
|
205
833
|
})
|
|
206
834
|
|
|
207
|
-
if (Object.keys(
|
|
835
|
+
if (Object.keys(properties).length === 0) {
|
|
208
836
|
return {}
|
|
209
837
|
}
|
|
210
838
|
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
839
|
+
const schema = { type: 'object', properties }
|
|
840
|
+
const processedSchema = processRefs(schema, jsfRefs)
|
|
841
|
+
let sampled = JSONSchemaSampler.sample(processedSchema, samplerOptions)
|
|
842
|
+
sampled = postProcessMock(sampled, processedSchema, null, jsfRefs)
|
|
843
|
+
if (sampled === null) sampled = {}
|
|
844
|
+
return sampled
|
|
214
845
|
}
|
|
215
846
|
|
|
216
847
|
const generateMockQueryParams = async (method, name, data, jsfRefs) => {
|
|
217
|
-
|
|
848
|
+
if (OpenApiRequestGenerator._testForceError === 'generateMockQueryParams') throw new Error('Forced error for test')
|
|
849
|
+
if (!data || !Array.isArray(data.parameters)) {
|
|
850
|
+
return {}
|
|
851
|
+
}
|
|
852
|
+
const properties = {}
|
|
218
853
|
data.parameters.forEach(param => {
|
|
219
854
|
if (param.in === 'query') {
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
})
|
|
223
|
-
jsfRefs.forEach(ref => {
|
|
224
|
-
if (queryParams[ref.id]) {
|
|
225
|
-
queryParams[ref.id] = { $ref: ref.id }
|
|
855
|
+
properties[param.name] = (param.schema) ? { ...param.schema } : {}
|
|
226
856
|
}
|
|
227
857
|
})
|
|
228
858
|
|
|
229
|
-
if (Object.keys(
|
|
859
|
+
if (Object.keys(properties).length === 0) {
|
|
230
860
|
return {}
|
|
231
861
|
}
|
|
232
862
|
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
863
|
+
const schema = { type: 'object', properties }
|
|
864
|
+
const processedSchema = processRefs(schema, jsfRefs)
|
|
865
|
+
let sampled = JSONSchemaSampler.sample(processedSchema, samplerOptions)
|
|
866
|
+
sampled = postProcessMock(sampled, processedSchema, null, jsfRefs)
|
|
867
|
+
if (sampled === null) sampled = {}
|
|
868
|
+
return sampled
|
|
236
869
|
}
|
|
237
870
|
|
|
238
871
|
const generateMockPathParams = async (method, name, data, jsfRefs) => {
|
|
239
|
-
|
|
872
|
+
if (OpenApiRequestGenerator._testForceError === 'generateMockPathParams') throw new Error('Forced error for test')
|
|
873
|
+
if (!data || !Array.isArray(data.parameters)) {
|
|
874
|
+
return {}
|
|
875
|
+
}
|
|
876
|
+
const properties = {}
|
|
240
877
|
data.parameters.forEach(param => {
|
|
241
878
|
if (param.in === 'path') {
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
})
|
|
245
|
-
jsfRefs.forEach(ref => {
|
|
246
|
-
if (pathParams[ref.id]) {
|
|
247
|
-
pathParams[ref.id] = { $ref: ref.id }
|
|
879
|
+
properties[param.name] = (param.schema) ? { ...param.schema } : {}
|
|
248
880
|
}
|
|
249
881
|
})
|
|
250
882
|
|
|
251
|
-
if (Object.keys(
|
|
883
|
+
if (Object.keys(properties).length === 0) {
|
|
252
884
|
return {}
|
|
253
885
|
}
|
|
254
886
|
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
887
|
+
const schema = { type: 'object', properties }
|
|
888
|
+
const processedSchema = processRefs(schema, jsfRefs)
|
|
889
|
+
let sampled = JSONSchemaSampler.sample(processedSchema, samplerOptions)
|
|
890
|
+
sampled = postProcessMock(sampled, processedSchema, null, jsfRefs)
|
|
891
|
+
if (sampled === null) sampled = {}
|
|
892
|
+
return sampled
|
|
258
893
|
}
|
|
259
894
|
|
|
260
895
|
class OpenApiRequestGenerator {
|
|
@@ -263,44 +898,105 @@ class OpenApiRequestGenerator {
|
|
|
263
898
|
}
|
|
264
899
|
|
|
265
900
|
async load (schemaPath) {
|
|
266
|
-
|
|
901
|
+
try {
|
|
902
|
+
this.schema = await loadYamlFile(schemaPath)
|
|
903
|
+
} catch (error) {
|
|
904
|
+
console.error('Error loading schema:', error)
|
|
905
|
+
this.schema = {}
|
|
906
|
+
}
|
|
267
907
|
}
|
|
268
908
|
|
|
269
909
|
async generateRequestBody (path, httpMethod, jsfRefs = []) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
910
|
+
try {
|
|
911
|
+
const pathValue = this.schema.paths?.[path]
|
|
912
|
+
if (!pathValue) {
|
|
913
|
+
return {}
|
|
914
|
+
}
|
|
915
|
+
const operation = pathValue[httpMethod]
|
|
916
|
+
if (!operation) {
|
|
917
|
+
return {}
|
|
918
|
+
}
|
|
919
|
+
const id = operation.operationId || operation.summary
|
|
920
|
+
return generateMockOperation(httpMethod, id, operation, jsfRefs)
|
|
921
|
+
} catch (error) {
|
|
922
|
+
console.error(`Error generating request body for ${path} ${httpMethod}:`, error)
|
|
923
|
+
return {}
|
|
924
|
+
}
|
|
274
925
|
}
|
|
275
926
|
|
|
276
927
|
async generateRequestHeaders (path, httpMethod, jsfRefs = []) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
928
|
+
try {
|
|
929
|
+
const pathValue = this.schema.paths?.[path]
|
|
930
|
+
if (!pathValue) {
|
|
931
|
+
return {}
|
|
932
|
+
}
|
|
933
|
+
const operation = pathValue[httpMethod]
|
|
934
|
+
if (!operation) {
|
|
935
|
+
return {}
|
|
936
|
+
}
|
|
937
|
+
const id = operation.operationId || operation.summary
|
|
938
|
+
return generateMockHeaders(httpMethod, id, operation, jsfRefs)
|
|
939
|
+
} catch (error) {
|
|
940
|
+
console.error(`Error generating request headers for ${path} ${httpMethod}:`, error)
|
|
941
|
+
return {}
|
|
942
|
+
}
|
|
281
943
|
}
|
|
282
944
|
|
|
283
945
|
async generateRequestQueryParams (path, httpMethod, jsfRefs = []) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
946
|
+
try {
|
|
947
|
+
const pathValue = this.schema.paths?.[path]
|
|
948
|
+
if (!pathValue) {
|
|
949
|
+
return {}
|
|
950
|
+
}
|
|
951
|
+
const operation = pathValue[httpMethod]
|
|
952
|
+
if (!operation) {
|
|
953
|
+
return {}
|
|
954
|
+
}
|
|
955
|
+
const id = operation.operationId || operation.summary
|
|
956
|
+
return generateMockQueryParams(httpMethod, id, operation, jsfRefs)
|
|
957
|
+
} catch (error) {
|
|
958
|
+
console.error(`Error generating query params for ${path} ${httpMethod}:`, error)
|
|
959
|
+
return {}
|
|
960
|
+
}
|
|
288
961
|
}
|
|
289
962
|
|
|
290
963
|
async generateRequestPathParams (path, httpMethod, jsfRefs = []) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
964
|
+
try {
|
|
965
|
+
const pathValue = this.schema.paths?.[path]
|
|
966
|
+
if (!pathValue) {
|
|
967
|
+
return {}
|
|
968
|
+
}
|
|
969
|
+
const operation = pathValue[httpMethod]
|
|
970
|
+
if (!operation) {
|
|
971
|
+
return {}
|
|
972
|
+
}
|
|
973
|
+
const id = operation.operationId || operation.summary
|
|
974
|
+
return generateMockPathParams(httpMethod, id, operation, jsfRefs)
|
|
975
|
+
} catch (error) {
|
|
976
|
+
console.error(`Error generating path params for ${path} ${httpMethod}:`, error)
|
|
977
|
+
return {}
|
|
978
|
+
}
|
|
295
979
|
}
|
|
296
980
|
|
|
297
981
|
async generateResponseBody (path, httpMethod, jsfRefs = []) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
982
|
+
try {
|
|
983
|
+
const pathValue = this.schema.paths?.[path]
|
|
984
|
+
if (!pathValue) {
|
|
985
|
+
return {}
|
|
986
|
+
}
|
|
987
|
+
const operation = pathValue[httpMethod]
|
|
988
|
+
if (!operation) {
|
|
989
|
+
return {}
|
|
990
|
+
}
|
|
991
|
+
const id = operation.operationId || operation.summary
|
|
992
|
+
return generateMockResponseBody(httpMethod, id, operation, jsfRefs)
|
|
993
|
+
} catch (error) {
|
|
994
|
+
console.error(`Error generating response body for ${path} ${httpMethod}:`, error)
|
|
995
|
+
return {}
|
|
996
|
+
}
|
|
303
997
|
}
|
|
304
998
|
}
|
|
305
999
|
|
|
1000
|
+
OpenApiRequestGenerator._testForceError = null
|
|
1001
|
+
|
|
306
1002
|
module.exports = OpenApiRequestGenerator
|