@voxgig/sdkgen 0.44.0 → 1.0.1
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/bin/voxgig-sdkgen +1 -1
- package/dist/cmp/ReadmeEntity.js +9 -153
- package/dist/cmp/ReadmeEntity.js.map +1 -1
- package/dist/cmp/ReadmeIntro.js +9 -14
- package/dist/cmp/ReadmeIntro.js.map +1 -1
- package/dist/cmp/ReadmeModel.js +6 -4
- package/dist/cmp/ReadmeModel.js.map +1 -1
- package/dist/cmp/ReadmeOptions.js +9 -61
- package/dist/cmp/ReadmeOptions.js.map +1 -1
- package/dist/cmp/ReadmeRef.js +10 -1328
- package/dist/cmp/ReadmeRef.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +2 -1
- package/dist/sdkgen.js.map +1 -1
- package/dist/utility.d.ts +2 -1
- package/dist/utility.js +9 -0
- package/dist/utility.js.map +1 -1
- package/package.json +3 -3
- package/project/.sdk/src/cmp/go/Config_go.ts +9 -4
- package/project/.sdk/src/cmp/go/Entity_go.ts +2 -2
- package/project/.sdk/src/cmp/go/Main_go.ts +8 -4
- package/project/.sdk/src/cmp/go/Package_go.ts +2 -2
- package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +138 -0
- package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +2 -2
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +8 -5
- package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +2 -2
- package/project/.sdk/src/cmp/go/ReadmeIntro_go.ts +18 -0
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +8 -5
- package/project/.sdk/src/cmp/go/ReadmeOptions_go.ts +58 -0
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +13 -9
- package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +354 -0
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +8 -6
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +2 -2
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +222 -41
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +142 -60
- package/project/.sdk/src/cmp/go/Test_go.ts +2 -2
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +21 -4
- package/project/.sdk/src/cmp/js/Config_js.ts +18 -0
- package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +138 -0
- package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +11 -6
- package/project/.sdk/src/cmp/js/ReadmeIntro_js.ts +18 -0
- package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +6 -3
- package/project/.sdk/src/cmp/js/ReadmeOptions_js.ts +58 -0
- package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +6 -4
- package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +384 -0
- package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +6 -4
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +23 -12
- package/project/.sdk/src/cmp/js/TestEntity_js.ts +107 -74
- package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +1 -5
- package/project/.sdk/src/cmp/lua/Config_lua.ts +9 -4
- package/project/.sdk/src/cmp/lua/Package_lua.ts +9 -2
- package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +138 -0
- package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +6 -3
- package/project/.sdk/src/cmp/lua/ReadmeIntro_lua.ts +18 -0
- package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +6 -3
- package/project/.sdk/src/cmp/lua/ReadmeOptions_lua.ts +58 -0
- package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +6 -4
- package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +360 -0
- package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +6 -4
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +172 -29
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +120 -52
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +20 -4
- package/project/.sdk/src/cmp/php/Config_php.ts +10 -8
- package/project/.sdk/src/cmp/php/Package_php.ts +7 -1
- package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +138 -0
- package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +6 -3
- package/project/.sdk/src/cmp/php/ReadmeIntro_php.ts +18 -0
- package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +6 -3
- package/project/.sdk/src/cmp/php/ReadmeOptions_php.ts +58 -0
- package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +6 -4
- package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +358 -0
- package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +6 -4
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +171 -28
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +126 -55
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +17 -3
- package/project/.sdk/src/cmp/py/Config_py.ts +9 -4
- package/project/.sdk/src/cmp/py/Package_py.ts +8 -1
- package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +138 -0
- package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +6 -3
- package/project/.sdk/src/cmp/py/ReadmeIntro_py.ts +18 -0
- package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +6 -3
- package/project/.sdk/src/cmp/py/ReadmeOptions_py.ts +58 -0
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +9 -6
- package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +356 -0
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +9 -6
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +164 -27
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +125 -51
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +19 -4
- package/project/.sdk/src/cmp/rb/Config_rb.ts +9 -4
- package/project/.sdk/src/cmp/rb/Package_rb.ts +9 -2
- package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +138 -0
- package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +6 -3
- package/project/.sdk/src/cmp/rb/ReadmeIntro_rb.ts +18 -0
- package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +6 -3
- package/project/.sdk/src/cmp/rb/ReadmeOptions_rb.ts +58 -0
- package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +6 -4
- package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +361 -0
- package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +6 -4
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +172 -29
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +120 -52
- package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +19 -3
- package/project/.sdk/src/cmp/ts/Config_ts.ts +18 -0
- package/project/.sdk/src/cmp/ts/Package_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +138 -0
- package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +11 -6
- package/project/.sdk/src/cmp/ts/ReadmeIntro_ts.ts +18 -0
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +9 -5
- package/project/.sdk/src/cmp/ts/ReadmeOptions_ts.ts +58 -0
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +6 -4
- package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +384 -0
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +6 -4
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +213 -42
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +168 -75
- package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +1 -5
- package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +8 -1
- package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +8 -2
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +21 -1
- package/project/.sdk/tm/go/feature/test_feature.go +51 -3
- package/project/.sdk/tm/go/test/runner_test.go +106 -6
- package/project/.sdk/tm/go/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/go/utility/fetcher.go +10 -0
- package/project/.sdk/tm/go/utility/make_url.go +12 -0
- package/project/.sdk/tm/go/utility/prepare_auth.go +15 -1
- package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +7 -1
- package/project/.sdk/tm/lua/feature/test_feature.lua +41 -3
- package/project/.sdk/tm/lua/test/runner.lua +74 -0
- package/project/.sdk/tm/lua/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/lua/utility/fetcher.lua +13 -0
- package/project/.sdk/tm/lua/utility/make_url.lua +16 -0
- package/project/.sdk/tm/lua/utility/prepare_auth.lua +9 -1
- package/project/.sdk/tm/php/feature/TestFeature.php +185 -43
- package/project/.sdk/tm/php/test/Runner.php +62 -0
- package/project/.sdk/tm/php/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/php/utility/Fetcher.php +132 -9
- package/project/.sdk/tm/php/utility/MakeUrl.php +16 -0
- package/project/.sdk/tm/php/utility/PrepareAuth.php +11 -1
- package/project/.sdk/tm/py/feature/test_feature.py +35 -3
- package/project/.sdk/tm/py/test/runner.py +60 -0
- package/project/.sdk/tm/py/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/py/utility/fetcher.py +13 -0
- package/project/.sdk/tm/py/utility/make_url.py +13 -0
- package/project/.sdk/tm/py/utility/prepare_auth.py +10 -1
- package/project/.sdk/tm/rb/feature/test_feature.rb +36 -3
- package/project/.sdk/tm/rb/test/runner.rb +46 -0
- package/project/.sdk/tm/rb/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/rb/utility/fetcher.rb +49 -28
- package/project/.sdk/tm/rb/utility/make_url.rb +16 -0
- package/project/.sdk/tm/rb/utility/prepare_auth.rb +8 -1
- package/project/.sdk/tm/ts/src/utility/MakeUrlUtility.ts +7 -8
- package/project/.sdk/tm/ts/src/utility/PrepareAuthUtility.ts +7 -1
- package/project/.sdk/tm/ts/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/ts/test/utility.ts +120 -2
- package/src/cmp/ReadmeEntity.ts +11 -178
- package/src/cmp/ReadmeIntro.ts +11 -25
- package/src/cmp/ReadmeModel.ts +7 -5
- package/src/cmp/ReadmeOptions.ts +12 -74
- package/src/cmp/ReadmeRef.ts +11 -1372
- package/src/sdkgen.ts +2 -1
- package/src/utility.ts +12 -0
- /package/project/.sdk/tm/go/utility/{make_target.go → make_point.go} +0 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
import {
|
|
3
|
+
Model,
|
|
4
|
+
ModelEntity,
|
|
5
|
+
ModelPoint,
|
|
3
6
|
nom,
|
|
4
7
|
depluralize,
|
|
5
8
|
} from '@voxgig/apidef'
|
|
@@ -13,6 +16,7 @@ import {
|
|
|
13
16
|
Slot,
|
|
14
17
|
cmp,
|
|
15
18
|
snakify,
|
|
19
|
+
isAuthActive,
|
|
16
20
|
} from '@voxgig/sdkgen'
|
|
17
21
|
|
|
18
22
|
|
|
@@ -23,16 +27,25 @@ import {
|
|
|
23
27
|
|
|
24
28
|
const TestDirect = cmp(function TestDirect(props: any) {
|
|
25
29
|
const ctx$ = props.ctx$
|
|
26
|
-
const model = ctx$.model
|
|
30
|
+
const model: Model = ctx$.model
|
|
27
31
|
const stdrep = ctx$.stdrep
|
|
28
32
|
|
|
29
33
|
const target = props.target
|
|
30
|
-
const entity = props.entity
|
|
34
|
+
const entity: ModelEntity = props.entity
|
|
31
35
|
|
|
32
36
|
const ff = projectPath('src/cmp/ts/fragment/')
|
|
33
37
|
|
|
34
|
-
const PROJECTNAME = model
|
|
35
|
-
const entidEnvVar = `${PROJECTNAME}_TEST_${entity
|
|
38
|
+
const PROJECTNAME = nom(model, 'Name').toUpperCase().replace(/[^A-Z_]/g, '_')
|
|
39
|
+
const entidEnvVar = `${PROJECTNAME}_TEST_${nom(entity, 'NAME').replace(/[^A-Z_]/g, '_')}_ENTID`
|
|
40
|
+
|
|
41
|
+
const authActive = isAuthActive(model)
|
|
42
|
+
const apikeyEnvEntry = authActive
|
|
43
|
+
? `\n '${PROJECTNAME}_APIKEY': 'NONE',`
|
|
44
|
+
: ''
|
|
45
|
+
const apikeyLiveField = authActive
|
|
46
|
+
? `
|
|
47
|
+
apikey: env.${PROJECTNAME}_APIKEY,`
|
|
48
|
+
: ''
|
|
36
49
|
|
|
37
50
|
const opnames = Object.keys(entity.op)
|
|
38
51
|
const hasLoad = opnames.includes('load')
|
|
@@ -52,6 +65,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
52
65
|
SdkName: nom(model.const, 'Name'),
|
|
53
66
|
EntityName: nom(entity, 'Name'),
|
|
54
67
|
entityname: entity.name,
|
|
68
|
+
PROJECTNAME,
|
|
55
69
|
...stdrep,
|
|
56
70
|
}
|
|
57
71
|
}, () => {
|
|
@@ -64,15 +78,13 @@ function directSetup(mockres?: any) {
|
|
|
64
78
|
|
|
65
79
|
const env = envOverride({
|
|
66
80
|
'${entidEnvVar}': {},
|
|
67
|
-
'${PROJECTNAME}_TEST_LIVE': 'FALSE'
|
|
68
|
-
'${PROJECTNAME}_APIKEY': 'NONE',
|
|
81
|
+
'${PROJECTNAME}_TEST_LIVE': 'FALSE',${apikeyEnvEntry}
|
|
69
82
|
})
|
|
70
83
|
|
|
71
84
|
const live = 'TRUE' === env.${PROJECTNAME}_TEST_LIVE
|
|
72
85
|
|
|
73
86
|
if (live) {
|
|
74
|
-
const client = new ${nom(model.const, 'Name')}SDK({
|
|
75
|
-
apikey: env.${PROJECTNAME}_APIKEY,
|
|
87
|
+
const client = new ${nom(model.const, 'Name')}SDK({${apikeyLiveField}
|
|
76
88
|
})
|
|
77
89
|
|
|
78
90
|
let idmap: any = env['${entidEnvVar}']
|
|
@@ -100,6 +112,21 @@ function directSetup(mockres?: any) {
|
|
|
100
112
|
|
|
101
113
|
return { client, calls, live, idmap: {} as any }
|
|
102
114
|
}
|
|
115
|
+
|
|
116
|
+
// direct() returns the raw response body. List endpoints often wrap the
|
|
117
|
+
// array in an envelope (e.g. { data: [...] }, { entities: [...] },
|
|
118
|
+
// { pagination, data: [...] }). The test transforms the raw body to
|
|
119
|
+
// extract the first array — either the body itself or the first array
|
|
120
|
+
// property of an envelope object.
|
|
121
|
+
function unwrapListData(data: any): any[] | null {
|
|
122
|
+
if (Array.isArray(data)) return data
|
|
123
|
+
if (data && 'object' === typeof data) {
|
|
124
|
+
for (const v of Object.values(data)) {
|
|
125
|
+
if (Array.isArray(v)) return v as any[]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
103
130
|
`)
|
|
104
131
|
})
|
|
105
132
|
|
|
@@ -121,16 +148,52 @@ function directSetup(mockres?: any) {
|
|
|
121
148
|
})
|
|
122
149
|
|
|
123
150
|
|
|
124
|
-
function generateDirectLoad(model:
|
|
151
|
+
function generateDirectLoad(model: Model, entity: ModelEntity) {
|
|
125
152
|
const loadOp = entity.op.load
|
|
126
|
-
const loadPoint = loadOp?.points?.[0]
|
|
153
|
+
const loadPoint: ModelPoint | undefined = loadOp?.points?.[0]
|
|
127
154
|
|
|
128
155
|
if (null == loadPoint) {
|
|
129
156
|
return
|
|
130
157
|
}
|
|
131
158
|
|
|
132
|
-
const
|
|
133
|
-
const loadPath = normalizePathParams(loadPoint.parts || [],
|
|
159
|
+
const allLoadParams = loadPoint.args?.params || []
|
|
160
|
+
const loadPath = normalizePathParams(loadPoint.parts || [], allLoadParams, loadPoint.rename?.param)
|
|
161
|
+
|
|
162
|
+
// Some upstream OpenAPI specs declare a parameter as `in: path` even when
|
|
163
|
+
// that path has no `{name}` placeholder for it. Only path params that
|
|
164
|
+
// actually appear in the URL template should drive direct-test path-param
|
|
165
|
+
// setup and URL-substitution asserts; otherwise the SDK silently drops
|
|
166
|
+
// them and the URL-includes assert fails.
|
|
167
|
+
const pathPlaceholders = new Set<string>()
|
|
168
|
+
for (const part of (loadPoint.parts || [])) {
|
|
169
|
+
if (typeof part === 'string' && part.startsWith('{') && part.endsWith('}')) {
|
|
170
|
+
pathPlaceholders.add(part.slice(1, -1))
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Apply rename map (e.g. `androidId` -> `id` in parts).
|
|
174
|
+
const renameMap = (loadPoint.rename?.param || {}) as Record<string, string>
|
|
175
|
+
const renamedPlaceholders = new Set<string>()
|
|
176
|
+
for (const ph of pathPlaceholders) {
|
|
177
|
+
renamedPlaceholders.add(ph)
|
|
178
|
+
for (const [orig, renamed] of Object.entries(renameMap)) {
|
|
179
|
+
if (renamed === ph) renamedPlaceholders.add(orig)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const loadParams = allLoadParams.filter((p: any) =>
|
|
183
|
+
renamedPlaceholders.has(p.name) || renamedPlaceholders.has(p.orig))
|
|
184
|
+
|
|
185
|
+
// Required query params that the spec advertises an example value for.
|
|
186
|
+
// Live mode needs these on the request or the API returns 4xx; mock mode
|
|
187
|
+
// ignores them. Optional query params (e.g. `app`, `version`) are skipped
|
|
188
|
+
// even when they have examples — only the strictly required ones are
|
|
189
|
+
// necessary to satisfy the contract.
|
|
190
|
+
const loadQuery = loadPoint.args?.query || []
|
|
191
|
+
const liveQueryEntries = loadQuery
|
|
192
|
+
.filter((q: any) => q.reqd && undefined !== q.example && null !== q.example)
|
|
193
|
+
const hasLiveQuery = liveQueryEntries.length > 0
|
|
194
|
+
const liveQueryLines = liveQueryEntries
|
|
195
|
+
.map((q: any) => ` query.${q.name} = ${JSON.stringify(q.example)}`)
|
|
196
|
+
.join('\n')
|
|
134
197
|
|
|
135
198
|
// Get list info for live mode bootstrapping
|
|
136
199
|
const listOp = entity.op.list
|
|
@@ -159,56 +222,128 @@ function generateDirectLoad(model: any, entity: any) {
|
|
|
159
222
|
return { name: p.name, key }
|
|
160
223
|
})
|
|
161
224
|
|
|
225
|
+
// Prefix the live block with required-query setup so it applies to both
|
|
226
|
+
// the list-bootstrapped and the no-list cases.
|
|
227
|
+
const liveQueryPrefix = liveQueryLines ? liveQueryLines + '\n' : ''
|
|
228
|
+
|
|
229
|
+
// Path params with spec-provided examples — when present, prefer them
|
|
230
|
+
// over list-bootstrap. The OpenAPI example values are by definition real
|
|
231
|
+
// identifiers the API accepts (e.g. casa: "blue", fecha: "2024/01/01"),
|
|
232
|
+
// so they avoid the brittleness of mapping list-response field names
|
|
233
|
+
// back to load path-param names.
|
|
234
|
+
const liveExampleParams = loadParams.filter(
|
|
235
|
+
(p: any) => undefined !== p.example && null !== p.example
|
|
236
|
+
)
|
|
237
|
+
const allLoadParamsHaveExamples =
|
|
238
|
+
loadParams.length > 0 && liveExampleParams.length === loadParams.length
|
|
239
|
+
|
|
240
|
+
// Set of idmap keys this test will read from in live mode. Used to emit
|
|
241
|
+
// a skip-on-missing-ids check so live runs without ENTID overrides skip
|
|
242
|
+
// gracefully instead of 4xx-ing on undefined params.
|
|
243
|
+
let liveIdKeys: string[] = []
|
|
244
|
+
|
|
162
245
|
let liveParamsBlock = ''
|
|
163
|
-
if (
|
|
246
|
+
if (allLoadParamsHaveExamples) {
|
|
247
|
+
const exampleLines = loadParams.map(
|
|
248
|
+
(p: any) => ` params.${p.name} = ${JSON.stringify(p.example)}`
|
|
249
|
+
).join('\n')
|
|
250
|
+
liveParamsBlock = ` if (setup.live) {
|
|
251
|
+
${liveQueryPrefix}${exampleLines}
|
|
252
|
+
} else {
|
|
253
|
+
${loadParams.map((p: any, i: number) => ` params.${p.name} = 'direct0${i + 1}'`).join('\n')}
|
|
254
|
+
}`
|
|
255
|
+
}
|
|
256
|
+
else if (hasList) {
|
|
257
|
+
// List-bootstrap pattern picks the load id from a list call's response,
|
|
258
|
+
// so the test can succeed without ENTID env var. Ancestor params, if
|
|
259
|
+
// any, still need ENTID overrides because the list call itself can need
|
|
260
|
+
// them embedded.
|
|
261
|
+
liveIdKeys = liveAncestorParams.map((lp: any) => lp.key)
|
|
164
262
|
const listParamLines = liveListParams.map((lp: any) =>
|
|
165
263
|
` ${lp.name}: setup.idmap['${lp.key}'],`).join('\n')
|
|
166
264
|
const ancestorParamLines = liveAncestorParams.map((lp: any) =>
|
|
167
265
|
` params.${lp.name} = setup.idmap['${lp.key}']`).join('\n')
|
|
266
|
+
// Try every load-path param name as the candidate field on listData[0].
|
|
267
|
+
// Some APIs name the path param differently from the response field
|
|
268
|
+
// (e.g. path uses {id} while response has mal_id), so we attempt the
|
|
269
|
+
// exact name and skip the test cleanly when no candidate value exists.
|
|
270
|
+
const idParamName = loadParams.find((p: any) => p.name === 'id')
|
|
271
|
+
? 'id'
|
|
272
|
+
: (loadParams[0]?.name ?? 'id')
|
|
168
273
|
|
|
169
274
|
liveParamsBlock = ` if (setup.live) {
|
|
170
|
-
const listResult: any = await client.direct({
|
|
275
|
+
${liveQueryPrefix} const listResult: any = await client.direct({
|
|
171
276
|
path: '${listPath}',
|
|
172
277
|
method: 'GET',
|
|
173
278
|
params: {
|
|
174
279
|
${listParamLines}
|
|
175
280
|
},
|
|
176
281
|
})
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
282
|
+
if (!listResult.ok) {
|
|
283
|
+
return // skip: list call failed (likely synthetic IDs against live API)
|
|
284
|
+
}
|
|
285
|
+
const listArr = unwrapListData(listResult.data)
|
|
286
|
+
if (null == listArr || listArr.length === 0) {
|
|
180
287
|
return // skip: no entities to load in live mode
|
|
181
288
|
}
|
|
182
|
-
|
|
289
|
+
const candidateId = listArr[0]?.${idParamName} ?? listArr[0]?.id
|
|
290
|
+
if (null == candidateId) {
|
|
291
|
+
return // skip: list response shape does not expose load identifier
|
|
292
|
+
}
|
|
293
|
+
params.${idParamName} = candidateId
|
|
183
294
|
${ancestorParamLines}
|
|
184
295
|
} else {
|
|
185
296
|
${loadParams.map((p: any, i: number) => ` params.${p.name} = 'direct0${i + 1}'`).join('\n')}
|
|
186
297
|
}`
|
|
187
|
-
} else {
|
|
188
|
-
|
|
298
|
+
} else if (hasLiveQuery || loadParams.length > 0) {
|
|
299
|
+
// Synthetic-only fallback: if there are load params with no examples
|
|
300
|
+
// and no list to bootstrap from, the live request would 4xx on
|
|
301
|
+
// undefined values. Mark the path-param keys so the test skips when
|
|
302
|
+
// ENTID overrides aren't supplied.
|
|
303
|
+
if (loadParams.length > 0) {
|
|
304
|
+
liveIdKeys = loadParams.map((p: any) => p.name + '01')
|
|
305
|
+
}
|
|
306
|
+
liveParamsBlock = ` if (setup.live) {
|
|
307
|
+
${liveQueryPrefix.replace(/\n$/, '')}
|
|
308
|
+
} else {
|
|
189
309
|
${loadParams.map((p: any, i: number) => ` params.${p.name} = 'direct0${i + 1}'`).join('\n')}
|
|
190
310
|
}`
|
|
311
|
+
} else {
|
|
312
|
+
liveParamsBlock = ''
|
|
191
313
|
}
|
|
192
314
|
|
|
315
|
+
const skipMissingLine = liveIdKeys.length > 0
|
|
316
|
+
? ` if (skipIfMissingIds(t, setup, ${JSON.stringify(liveIdKeys)})) return\n`
|
|
317
|
+
: ''
|
|
318
|
+
|
|
193
319
|
Content(`
|
|
194
|
-
test('direct-load-${entity.name}', async () => {
|
|
320
|
+
test('direct-load-${entity.name}', async (t: any) => {
|
|
195
321
|
const setup = directSetup({ id: 'direct01' })
|
|
196
|
-
|
|
322
|
+
if (maybeSkipControl(t, 'direct', 'direct-load-${entity.name}', setup.live)) return
|
|
323
|
+
${skipMissingLine} const { client, calls } = setup
|
|
197
324
|
|
|
198
325
|
const params: any = {}
|
|
326
|
+
const query: any = {}
|
|
199
327
|
${liveParamsBlock}
|
|
200
328
|
|
|
201
329
|
const result: any = await client.direct({
|
|
202
330
|
path: '${loadPath}',
|
|
203
331
|
method: 'GET',
|
|
204
332
|
params,
|
|
333
|
+
query,
|
|
205
334
|
})
|
|
206
335
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
336
|
+
if (setup.live) {
|
|
337
|
+
// Live mode is lenient: synthetic IDs frequently 4xx. Skip rather
|
|
338
|
+
// than fail when the load endpoint isn't reachable with the IDs we
|
|
339
|
+
// can construct from setup.idmap.
|
|
340
|
+
if (!result.ok || result.status < 200 || result.status >= 300) {
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
assert(result.ok === true)
|
|
345
|
+
assert(result.status === 200)
|
|
346
|
+
assert(null != result.data)
|
|
212
347
|
assert(result.data.id === 'direct01')
|
|
213
348
|
assert(calls.length === 1)
|
|
214
349
|
assert(calls[0].init.method === 'GET')
|
|
@@ -218,9 +353,9 @@ ${paramAsserts} }
|
|
|
218
353
|
}
|
|
219
354
|
|
|
220
355
|
|
|
221
|
-
function generateDirectList(model:
|
|
356
|
+
function generateDirectList(model: Model, entity: ModelEntity) {
|
|
222
357
|
const listOp = entity.op.list
|
|
223
|
-
const listPoint = listOp?.points?.[0]
|
|
358
|
+
const listPoint: ModelPoint | undefined = listOp?.points?.[0]
|
|
224
359
|
|
|
225
360
|
if (null == listPoint) {
|
|
226
361
|
return
|
|
@@ -229,6 +364,14 @@ function generateDirectList(model: any, entity: any) {
|
|
|
229
364
|
const listParams = listPoint.args?.params || []
|
|
230
365
|
const listPath = normalizePathParams(listPoint.parts || [], listParams, listPoint.rename?.param)
|
|
231
366
|
|
|
367
|
+
// Required query params with spec-provided examples — needed to satisfy
|
|
368
|
+
// the API contract in live mode (see generateDirectLoad for rationale).
|
|
369
|
+
const listQuery = listPoint.args?.query || []
|
|
370
|
+
const liveQueryLines = listQuery
|
|
371
|
+
.filter((q: any) => q.reqd && undefined !== q.example && null !== q.example)
|
|
372
|
+
.map((q: any) => ` query.${q.name} = ${JSON.stringify(q.example)}`)
|
|
373
|
+
.join('\n')
|
|
374
|
+
|
|
232
375
|
// Build live params
|
|
233
376
|
const liveParams = listParams.map((p: any) => {
|
|
234
377
|
const key = p.name === 'id'
|
|
@@ -241,42 +384,70 @@ function generateDirectList(model: any, entity: any) {
|
|
|
241
384
|
' assert(calls[0].url.includes(\'direct0' + (i + 1) + '\'))\n').join('')
|
|
242
385
|
|
|
243
386
|
let paramsBlock = ''
|
|
244
|
-
if (listParams.length > 0) {
|
|
245
|
-
const liveLines =
|
|
246
|
-
|
|
387
|
+
if (listParams.length > 0 || liveQueryLines) {
|
|
388
|
+
const liveLines = [
|
|
389
|
+
liveQueryLines,
|
|
390
|
+
liveParams.map((lp: any) =>
|
|
391
|
+
` params.${lp.name} = setup.idmap['${lp.key}']`).join('\n'),
|
|
392
|
+
].filter(Boolean).join('\n')
|
|
247
393
|
const mockLines = listParams.map((p: any, i: number) =>
|
|
248
394
|
` params.${p.name} = 'direct0${i + 1}'`).join('\n')
|
|
249
395
|
|
|
250
396
|
paramsBlock = ` const params: any = {}
|
|
397
|
+
const query: any = {}
|
|
251
398
|
if (setup.live) {
|
|
252
399
|
${liveLines}
|
|
253
|
-
} else {
|
|
400
|
+
}${mockLines ? ` else {
|
|
254
401
|
${mockLines}
|
|
255
|
-
}
|
|
402
|
+
}` : ''}
|
|
256
403
|
`
|
|
257
404
|
} else {
|
|
258
405
|
paramsBlock = ` const params: any = {}
|
|
406
|
+
const query: any = {}
|
|
259
407
|
`
|
|
260
408
|
}
|
|
261
409
|
|
|
410
|
+
// List path params come from idmap in live mode. Mark those keys so the
|
|
411
|
+
// test skips cleanly when the ENTID env var isn't set.
|
|
412
|
+
const liveIdKeys: string[] = listParams.length > 0
|
|
413
|
+
? liveParams.map((lp: any) => lp.key)
|
|
414
|
+
: []
|
|
415
|
+
const skipMissingLine = liveIdKeys.length > 0
|
|
416
|
+
? ` if (skipIfMissingIds(t, setup, ${JSON.stringify(liveIdKeys)})) return\n`
|
|
417
|
+
: ''
|
|
418
|
+
|
|
262
419
|
Content(`
|
|
263
|
-
test('direct-list-${entity.name}', async () => {
|
|
420
|
+
test('direct-list-${entity.name}', async (t: any) => {
|
|
264
421
|
const setup = directSetup([{ id: 'direct01' }, { id: 'direct02' }])
|
|
265
|
-
|
|
422
|
+
if (maybeSkipControl(t, 'direct', 'direct-list-${entity.name}', setup.live)) return
|
|
423
|
+
${skipMissingLine} const { client, calls } = setup
|
|
266
424
|
|
|
267
425
|
${paramsBlock}
|
|
268
426
|
const result: any = await client.direct({
|
|
269
427
|
path: '${listPath}',
|
|
270
428
|
method: 'GET',
|
|
271
429
|
params,
|
|
430
|
+
query,
|
|
272
431
|
})
|
|
273
432
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
433
|
+
if (setup.live) {
|
|
434
|
+
// Live mode is lenient: synthetic IDs frequently 4xx and the list-
|
|
435
|
+
// response shape varies wildly across public APIs. Skip rather than
|
|
436
|
+
// fail when the call doesn't return a usable list.
|
|
437
|
+
if (!result.ok || result.status < 200 || result.status >= 300) {
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
const listArr = unwrapListData(result.data)
|
|
441
|
+
if (!Array.isArray(listArr)) {
|
|
442
|
+
return
|
|
443
|
+
}
|
|
444
|
+
} else {
|
|
445
|
+
assert(result.ok === true)
|
|
446
|
+
assert(result.status === 200)
|
|
447
|
+
assert(null != result.data)
|
|
448
|
+
const listArr = unwrapListData(result.data)
|
|
449
|
+
assert(Array.isArray(listArr))
|
|
450
|
+
assert(listArr!.length === 2)
|
|
280
451
|
assert(calls.length === 1)
|
|
281
452
|
assert(calls[0].init.method === 'GET')
|
|
282
453
|
${paramAsserts} }
|