@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
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
Slot,
|
|
30
30
|
cmp,
|
|
31
31
|
each,
|
|
32
|
+
isAuthActive,
|
|
32
33
|
} from '@voxgig/sdkgen'
|
|
33
34
|
|
|
34
35
|
|
|
@@ -37,19 +38,38 @@ import {
|
|
|
37
38
|
} from './utility_ts'
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
// GenCtx is the per-language generation context passed to every OpGen.
|
|
42
|
+
// Languages with extra needs (Go's `gomodule`, PHP's `accessor`) extend
|
|
43
|
+
// this shape locally. The signature `(ctx, step, index)` is now uniform
|
|
44
|
+
// across all seven language tracks (Phase 1 of the templates refactor).
|
|
45
|
+
type GenCtx = {
|
|
46
|
+
model: Model
|
|
47
|
+
entity: ModelEntity
|
|
48
|
+
flow: ModelEntityFlow
|
|
49
|
+
PROJUPPER: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type OpGen = (ctx: GenCtx, step: ModelEntityFlowStep, index: number) => void
|
|
41
53
|
|
|
42
54
|
|
|
43
55
|
const TestEntity = cmp(function TestEntity(props: any) {
|
|
44
56
|
const ctx$ = props.ctx$
|
|
45
|
-
const model = ctx$.model
|
|
57
|
+
const model: Model = ctx$.model
|
|
46
58
|
const stdrep = ctx$.stdrep
|
|
47
59
|
|
|
48
60
|
const target = props.target
|
|
49
|
-
const entity = props.entity
|
|
61
|
+
const entity: ModelEntity = props.entity
|
|
50
62
|
|
|
51
63
|
const PROJENVNAME = nom(model.const, 'NAME').replace(/[^A-Z_]/g, '_')
|
|
52
64
|
const ENTENVNAME = nom(entity, 'NAME').replace(/[^A-Z_]/g, '_')
|
|
65
|
+
const authActive = isAuthActive(model)
|
|
66
|
+
const apikeyEnvEntry = authActive
|
|
67
|
+
? `\n '${PROJENVNAME}_APIKEY': 'NONE',`
|
|
68
|
+
: ''
|
|
69
|
+
const apikeyLiveField = authActive
|
|
70
|
+
? `
|
|
71
|
+
apikey: env.${PROJENVNAME}_APIKEY,`
|
|
72
|
+
: ''
|
|
53
73
|
|
|
54
74
|
// TODO: should be a utility function
|
|
55
75
|
const ff = projectPath('src/cmp/ts/fragment/')
|
|
@@ -63,11 +83,13 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
63
83
|
replace: {
|
|
64
84
|
SdkName: nom(model.const, 'Name'),
|
|
65
85
|
EntityName: nom(entity, 'Name'),
|
|
86
|
+
entityname: entity.name,
|
|
87
|
+
PROJECTNAME: PROJENVNAME,
|
|
66
88
|
...stdrep,
|
|
67
89
|
}
|
|
68
90
|
}, () => {
|
|
69
91
|
|
|
70
|
-
const basicflow = getModelPath(model, `main.${KIT}.flow.Basic${entity
|
|
92
|
+
const basicflow = getModelPath(model, `main.${KIT}.flow.Basic${nom(entity, 'Name')}Flow`)
|
|
71
93
|
|
|
72
94
|
const dobasic = basicflow && true === basicflow.active
|
|
73
95
|
|
|
@@ -120,19 +142,26 @@ function basicSetup(extra?: any) {
|
|
|
120
142
|
}]
|
|
121
143
|
})
|
|
122
144
|
|
|
145
|
+
// Detect whether the user provided a real ENTID JSON via env var. The
|
|
146
|
+
// basic flow consumes synthetic IDs from the fixture file; without an
|
|
147
|
+
// override those synthetic IDs reach the live API and 4xx. Surface this
|
|
148
|
+
// to the test so it can skip rather than fail.
|
|
149
|
+
const idmapEnvVal = process.env['${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID']
|
|
150
|
+
const idmapOverridden = null != idmapEnvVal && idmapEnvVal.trim().startsWith('{')
|
|
151
|
+
|
|
123
152
|
const env = envOverride({
|
|
124
153
|
'${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID': idmap,
|
|
125
154
|
'${PROJENVNAME}_TEST_LIVE': 'FALSE',
|
|
126
|
-
'${PROJENVNAME}_TEST_EXPLAIN': 'FALSE'
|
|
127
|
-
'${PROJENVNAME}_APIKEY': 'NONE',
|
|
155
|
+
'${PROJENVNAME}_TEST_EXPLAIN': 'FALSE',${apikeyEnvEntry}
|
|
128
156
|
})
|
|
129
157
|
|
|
130
158
|
idmap = env['${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID']
|
|
131
159
|
|
|
132
|
-
|
|
160
|
+
const live = 'TRUE' === env.${PROJENVNAME}_TEST_LIVE
|
|
161
|
+
|
|
162
|
+
if (live) {
|
|
133
163
|
client = new ${model.Name}SDK(merge([
|
|
134
|
-
{
|
|
135
|
-
apikey: env.${PROJENVNAME}_APIKEY,
|
|
164
|
+
{${apikeyLiveField}
|
|
136
165
|
},
|
|
137
166
|
extra
|
|
138
167
|
]))
|
|
@@ -146,6 +175,8 @@ function basicSetup(extra?: any) {
|
|
|
146
175
|
struct,
|
|
147
176
|
data: entityData,
|
|
148
177
|
explain: 'TRUE' === env.${PROJENVNAME}_TEST_EXPLAIN,
|
|
178
|
+
live,
|
|
179
|
+
syntheticOnly: live && !idmapOverridden,
|
|
149
180
|
now: Date.now(),
|
|
150
181
|
}
|
|
151
182
|
|
|
@@ -160,8 +191,30 @@ function basicSetup(extra?: any) {
|
|
|
160
191
|
(s: any) => s.op === 'create'
|
|
161
192
|
)
|
|
162
193
|
|
|
194
|
+
// The basic test exercises a flow with one or more ops (load,
|
|
195
|
+
// list, create, update, remove, ...). The control file lets users
|
|
196
|
+
// skip per-op for an entity. Since the flow is sequential and
|
|
197
|
+
// dependent (e.g. update needs prior load), skipping ANY op the
|
|
198
|
+
// flow exercises skips the whole basic test.
|
|
199
|
+
const flowOps = Array.from(new Set(
|
|
200
|
+
(basicflow.step as any[]).map((s: any) => s.op).filter(Boolean)
|
|
201
|
+
))
|
|
202
|
+
const flowOpsLiteral = '[' + flowOps.map((o: any) => `'${o}'`).join(', ') + ']'
|
|
203
|
+
|
|
163
204
|
Content(`
|
|
205
|
+
const live = 'TRUE' === process.env.${PROJENVNAME}_TEST_LIVE
|
|
206
|
+
for (const op of ${flowOpsLiteral}) {
|
|
207
|
+
if (maybeSkipControl(t, 'entityOp', '${entity.name}.' + op, live)) return
|
|
208
|
+
}
|
|
209
|
+
|
|
164
210
|
const setup = basicSetup()
|
|
211
|
+
// The basic flow consumes synthetic IDs and field values from the
|
|
212
|
+
// fixture (entity TestData.json). Those don't exist on the live API.
|
|
213
|
+
// Skip live runs unless the user provided a real ENTID env override.
|
|
214
|
+
if (setup.syntheticOnly) {
|
|
215
|
+
t.skip('live entity test uses synthetic IDs from fixture — set ${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID JSON to run live')
|
|
216
|
+
return
|
|
217
|
+
}
|
|
165
218
|
const client = setup.client
|
|
166
219
|
const struct = setup.struct
|
|
167
220
|
|
|
@@ -179,10 +232,15 @@ function basicSetup(extra?: any) {
|
|
|
179
232
|
`)
|
|
180
233
|
}
|
|
181
234
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
235
|
+
const genCtx: GenCtx = {
|
|
236
|
+
model, entity, flow: basicflow, PROJUPPER: PROJENVNAME,
|
|
237
|
+
}
|
|
238
|
+
each(basicflow.step, (step: ModelEntityFlowStep, index: number) => {
|
|
239
|
+
const opgen = GENERATE_OP[step.op]
|
|
240
|
+
if (null != opgen) {
|
|
241
|
+
opgen(genCtx, step, index)
|
|
242
|
+
Content('\n')
|
|
243
|
+
}
|
|
186
244
|
})
|
|
187
245
|
})
|
|
188
246
|
})
|
|
@@ -191,25 +249,20 @@ function basicSetup(extra?: any) {
|
|
|
191
249
|
})
|
|
192
250
|
|
|
193
251
|
|
|
194
|
-
const generateCreate: OpGen = (
|
|
195
|
-
|
|
196
|
-
entity: ModelEntity,
|
|
197
|
-
flow: ModelEntityFlow,
|
|
198
|
-
step: ModelEntityFlowStep,
|
|
199
|
-
index: any
|
|
200
|
-
) => {
|
|
252
|
+
const generateCreate: OpGen = (ctx, step, index) => {
|
|
253
|
+
const { entity, flow } = ctx
|
|
201
254
|
const ref = step.input.ref ?? entity.name + '_ref01'
|
|
202
255
|
const entvar = step.input.entvar ?? ref + '_ent'
|
|
203
256
|
const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
|
|
204
257
|
|
|
205
|
-
const priorSteps =
|
|
206
|
-
const needsEnt = !priorSteps.some(
|
|
258
|
+
const priorSteps = flow.step.slice(0, Number(index))
|
|
259
|
+
const needsEnt = !priorSteps.some(s =>
|
|
207
260
|
['create', 'list', 'load', 'update', 'remove'].includes(s.op))
|
|
208
261
|
|
|
209
|
-
const hasDatvar = priorSteps.some(
|
|
262
|
+
const hasDatvar = priorSteps.some(s => {
|
|
210
263
|
if ('create' === s.op) {
|
|
211
|
-
const priorRef = s.input
|
|
212
|
-
const priorDatvar = s.input
|
|
264
|
+
const priorRef = s.input.ref ?? entity.name + '_ref01'
|
|
265
|
+
const priorDatvar = s.input.datavar ?? (priorRef + '_data' + (s.input.suffix ?? ''))
|
|
213
266
|
return priorDatvar === datavar
|
|
214
267
|
}
|
|
215
268
|
return false
|
|
@@ -235,27 +288,31 @@ const generateCreate: OpGen = (
|
|
|
235
288
|
`)
|
|
236
289
|
})
|
|
237
290
|
|
|
291
|
+
const hasEntIdC = null != entity.id
|
|
292
|
+
|
|
238
293
|
Content(`
|
|
239
294
|
${datavar} = await ${entvar}.create(${datavar})
|
|
240
|
-
assert(null != ${datavar}.id)
|
|
241
295
|
`)
|
|
296
|
+
if (hasEntIdC) {
|
|
297
|
+
Content(` assert(null != ${datavar}.id)
|
|
298
|
+
`)
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
Content(` assert(null != ${datavar})
|
|
302
|
+
`)
|
|
303
|
+
}
|
|
242
304
|
}
|
|
243
305
|
|
|
244
306
|
|
|
245
|
-
const generateList: OpGen = (
|
|
246
|
-
|
|
247
|
-
entity: ModelEntity,
|
|
248
|
-
flow: ModelEntityFlow,
|
|
249
|
-
step: ModelEntityFlowStep,
|
|
250
|
-
index: any
|
|
251
|
-
) => {
|
|
307
|
+
const generateList: OpGen = (ctx, step, index) => {
|
|
308
|
+
const { entity, flow } = ctx
|
|
252
309
|
const ref = step.input.ref ?? entity.name + '_ref01'
|
|
253
310
|
const entvar = step.input.entvar ?? ref + '_ent'
|
|
254
311
|
const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
|
|
255
312
|
const listvar = step.input.listvar ?? (ref + '_list' + (step.input.suffix ?? ''))
|
|
256
313
|
|
|
257
|
-
const priorSteps =
|
|
258
|
-
const needsEnt = !priorSteps.some(
|
|
314
|
+
const priorSteps = flow.step.slice(0, Number(index))
|
|
315
|
+
const needsEnt = !priorSteps.some(s =>
|
|
259
316
|
['create', 'list', 'load', 'update', 'remove'].includes(s.op))
|
|
260
317
|
|
|
261
318
|
Content(`
|
|
@@ -276,12 +333,12 @@ const generateList: OpGen = (
|
|
|
276
333
|
Content(`
|
|
277
334
|
const ${listvar} = await ${entvar}.list(${matchvar})
|
|
278
335
|
`)
|
|
279
|
-
const allSteps =
|
|
336
|
+
const allSteps = flow.step
|
|
280
337
|
for (let vI = 0; vI < step.valid.length; vI++) {
|
|
281
338
|
const validator = step.valid[vI]
|
|
282
339
|
const validRef = validator.def?.ref
|
|
283
|
-
const hasRefData = validRef && allSteps.some(
|
|
284
|
-
((s.input
|
|
340
|
+
const hasRefData = validRef && allSteps.some(s => 'create' === s.op &&
|
|
341
|
+
((s.input.ref ?? entity.name + '_ref01') === validRef))
|
|
285
342
|
|
|
286
343
|
if ('ItemExists' === validator.apply && hasRefData) {
|
|
287
344
|
Content(`
|
|
@@ -297,13 +354,8 @@ const generateList: OpGen = (
|
|
|
297
354
|
}
|
|
298
355
|
|
|
299
356
|
|
|
300
|
-
const generateUpdate: OpGen = (
|
|
301
|
-
|
|
302
|
-
entity: ModelEntity,
|
|
303
|
-
flow: ModelEntityFlow,
|
|
304
|
-
step: ModelEntityFlowStep,
|
|
305
|
-
index: any
|
|
306
|
-
) => {
|
|
357
|
+
const generateUpdate: OpGen = (ctx, step, index) => {
|
|
358
|
+
const { entity, flow } = ctx
|
|
307
359
|
const ref = step.input.ref ?? entity.name + '_ref01'
|
|
308
360
|
const entvar = step.input.entvar ?? ref + '_ent'
|
|
309
361
|
const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
|
|
@@ -311,10 +363,12 @@ const generateUpdate: OpGen = (
|
|
|
311
363
|
const markdefvar = step.input.markdefvar ?? (ref + '_markdef' + (step.input.suffix ?? ''))
|
|
312
364
|
const srcdatavar = step.input.srcdatavar ?? (ref + '_data' + (step.input.suffix ?? ''))
|
|
313
365
|
|
|
314
|
-
const priorSteps =
|
|
315
|
-
const needsEnt = !priorSteps.some(
|
|
366
|
+
const priorSteps = flow.step.slice(0, Number(index))
|
|
367
|
+
const needsEnt = !priorSteps.some(s =>
|
|
316
368
|
['create', 'list', 'load', 'update', 'remove'].includes(s.op))
|
|
317
369
|
|
|
370
|
+
const hasEntIdU = null != entity.id
|
|
371
|
+
|
|
318
372
|
Content(`
|
|
319
373
|
// UPDATE
|
|
320
374
|
`)
|
|
@@ -323,8 +377,11 @@ const generateUpdate: OpGen = (
|
|
|
323
377
|
`)
|
|
324
378
|
}
|
|
325
379
|
Content(` const ${datavar}: any = {}
|
|
326
|
-
${datavar}.id = ${srcdatavar}.id
|
|
327
380
|
`)
|
|
381
|
+
if (hasEntIdU) {
|
|
382
|
+
Content(` ${datavar}.id = ${srcdatavar}.id
|
|
383
|
+
`)
|
|
384
|
+
}
|
|
328
385
|
|
|
329
386
|
each(step.data, (mi: any) => {
|
|
330
387
|
if ('id' !== mi.key$) {
|
|
@@ -348,8 +405,15 @@ const generateUpdate: OpGen = (
|
|
|
348
405
|
|
|
349
406
|
Content(`
|
|
350
407
|
const ${resdatavar} = await ${entvar}.update(${datavar})
|
|
351
|
-
assert(${resdatavar}.id === ${datavar}.id)
|
|
352
408
|
`)
|
|
409
|
+
if (hasEntIdU) {
|
|
410
|
+
Content(` assert(${resdatavar}.id === ${datavar}.id)
|
|
411
|
+
`)
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
Content(` assert(null != ${resdatavar})
|
|
415
|
+
`)
|
|
416
|
+
}
|
|
353
417
|
|
|
354
418
|
for (let sI = 0; sI < step.spec.length; sI++) {
|
|
355
419
|
const spec = step.spec[sI]
|
|
@@ -363,37 +427,57 @@ const generateUpdate: OpGen = (
|
|
|
363
427
|
}
|
|
364
428
|
|
|
365
429
|
|
|
366
|
-
const generateLoad: OpGen = (
|
|
367
|
-
|
|
368
|
-
entity: ModelEntity,
|
|
369
|
-
flow: ModelEntityFlow,
|
|
370
|
-
step: ModelEntityFlowStep,
|
|
371
|
-
index: any
|
|
372
|
-
) => {
|
|
430
|
+
const generateLoad: OpGen = (ctx, step, index) => {
|
|
431
|
+
const { entity, flow } = ctx
|
|
373
432
|
const ref = step.input.ref ?? entity.name + '_ref01'
|
|
374
433
|
const entvar = step.input.entvar ?? ref + '_ent'
|
|
375
434
|
const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
|
|
376
435
|
const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
|
|
377
436
|
const srcdatavar = step.input.srcdatavar ?? (ref + '_data' + (step.input.suffix ?? ''))
|
|
378
437
|
|
|
379
|
-
const priorSteps =
|
|
380
|
-
const hasEntVar = priorSteps.some(
|
|
438
|
+
const priorSteps = flow.step.slice(0, Number(index))
|
|
439
|
+
const hasEntVar = priorSteps.some(s =>
|
|
381
440
|
['create', 'list', 'load', 'update', 'remove'].includes(s.op))
|
|
382
441
|
|
|
383
442
|
// Check if srcdatavar was declared by a prior create step or by the
|
|
384
443
|
// preamble bootstrap (which runs when the flow has no create step)
|
|
385
|
-
const flowHasCreate =
|
|
444
|
+
const flowHasCreate = flow.step.some(s => s.op === 'create')
|
|
386
445
|
const preambleRef = entity.name + '_ref01'
|
|
387
446
|
const hasSrcData = (!flowHasCreate && srcdatavar === preambleRef + '_data') ||
|
|
388
|
-
priorSteps.some(
|
|
447
|
+
priorSteps.some(s => {
|
|
389
448
|
if ('create' === s.op) {
|
|
390
|
-
const priorRef = s.input
|
|
391
|
-
const priorDatvar = s.input
|
|
449
|
+
const priorRef = s.input.ref ?? entity.name + '_ref01'
|
|
450
|
+
const priorDatvar = s.input.datavar ?? (priorRef + '_data' + (s.input.suffix ?? ''))
|
|
392
451
|
return priorDatvar === srcdatavar
|
|
393
452
|
}
|
|
394
453
|
return false
|
|
395
454
|
})
|
|
396
455
|
|
|
456
|
+
const hasEntId = null != entity.id
|
|
457
|
+
|
|
458
|
+
// When the entity has no id model field but the load operation requires
|
|
459
|
+
// path parameters (e.g. cotizacion needs {casa}/{fecha}), calling
|
|
460
|
+
// load({}) leaves the URL with literal {param} placeholders and the live
|
|
461
|
+
// API returns 404 HTML, which the SDK then fails to parse as JSON. There
|
|
462
|
+
// is no synthetic identifier to substitute, so skip emitting the load
|
|
463
|
+
// step's call in that case — but still declare the entity-var if no
|
|
464
|
+
// prior step has, so that later flow steps (e.g. remove) referencing
|
|
465
|
+
// ${entvar} compile.
|
|
466
|
+
const loadOp = entity.op?.load
|
|
467
|
+
const loadPoint = loadOp?.points?.[0]
|
|
468
|
+
const loadPathParams = loadPoint?.args?.params || []
|
|
469
|
+
const loadHasRequiredParams = loadPathParams.some((p: any) => p.reqd !== false)
|
|
470
|
+
if (!hasEntId && loadHasRequiredParams) {
|
|
471
|
+
if (!hasEntVar) {
|
|
472
|
+
Content(`
|
|
473
|
+
// LOAD: skipped — no entity id field and load requires path params.
|
|
474
|
+
// Entity-var is declared here so later flow steps still compile.
|
|
475
|
+
const ${entvar} = client.${nom(entity, 'Name')}()
|
|
476
|
+
`)
|
|
477
|
+
}
|
|
478
|
+
return
|
|
479
|
+
}
|
|
480
|
+
|
|
397
481
|
Content(`
|
|
398
482
|
// LOAD
|
|
399
483
|
`)
|
|
@@ -401,34 +485,39 @@ const generateLoad: OpGen = (
|
|
|
401
485
|
Content(` const ${entvar} = client.${nom(entity, 'Name')}()
|
|
402
486
|
`)
|
|
403
487
|
}
|
|
404
|
-
if (!hasSrcData) {
|
|
488
|
+
if (!hasSrcData && hasEntId) {
|
|
405
489
|
Content(` const ${srcdatavar} = Object.values(setup.data.existing.${entity.name})[0] as any
|
|
406
490
|
`)
|
|
407
491
|
}
|
|
408
|
-
|
|
492
|
+
if (hasEntId) {
|
|
493
|
+
Content(` const ${matchvar}: any = {}
|
|
409
494
|
${matchvar}.id = ${srcdatavar}.id
|
|
410
495
|
const ${datavar} = await ${entvar}.load(${matchvar})
|
|
411
496
|
assert(${datavar}.id === ${srcdatavar}.id)
|
|
412
497
|
`)
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
Content(` const ${matchvar}: any = {}
|
|
501
|
+
const ${datavar} = await ${entvar}.load(${matchvar})
|
|
502
|
+
assert(null != ${datavar})
|
|
503
|
+
`)
|
|
504
|
+
}
|
|
413
505
|
}
|
|
414
506
|
|
|
415
507
|
|
|
416
|
-
const generateRemove: OpGen = (
|
|
417
|
-
|
|
418
|
-
entity: ModelEntity,
|
|
419
|
-
flow: ModelEntityFlow,
|
|
420
|
-
step: ModelEntityFlowStep,
|
|
421
|
-
index: any
|
|
422
|
-
) => {
|
|
508
|
+
const generateRemove: OpGen = (ctx, step, index) => {
|
|
509
|
+
const { entity, flow } = ctx
|
|
423
510
|
const ref = step.input.ref ?? entity.name + '_ref01'
|
|
424
511
|
const entvar = step.input.entvar ?? ref + '_ent'
|
|
425
512
|
const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
|
|
426
513
|
const srcdatavar = step.input.srcdatavar ?? (ref + '_data')
|
|
427
514
|
|
|
428
|
-
const priorSteps =
|
|
429
|
-
const needsEnt = !priorSteps.some(
|
|
515
|
+
const priorSteps = flow.step.slice(0, Number(index))
|
|
516
|
+
const needsEnt = !priorSteps.some(s =>
|
|
430
517
|
['create', 'list', 'load', 'update', 'remove'].includes(s.op))
|
|
431
518
|
|
|
519
|
+
const hasEntIdR = null != entity.id
|
|
520
|
+
|
|
432
521
|
Content(`
|
|
433
522
|
// REMOVE
|
|
434
523
|
`)
|
|
@@ -437,8 +526,12 @@ const generateRemove: OpGen = (
|
|
|
437
526
|
`)
|
|
438
527
|
}
|
|
439
528
|
Content(` const ${matchvar}: any = {}
|
|
440
|
-
|
|
441
|
-
|
|
529
|
+
`)
|
|
530
|
+
if (hasEntIdR) {
|
|
531
|
+
Content(` ${matchvar}.id = ${srcdatavar}.id
|
|
532
|
+
`)
|
|
533
|
+
}
|
|
534
|
+
Content(` await ${entvar}.remove(${matchvar})
|
|
442
535
|
`)
|
|
443
536
|
}
|
|
444
537
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const envlocal = __dirname + '/../../../.env.local'
|
|
3
3
|
require('dotenv').config({ quiet: true, path: [envlocal] })
|
|
4
4
|
|
|
5
|
-
import { test, describe } from 'node:test'
|
|
5
|
+
import { test, describe, afterEach } from 'node:test'
|
|
6
6
|
import assert from 'node:assert'
|
|
7
7
|
|
|
8
8
|
|
|
@@ -10,11 +10,18 @@ import { ProjectNameSDK } from '../../..'
|
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
envOverride,
|
|
13
|
+
liveDelay,
|
|
14
|
+
maybeSkipControl,
|
|
15
|
+
skipIfMissingIds,
|
|
13
16
|
} from '../../utility'
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
describe('EntityNameDirect', async () => {
|
|
17
20
|
|
|
21
|
+
// Per-test live pacing. Delay is read from sdk-test-control.json's
|
|
22
|
+
// `test.live.delayMs`; only sleeps when PROJECTNAME_TEST_LIVE=TRUE.
|
|
23
|
+
afterEach(liveDelay('PROJECTNAME_TEST_LIVE'))
|
|
24
|
+
|
|
18
25
|
test('direct-exists', async () => {
|
|
19
26
|
const sdk = new ProjectNameSDK({
|
|
20
27
|
system: { fetch: async () => ({}) }
|
|
@@ -5,7 +5,7 @@ require('dotenv').config({ quiet: true, path: [envlocal] })
|
|
|
5
5
|
import Path from 'node:path'
|
|
6
6
|
import * as Fs from 'node:fs'
|
|
7
7
|
|
|
8
|
-
import { test, describe } from 'node:test'
|
|
8
|
+
import { test, describe, afterEach } from 'node:test'
|
|
9
9
|
import assert from 'node:assert'
|
|
10
10
|
|
|
11
11
|
|
|
@@ -13,16 +13,22 @@ import { ProjectNameSDK, BaseFeature, stdutil } from '../../..'
|
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
15
|
envOverride,
|
|
16
|
+
liveDelay,
|
|
16
17
|
makeCtrl,
|
|
17
18
|
makeMatch,
|
|
18
19
|
makeReqdata,
|
|
19
20
|
makeStepData,
|
|
20
21
|
makeValid,
|
|
22
|
+
maybeSkipControl,
|
|
21
23
|
} from '../../utility'
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
describe('EntityNameEntity', async () => {
|
|
25
27
|
|
|
28
|
+
// Per-test live pacing. Delay is read from sdk-test-control.json's
|
|
29
|
+
// `test.live.delayMs`; only sleeps when PROJECTNAME_TEST_LIVE=TRUE.
|
|
30
|
+
afterEach(liveDelay('PROJECTNAME_TEST_LIVE'))
|
|
31
|
+
|
|
26
32
|
test('instance', async () => {
|
|
27
33
|
const testsdk = ProjectNameSDK.test()
|
|
28
34
|
const ent = testsdk.EntityName()
|
|
@@ -30,7 +36,7 @@ describe('EntityNameEntity', async () => {
|
|
|
30
36
|
})
|
|
31
37
|
|
|
32
38
|
|
|
33
|
-
test('basic', async () => {
|
|
39
|
+
test('basic', async (t) => {
|
|
34
40
|
// <[SLOT:basic]>
|
|
35
41
|
})
|
|
36
42
|
})
|
|
@@ -162,7 +162,27 @@ class ProjectNameSDK {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
const status = fetched.status
|
|
165
|
-
|
|
165
|
+
|
|
166
|
+
// No body responses (204 No Content, 304 Not Modified) and explicit
|
|
167
|
+
// zero content-length must skip JSON parsing — fetched.json() would
|
|
168
|
+
// throw `Unexpected end of JSON input` on an empty body.
|
|
169
|
+
const headers = fetched.headers
|
|
170
|
+
const contentLength = headers && 'function' === typeof headers.get
|
|
171
|
+
? headers.get('content-length')
|
|
172
|
+
: (headers || {})['content-length']
|
|
173
|
+
const noBody = 204 === status || 304 === status || '0' === String(contentLength)
|
|
174
|
+
|
|
175
|
+
let json: any = undefined
|
|
176
|
+
if (!noBody) {
|
|
177
|
+
try {
|
|
178
|
+
json = 'function' === typeof fetched.json ? await fetched.json() : fetched.json
|
|
179
|
+
}
|
|
180
|
+
catch (parseErr) {
|
|
181
|
+
// Body wasn't valid JSON — surface the raw response rather than
|
|
182
|
+
// throwing. data stays undefined; callers can inspect status/headers.
|
|
183
|
+
json = undefined
|
|
184
|
+
}
|
|
185
|
+
}
|
|
166
186
|
|
|
167
187
|
return {
|
|
168
188
|
ok: status >= 200 && status < 300,
|
|
@@ -69,8 +69,28 @@ func (f *TestFeature) Init(ctx *core.Context, options map[string]any) {
|
|
|
69
69
|
entmap = map[string]any{}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
// For single-entity ops (load, remove) with an empty explicit match,
|
|
73
|
+
// fall back to the id the entity client already knows from a prior
|
|
74
|
+
// create/load (in ctx.Match / ctx.Data). Mirrors the TS mock where
|
|
75
|
+
// param() resolves the id from that accumulated state.
|
|
76
|
+
resolveMatch := func(explicit map[string]any) map[string]any {
|
|
77
|
+
if len(explicit) > 0 {
|
|
78
|
+
return explicit
|
|
79
|
+
}
|
|
80
|
+
for _, src := range []any{ctx.Match, ctx.Data} {
|
|
81
|
+
if src == nil {
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
v := vs.GetProp(src, "id")
|
|
85
|
+
if v != nil && v != "__UNDEFINED__" {
|
|
86
|
+
return map[string]any{"id": v}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return map[string]any{}
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
if op.Name == "load" {
|
|
73
|
-
args := self.buildArgs(ctx, op, ctx.Reqmatch)
|
|
93
|
+
args := self.buildArgs(ctx, op, resolveMatch(ctx.Reqmatch))
|
|
74
94
|
found := vs.Select(entmap, args)
|
|
75
95
|
ent := vs.GetElem(found, 0)
|
|
76
96
|
if ent == nil {
|
|
@@ -91,9 +111,37 @@ func (f *TestFeature) Init(ctx *core.Context, options map[string]any) {
|
|
|
91
111
|
out := vs.Clone(found)
|
|
92
112
|
return respond(200, out, nil), nil
|
|
93
113
|
} else if op.Name == "update" {
|
|
94
|
-
|
|
114
|
+
// Match the existing entity by id only (or its alias). Reqdata
|
|
115
|
+
// also contains the new field values, which would otherwise
|
|
116
|
+
// cause Select to filter out the entity we want to update.
|
|
117
|
+
// Falls back to first entity when no match found, mirroring
|
|
118
|
+
// the TS mock.
|
|
119
|
+
updateMatch := map[string]any{}
|
|
120
|
+
if ctx.Reqdata != nil {
|
|
121
|
+
if v, has := ctx.Reqdata["id"]; has {
|
|
122
|
+
updateMatch["id"] = v
|
|
123
|
+
}
|
|
124
|
+
if op.Alias != nil {
|
|
125
|
+
if aliasIdRaw := vs.GetProp(op.Alias, "id"); aliasIdRaw != nil {
|
|
126
|
+
if aliasId, ok := aliasIdRaw.(string); ok {
|
|
127
|
+
if v, has := ctx.Reqdata[aliasId]; has {
|
|
128
|
+
updateMatch[aliasId] = v
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
args := self.buildArgs(ctx, op, updateMatch)
|
|
95
135
|
found := vs.Select(entmap, args)
|
|
96
136
|
ent := vs.GetElem(found, 0)
|
|
137
|
+
if ent == nil && entmap != nil {
|
|
138
|
+
for _, e := range entmap {
|
|
139
|
+
if _, ok := e.(map[string]any); ok {
|
|
140
|
+
ent = e
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
97
145
|
if ent == nil {
|
|
98
146
|
return respond(404, nil, map[string]any{"statusText": "Not found"}), nil
|
|
99
147
|
}
|
|
@@ -109,7 +157,7 @@ func (f *TestFeature) Init(ctx *core.Context, options map[string]any) {
|
|
|
109
157
|
out := vs.Clone(ent)
|
|
110
158
|
return respond(200, out, nil), nil
|
|
111
159
|
} else if op.Name == "remove" {
|
|
112
|
-
args := self.buildArgs(ctx, op, ctx.Reqmatch)
|
|
160
|
+
args := self.buildArgs(ctx, op, resolveMatch(ctx.Reqmatch))
|
|
113
161
|
found := vs.Select(entmap, args)
|
|
114
162
|
ent := vs.GetElem(found, 0)
|
|
115
163
|
if ent == nil {
|