@voxgig/sdkgen 0.44.0 → 0.45.0

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.
Files changed (113) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/ReadmeEntity.js +9 -153
  3. package/dist/cmp/ReadmeEntity.js.map +1 -1
  4. package/dist/cmp/ReadmeIntro.js +9 -14
  5. package/dist/cmp/ReadmeIntro.js.map +1 -1
  6. package/dist/cmp/ReadmeModel.js +6 -4
  7. package/dist/cmp/ReadmeModel.js.map +1 -1
  8. package/dist/cmp/ReadmeOptions.js +9 -61
  9. package/dist/cmp/ReadmeOptions.js.map +1 -1
  10. package/dist/cmp/ReadmeRef.js +10 -1328
  11. package/dist/cmp/ReadmeRef.js.map +1 -1
  12. package/dist/sdkgen.d.ts +2 -2
  13. package/dist/sdkgen.js +2 -1
  14. package/dist/sdkgen.js.map +1 -1
  15. package/dist/utility.d.ts +2 -1
  16. package/dist/utility.js +9 -0
  17. package/dist/utility.js.map +1 -1
  18. package/package.json +1 -1
  19. package/project/.sdk/src/cmp/go/Config_go.ts +9 -4
  20. package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +138 -0
  21. package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +6 -3
  22. package/project/.sdk/src/cmp/go/ReadmeIntro_go.ts +18 -0
  23. package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +6 -3
  24. package/project/.sdk/src/cmp/go/ReadmeOptions_go.ts +58 -0
  25. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +11 -7
  26. package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +354 -0
  27. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +6 -4
  28. package/project/.sdk/src/cmp/go/TestDirect_go.ts +18 -8
  29. package/project/.sdk/src/cmp/go/TestEntity_go.ts +105 -54
  30. package/project/.sdk/src/cmp/js/Config_js.ts +18 -0
  31. package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +138 -0
  32. package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +11 -6
  33. package/project/.sdk/src/cmp/js/ReadmeIntro_js.ts +18 -0
  34. package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +6 -3
  35. package/project/.sdk/src/cmp/js/ReadmeOptions_js.ts +58 -0
  36. package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +6 -4
  37. package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +384 -0
  38. package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +6 -4
  39. package/project/.sdk/src/cmp/js/TestDirect_js.ts +23 -12
  40. package/project/.sdk/src/cmp/js/TestEntity_js.ts +107 -74
  41. package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +1 -5
  42. package/project/.sdk/src/cmp/lua/Config_lua.ts +9 -4
  43. package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +138 -0
  44. package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +6 -3
  45. package/project/.sdk/src/cmp/lua/ReadmeIntro_lua.ts +18 -0
  46. package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +6 -3
  47. package/project/.sdk/src/cmp/lua/ReadmeOptions_lua.ts +58 -0
  48. package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +6 -4
  49. package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +360 -0
  50. package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +6 -4
  51. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +18 -8
  52. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +95 -51
  53. package/project/.sdk/src/cmp/php/Config_php.ts +10 -8
  54. package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +138 -0
  55. package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +6 -3
  56. package/project/.sdk/src/cmp/php/ReadmeIntro_php.ts +18 -0
  57. package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +6 -3
  58. package/project/.sdk/src/cmp/php/ReadmeOptions_php.ts +58 -0
  59. package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +6 -4
  60. package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +358 -0
  61. package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +6 -4
  62. package/project/.sdk/src/cmp/php/TestDirect_php.ts +18 -8
  63. package/project/.sdk/src/cmp/php/TestEntity_php.ts +101 -54
  64. package/project/.sdk/src/cmp/py/Config_py.ts +9 -4
  65. package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +138 -0
  66. package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +6 -3
  67. package/project/.sdk/src/cmp/py/ReadmeIntro_py.ts +18 -0
  68. package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +6 -3
  69. package/project/.sdk/src/cmp/py/ReadmeOptions_py.ts +58 -0
  70. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +9 -6
  71. package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +356 -0
  72. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +9 -6
  73. package/project/.sdk/src/cmp/py/TestDirect_py.ts +18 -8
  74. package/project/.sdk/src/cmp/py/TestEntity_py.ts +100 -50
  75. package/project/.sdk/src/cmp/rb/Config_rb.ts +9 -4
  76. package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +138 -0
  77. package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +6 -3
  78. package/project/.sdk/src/cmp/rb/ReadmeIntro_rb.ts +18 -0
  79. package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +6 -3
  80. package/project/.sdk/src/cmp/rb/ReadmeOptions_rb.ts +58 -0
  81. package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +6 -4
  82. package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +361 -0
  83. package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +6 -4
  84. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +18 -8
  85. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +95 -51
  86. package/project/.sdk/src/cmp/ts/Config_ts.ts +18 -0
  87. package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +138 -0
  88. package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +11 -6
  89. package/project/.sdk/src/cmp/ts/ReadmeIntro_ts.ts +18 -0
  90. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +9 -5
  91. package/project/.sdk/src/cmp/ts/ReadmeOptions_ts.ts +58 -0
  92. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +6 -4
  93. package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +384 -0
  94. package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +6 -4
  95. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +68 -20
  96. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +109 -74
  97. package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +1 -5
  98. package/project/.sdk/tm/go/utility/prepare_auth.go +15 -1
  99. package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +7 -1
  100. package/project/.sdk/tm/lua/utility/prepare_auth.lua +9 -1
  101. package/project/.sdk/tm/php/utility/PrepareAuth.php +11 -1
  102. package/project/.sdk/tm/py/utility/prepare_auth.py +10 -1
  103. package/project/.sdk/tm/rb/utility/prepare_auth.rb +8 -1
  104. package/project/.sdk/tm/ts/src/utility/MakeUrlUtility.ts +7 -8
  105. package/project/.sdk/tm/ts/src/utility/PrepareAuthUtility.ts +7 -1
  106. package/src/cmp/ReadmeEntity.ts +11 -178
  107. package/src/cmp/ReadmeIntro.ts +11 -25
  108. package/src/cmp/ReadmeModel.ts +7 -5
  109. package/src/cmp/ReadmeOptions.ts +12 -74
  110. package/src/cmp/ReadmeRef.ts +11 -1372
  111. package/src/sdkgen.ts +2 -1
  112. package/src/utility.ts +12 -0
  113. /package/project/.sdk/tm/go/utility/{make_target.go → make_point.go} +0 -0
@@ -7,7 +7,12 @@ import {
7
7
 
8
8
  import {
9
9
  KIT,
10
+ Model,
11
+ ModelEntity,
12
+ ModelEntityFlow,
13
+ ModelEntityFlowStep,
10
14
  getModelPath,
15
+ nom,
11
16
  } from '@voxgig/apidef'
12
17
 
13
18
 
@@ -18,42 +23,54 @@ import {
18
23
  each,
19
24
  buildIdNames,
20
25
  getMatchEntries,
26
+ isAuthActive,
21
27
  } from '@voxgig/sdkgen'
22
28
 
23
29
 
24
- type OpGen = (ctx: GenCtx, step: any, index: any) => void
25
-
30
+ // PHP's GenCtx mirrors the shared shape (see TestEntity_ts.ts) plus an
31
+ // `accessor` slot used to mangle the entity factory name when it collides
32
+ // with PHP's case-insensitive `test()` static constructor.
26
33
  type GenCtx = {
27
- model: any
28
- entity: any
29
- flow: any
34
+ model: Model
35
+ entity: ModelEntity
36
+ flow: ModelEntityFlow
30
37
  PROJUPPER: string
31
38
  accessor: string
32
39
  }
33
40
 
41
+ type OpGen = (ctx: GenCtx, step: ModelEntityFlowStep, index: number) => void
42
+
34
43
 
35
44
  const TestEntity = cmp(function TestEntity(props: any) {
36
45
  const ctx$ = props.ctx$
37
- const model = ctx$.model
46
+ const model: Model = ctx$.model
38
47
 
39
48
  const target = props.target
40
- const entity = props.entity
49
+ const entity: ModelEntity = props.entity
41
50
 
42
- const basicflow = getModelPath(model, `main.${KIT}.flow.Basic${entity.Name}Flow`)
43
- const dobasic = basicflow && true === basicflow.active
44
-
45
- if (!dobasic) {
51
+ const basicflow: ModelEntityFlow | undefined =
52
+ getModelPath(model, `main.${KIT}.flow.Basic${nom(entity, 'Name')}Flow`)
53
+ if (null == basicflow || true !== basicflow.active) {
46
54
  return
47
55
  }
48
56
 
49
57
  // PHP method names are case-insensitive — an entity literally named 'test'
50
58
  // collides with the static `test()` test-mode constructor on the SDK class.
51
59
  // Mirror the mangling done in MainEntity_php.ts.
52
- const accessor = 'test' === entity.Name.toLowerCase()
53
- ? entity.Name + '_'
54
- : entity.Name
60
+ const entName = nom(entity, 'Name')
61
+ const accessor = 'test' === entName.toLowerCase()
62
+ ? entName + '_'
63
+ : entName
64
+
65
+ const PROJUPPER = nom(model.const, 'Name').toUpperCase().replace(/[^A-Z_]/g, '_')
55
66
 
56
- const PROJUPPER = model.const.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
67
+ const authActive = isAuthActive(model)
68
+ const apikeyEnvEntry = authActive
69
+ ? `\n "${PROJUPPER}_APIKEY" => "NONE",`
70
+ : ''
71
+ const apikeyLiveField = authActive
72
+ ? `\n "apikey" => $env["${PROJUPPER}_APIKEY"],`
73
+ : ''
57
74
 
58
75
  const idnames = buildIdNames(entity, basicflow)
59
76
  const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
@@ -152,8 +169,7 @@ class ${entity.Name}EntityTest extends TestCase
152
169
  Content(` $env = Runner::env_override([
153
170
  "${PROJUPPER}_TEST_${entity.name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID" => $idmap,
154
171
  "${PROJUPPER}_TEST_LIVE" => "FALSE",
155
- "${PROJUPPER}_TEST_EXPLAIN" => "FALSE",
156
- "${PROJUPPER}_APIKEY" => "NONE",
172
+ "${PROJUPPER}_TEST_EXPLAIN" => "FALSE",${apikeyEnvEntry}
157
173
  ]);
158
174
 
159
175
  $idmap_resolved = Helpers::to_map(
@@ -174,8 +190,7 @@ class ${entity.Name}EntityTest extends TestCase
174
190
  Content(`
175
191
  if ($env["${PROJUPPER}_TEST_LIVE"] === "TRUE") {
176
192
  $merged_opts = Vs::merge([
177
- [
178
- "apikey" => $env["${PROJUPPER}_APIKEY"],
193
+ [${apikeyLiveField}
179
194
  ],
180
195
  $extra ?? [],
181
196
  ]);
@@ -198,9 +213,9 @@ class ${entity.Name}EntityTest extends TestCase
198
213
 
199
214
  const generateCreate: OpGen = (ctx, step, index) => {
200
215
  const { entity, flow, accessor } = ctx
201
- const ref = step.input?.ref ?? entity.name + '_ref01'
202
- const entvar = step.input?.entvar ?? ref + '_ent'
203
- const datavar = step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
216
+ const ref = step.input.ref ?? entity.name + '_ref01'
217
+ const entvar = step.input.entvar ?? ref + '_ent'
218
+ const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
204
219
 
205
220
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
206
221
  const needsEnt = !priorSteps.some((s: any) =>
@@ -208,8 +223,8 @@ const generateCreate: OpGen = (ctx, step, index) => {
208
223
 
209
224
  const hasDatvar = priorSteps.some((s: any) => {
210
225
  if ('create' === s.op) {
211
- const priorRef = s.input?.ref ?? entity.name + '_ref01'
212
- const priorDatvar = s.input?.datavar ?? (priorRef + '_data' + (s.input?.suffix ?? ''))
226
+ const priorRef = s.input.ref ?? entity.name + '_ref01'
227
+ const priorDatvar = s.input.datavar ?? (priorRef + '_data' + (s.input.suffix ?? ''))
213
228
  return priorDatvar === datavar
214
229
  }
215
230
  return false
@@ -243,17 +258,20 @@ const generateCreate: OpGen = (ctx, step, index) => {
243
258
  $this->assertNull($err);
244
259
  $${datavar} = Helpers::to_map($${datavar}_result);
245
260
  $this->assertNotNull($${datavar});
246
- $this->assertNotNull($${datavar}["id"]);
247
261
  `)
262
+ if (null != ctx.entity.id) {
263
+ Content(` $this->assertNotNull($${datavar}["id"]);
264
+ `)
265
+ }
248
266
  }
249
267
 
250
268
 
251
269
  const generateList: OpGen = (ctx, step, index) => {
252
270
  const { entity, flow, accessor } = ctx
253
- const ref = step.input?.ref ?? entity.name + '_ref01'
254
- const entvar = step.input?.entvar ?? ref + '_ent'
255
- const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
256
- const listvar = step.input?.listvar ?? (ref + '_list' + (step.input?.suffix ?? ''))
271
+ const ref = step.input.ref ?? entity.name + '_ref01'
272
+ const entvar = step.input.entvar ?? ref + '_ent'
273
+ const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
274
+ const listvar = step.input.listvar ?? (ref + '_list' + (step.input.suffix ?? ''))
257
275
 
258
276
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
259
277
  const needsEnt = !priorSteps.some((s: any) =>
@@ -293,7 +311,7 @@ const generateList: OpGen = (ctx, step, index) => {
293
311
  for (const validator of step.valid) {
294
312
  const validRef = validator.def?.ref
295
313
  const hasRefData = validRef && allSteps.some((s: any) => 'create' === s.op &&
296
- ((s.input?.ref ?? entity.name + '_ref01') === validRef))
314
+ ((s.input.ref ?? entity.name + '_ref01') === validRef))
297
315
 
298
316
  if ('ItemExists' === validator.apply && hasRefData) {
299
317
  const refDataVar = validRef + '_data'
@@ -319,17 +337,19 @@ const generateList: OpGen = (ctx, step, index) => {
319
337
 
320
338
  const generateUpdate: OpGen = (ctx, step, index) => {
321
339
  const { entity, flow, accessor } = ctx
322
- const ref = step.input?.ref ?? entity.name + '_ref01'
323
- const entvar = step.input?.entvar ?? ref + '_ent'
324
- const datavar = step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
325
- const resdatavar = step.input?.resdatavar ?? (ref + '_resdata' + (step.input?.suffix ?? ''))
326
- const markdefvar = step.input?.markdefvar ?? (ref + '_markdef' + (step.input?.suffix ?? ''))
327
- const srcdatavar = step.input?.srcdatavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
340
+ const ref = step.input.ref ?? entity.name + '_ref01'
341
+ const entvar = step.input.entvar ?? ref + '_ent'
342
+ const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
343
+ const resdatavar = step.input.resdatavar ?? (ref + '_resdata' + (step.input.suffix ?? ''))
344
+ const markdefvar = step.input.markdefvar ?? (ref + '_markdef' + (step.input.suffix ?? ''))
345
+ const srcdatavar = step.input.srcdatavar ?? (ref + '_data' + (step.input.suffix ?? ''))
328
346
 
329
347
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
330
348
  const needsEnt = !priorSteps.some((s: any) =>
331
349
  ['create', 'list', 'load', 'update', 'remove'].includes(s.op))
332
350
 
351
+ const hasEntIdU = null != entity.id
352
+
333
353
  Content(` // UPDATE
334
354
  `)
335
355
  if (needsEnt) {
@@ -337,8 +357,11 @@ const generateUpdate: OpGen = (ctx, step, index) => {
337
357
  `)
338
358
  }
339
359
  Content(` $${datavar}_up = [
340
- "id" => $${srcdatavar}["id"],
341
360
  `)
361
+ if (hasEntIdU) {
362
+ Content(` "id" => $${srcdatavar}["id"],
363
+ `)
364
+ }
342
365
 
343
366
  if (step.data) {
344
367
  const dataEntries = Object.entries(step.data).filter(([k]: any) => k !== 'id' && !k.endsWith('$'))
@@ -353,7 +376,7 @@ const generateUpdate: OpGen = (ctx, step, index) => {
353
376
 
354
377
  if (step.spec) {
355
378
  for (const spec of step.spec) {
356
- if ('TextFieldMark' === spec.apply && null != step.input?.textfield) {
379
+ if ('TextFieldMark' === spec.apply && null != step.input.textfield) {
357
380
  const fieldname = step.input.textfield
358
381
  const fieldvalue = spec.def?.mark ?? `Mark01-${ref}`
359
382
  Content(`
@@ -370,12 +393,15 @@ const generateUpdate: OpGen = (ctx, step, index) => {
370
393
  $this->assertNull($err);
371
394
  $${resdatavar} = Helpers::to_map($${resdatavar}_result);
372
395
  $this->assertNotNull($${resdatavar});
373
- $this->assertEquals($${resdatavar}["id"], $${datavar}_up["id"]);
374
396
  `)
397
+ if (hasEntIdU) {
398
+ Content(` $this->assertEquals($${resdatavar}["id"], $${datavar}_up["id"]);
399
+ `)
400
+ }
375
401
 
376
402
  if (step.spec) {
377
403
  for (const spec of step.spec) {
378
- if ('TextFieldMark' === spec.apply && null != step.input?.textfield) {
404
+ if ('TextFieldMark' === spec.apply && null != step.input.textfield) {
379
405
  Content(` $this->assertEquals($${resdatavar}[$${markdefvar}_name], $${markdefvar}_value);
380
406
  `)
381
407
  }
@@ -386,11 +412,11 @@ const generateUpdate: OpGen = (ctx, step, index) => {
386
412
 
387
413
  const generateLoad: OpGen = (ctx, step, index) => {
388
414
  const { entity, flow, accessor } = ctx
389
- const ref = step.input?.ref ?? entity.name + '_ref01'
390
- const entvar = step.input?.entvar ?? ref + '_ent'
391
- const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
392
- const datavar = step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
393
- const srcdatavar = step.input?.srcdatavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
415
+ const ref = step.input.ref ?? entity.name + '_ref01'
416
+ const entvar = step.input.entvar ?? ref + '_ent'
417
+ const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
418
+ const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
419
+ const srcdatavar = step.input.srcdatavar ?? (ref + '_data' + (step.input.suffix ?? ''))
394
420
 
395
421
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
396
422
  const hasEntVar = priorSteps.some((s: any) =>
@@ -401,20 +427,22 @@ const generateLoad: OpGen = (ctx, step, index) => {
401
427
  const hasSrcData = (!flowHasCreate && srcdatavar === (preambleRef + '_data')) ||
402
428
  priorSteps.some((s: any) => {
403
429
  if ('create' === s.op) {
404
- const priorRef = s.input?.ref ?? entity.name + '_ref01'
405
- const priorDatvar = s.input?.datavar ?? (priorRef + '_data' + (s.input?.suffix ?? ''))
430
+ const priorRef = s.input.ref ?? entity.name + '_ref01'
431
+ const priorDatvar = s.input.datavar ?? (priorRef + '_data' + (s.input.suffix ?? ''))
406
432
  return priorDatvar === srcdatavar
407
433
  }
408
434
  return false
409
435
  })
410
436
 
437
+ const hasEntId = null != entity.id
438
+
411
439
  Content(` // LOAD
412
440
  `)
413
441
  if (!hasEntVar) {
414
442
  Content(` $${entvar} = $client->${accessor}(null);
415
443
  `)
416
444
  }
417
- if (!hasSrcData) {
445
+ if (!hasSrcData && hasEntId) {
418
446
  Content(` $${srcdatavar}_raw = Vs::items(Helpers::to_map(
419
447
  Vs::getpath($setup["data"], "existing.${entity.name}")));
420
448
  $${srcdatavar} = null;
@@ -423,7 +451,8 @@ const generateLoad: OpGen = (ctx, step, index) => {
423
451
  }
424
452
  `)
425
453
  }
426
- Content(` $${matchvar} = [
454
+ if (hasEntId) {
455
+ Content(` $${matchvar} = [
427
456
  "id" => $${srcdatavar}["id"],
428
457
  ];
429
458
  [$${datavar}_loaded, $err] = $${entvar}->load($${matchvar}, null);
@@ -432,32 +461,50 @@ const generateLoad: OpGen = (ctx, step, index) => {
432
461
  $this->assertNotNull($${datavar}_load_result);
433
462
  $this->assertEquals($${datavar}_load_result["id"], $${srcdatavar}["id"]);
434
463
  `)
464
+ }
465
+ else {
466
+ Content(` $${matchvar} = [];
467
+ [$${datavar}_loaded, $err] = $${entvar}->load($${matchvar}, null);
468
+ $this->assertNull($err);
469
+ $this->assertNotNull($${datavar}_loaded);
470
+ `)
471
+ }
435
472
  }
436
473
 
437
474
 
438
475
  const generateRemove: OpGen = (ctx, step, index) => {
439
476
  const { entity, flow, accessor } = ctx
440
- const ref = step.input?.ref ?? entity.name + '_ref01'
441
- const entvar = step.input?.entvar ?? ref + '_ent'
442
- const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
443
- const srcdatavar = step.input?.srcdatavar ?? (ref + '_data')
477
+ const ref = step.input.ref ?? entity.name + '_ref01'
478
+ const entvar = step.input.entvar ?? ref + '_ent'
479
+ const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
480
+ const srcdatavar = step.input.srcdatavar ?? (ref + '_data')
444
481
 
445
482
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
446
483
  const needsEnt = !priorSteps.some((s: any) =>
447
484
  ['create', 'list', 'load', 'update', 'remove'].includes(s.op))
448
485
 
486
+ const hasEntIdR = null != entity.id
487
+
449
488
  Content(` // REMOVE
450
489
  `)
451
490
  if (needsEnt) {
452
491
  Content(` $${entvar} = $client->${accessor}(null);
453
492
  `)
454
493
  }
455
- Content(` $${matchvar} = [
494
+ if (hasEntIdR) {
495
+ Content(` $${matchvar} = [
456
496
  "id" => $${srcdatavar}["id"],
457
497
  ];
458
498
  [$_, $err] = $${entvar}->remove($${matchvar}, null);
459
499
  $this->assertNull($err);
460
500
  `)
501
+ }
502
+ else {
503
+ Content(` $${matchvar} = [];
504
+ [$_, $err] = $${entvar}->remove($${matchvar}, null);
505
+ $this->assertNull($err);
506
+ `)
507
+ }
461
508
  }
462
509
 
463
510
 
@@ -9,6 +9,7 @@ import {
9
9
  Line,
10
10
  cmp,
11
11
  each,
12
+ isAuthActive,
12
13
  } from '@voxgig/sdkgen'
13
14
 
14
15
 
@@ -37,12 +38,19 @@ const Config = cmp(async function Config(props: any) {
37
38
 
38
39
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
39
40
 
41
+ const authActive = isAuthActive(model)
40
42
  let authPrefix = ''
41
43
  try { authPrefix = getModelPath(model, `main.${KIT}.config.auth.prefix`) } catch (_e) { }
42
44
 
43
45
  let baseUrl = ''
44
46
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
45
47
 
48
+ const authBlock = authActive
49
+ ? ` "auth": {
50
+ "prefix": "${authPrefix}",
51
+ },\n`
52
+ : ''
53
+
46
54
  File({ name: 'config.' + target.ext }, () => {
47
55
 
48
56
  Content(`# ${model.const.Name} SDK configuration
@@ -65,10 +73,7 @@ def make_config():
65
73
  Content(` },
66
74
  "options": {
67
75
  "base": "${baseUrl}",
68
- "auth": {
69
- "prefix": "${authPrefix}",
70
- },
71
- "headers": ${formatPyDict(headers, 3)},
76
+ ${authBlock} "headers": ${formatPyDict(headers, 3)},
72
77
  "entity": {
73
78
  `)
74
79
 
@@ -0,0 +1,138 @@
1
+
2
+ import { cmp, each, Content } from '@voxgig/sdkgen'
3
+
4
+ import {
5
+ KIT,
6
+ getModelPath,
7
+ } from '@voxgig/apidef'
8
+
9
+
10
+ // Operation method spelling differs between Go and other languages — Go
11
+ // uses PascalCase methods with explicit ctrl arg, others use lowercase
12
+ // methods with optional ctrl. The op descriptions are language-agnostic.
13
+ const OP_DESC: Record<string, { method: string, desc: string }> = {
14
+ load: { method: 'load(match)', desc: 'Load a single entity by match criteria.' },
15
+ list: { method: 'list(match)', desc: 'List entities matching the criteria.' },
16
+ create: { method: 'create(data)', desc: 'Create a new entity with the given data.' },
17
+ update: { method: 'update(data)', desc: 'Update an existing entity.' },
18
+ remove: { method: 'remove(match)', desc: 'Remove the matching entity.' },
19
+ }
20
+
21
+
22
+ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
23
+ const { target } = props
24
+ const { model } = props.ctx$
25
+
26
+ const entity = getModelPath(model, `main.${KIT}.entity`)
27
+
28
+ const publishedEntities = each(entity)
29
+ .filter((entity: any) => entity.active !== false)
30
+
31
+ if (0 === publishedEntities.length) {
32
+ return
33
+ }
34
+
35
+ Content(`
36
+
37
+ ## Entities
38
+
39
+ `)
40
+
41
+ publishedEntities.map((entity: any) => {
42
+ const opnames = Object.keys(entity.op || {})
43
+ const fields = entity.fields || []
44
+
45
+ Content(`
46
+ ### ${entity.Name}
47
+
48
+ `)
49
+
50
+ if (entity.short) {
51
+ Content(`${entity.short}
52
+
53
+ `)
54
+ }
55
+
56
+ Content(`Create an instance: \`const ${entity.name} = client.${entity.Name}()\`
57
+
58
+ `)
59
+
60
+ if (opnames.length > 0) {
61
+ Content(`#### Operations
62
+
63
+ | Method | Description |
64
+ | --- | --- |
65
+ `)
66
+ opnames.map((opname: string) => {
67
+ const info = OP_DESC[opname]
68
+ if (info) {
69
+ Content(`| \`${info.method}\` | ${info.desc} |
70
+ `)
71
+ }
72
+ })
73
+
74
+ Content(`
75
+ `)
76
+ }
77
+
78
+ if (fields.length > 0) {
79
+ Content(`#### Fields
80
+
81
+ | Field | Type | Description |
82
+ | --- | --- | --- |
83
+ `)
84
+
85
+ each(fields, (field: any) => {
86
+ const desc = field.short || ''
87
+ Content(`| \`${field.name}\` | \`${field.type || 'any'}\` | ${desc} |
88
+ `)
89
+ })
90
+
91
+ Content(`
92
+ `)
93
+ }
94
+
95
+ if (opnames.includes('load')) {
96
+ Content(`#### Example: Load
97
+
98
+ \`\`\`ts
99
+ const ${entity.name} = await client.${entity.Name}().load({ id: '${entity.name}_id' })
100
+ \`\`\`
101
+
102
+ `)
103
+ }
104
+
105
+ if (opnames.includes('list')) {
106
+ Content(`#### Example: List
107
+
108
+ \`\`\`ts
109
+ const ${entity.name}s = await client.${entity.Name}().list()
110
+ \`\`\`
111
+
112
+ `)
113
+ }
114
+
115
+ if (opnames.includes('create')) {
116
+ Content(`#### Example: Create
117
+
118
+ \`\`\`ts
119
+ const ${entity.name} = await client.${entity.Name}().create({
120
+ `)
121
+ each(fields, (field: any) => {
122
+ if ('id' !== field.name && field.req) {
123
+ Content(` ${field.name}: /* ${field.type || 'value'} */,
124
+ `)
125
+ }
126
+ })
127
+ Content(`})
128
+ \`\`\`
129
+
130
+ `)
131
+ }
132
+ })
133
+ })
134
+
135
+
136
+ export {
137
+ ReadmeEntity
138
+ }
@@ -1,10 +1,14 @@
1
1
 
2
- import { cmp, Content } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isAuthActive } from '@voxgig/sdkgen'
3
3
 
4
4
 
5
5
  const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
6
6
  const { target, ctx$: { model } } = props
7
7
 
8
+ const apikeyEnvLine = isAuthActive(model)
9
+ ? `\n${model.NAME}_APIKEY=<your-key>`
10
+ : ''
11
+
8
12
  Content(`### Make a direct HTTP request
9
13
 
10
14
  For endpoints not covered by entity methods:
@@ -78,8 +82,7 @@ client = ${model.const.Name}SDK({
78
82
  Create a \`.env.local\` file at the project root:
79
83
 
80
84
  \`\`\`
81
- ${model.NAME}_TEST_LIVE=TRUE
82
- ${model.NAME}_APIKEY=<your-key>
85
+ ${model.NAME}_TEST_LIVE=TRUE${apikeyEnvLine}
83
86
  \`\`\`
84
87
 
85
88
  Then run:
@@ -0,0 +1,18 @@
1
+
2
+ import { cmp, Content } from '@voxgig/sdkgen'
3
+
4
+
5
+ const ReadmeIntro = cmp(function ReadmeIntro(props: any) {
6
+ const { target, ctx$: { model } } = props
7
+
8
+ Content(`# ${model.Name} ${target.title} SDK
9
+
10
+ The ${target.title} SDK for the ${model.Name} API. Provides an entity-oriented interface following Pythonic conventions.
11
+
12
+ `)
13
+ })
14
+
15
+
16
+ export {
17
+ ReadmeIntro
18
+ }
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, each, Content } from '@voxgig/sdkgen'
2
+ import { cmp, each, Content, isAuthActive } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -13,6 +13,10 @@ const ReadmeModel = cmp(function ReadmeModel(props: any) {
13
13
  const entity = getModelPath(model, `main.${KIT}.entity`)
14
14
  const entityList = each(entity).filter((e: any) => e.active !== false)
15
15
 
16
+ const apikeyOptionRow = isAuthActive(model)
17
+ ? '| `apikey` | `str` | API key for authentication. |\n'
18
+ : ''
19
+
16
20
  Content(`### ${model.const.Name}SDK
17
21
 
18
22
  \`\`\`python
@@ -25,8 +29,7 @@ Creates a new SDK client.
25
29
 
26
30
  | Option | Type | Description |
27
31
  | --- | --- | --- |
28
- | \`apikey\` | \`str\` | API key for authentication. |
29
- | \`base\` | \`str\` | Base URL of the API server. |
32
+ ${apikeyOptionRow}| \`base\` | \`str\` | Base URL of the API server. |
30
33
  | \`prefix\` | \`str\` | URL path prefix prepended to all requests. |
31
34
  | \`suffix\` | \`str\` | URL path suffix appended to all requests. |
32
35
  | \`feature\` | \`dict\` | Feature activation flags. |
@@ -0,0 +1,58 @@
1
+
2
+ import { cmp, each, Content } from '@voxgig/sdkgen'
3
+
4
+
5
+ const ReadmeOptions = cmp(function ReadmeOptions(props: any) {
6
+ const { target } = props
7
+ const { model } = props.ctx$
8
+
9
+ const publishedOptions = each(target.options).filter((option: any) => option.publish)
10
+ if (0 === publishedOptions.length) {
11
+ return
12
+ }
13
+
14
+ Content(`
15
+
16
+ ## Options
17
+
18
+ Pass options when creating a client instance:
19
+
20
+ `)
21
+
22
+ Content(`\`\`\`python
23
+ client = ${model.Name}SDK({
24
+ `)
25
+
26
+ publishedOptions.map((option: any) => {
27
+ if ('apikey' === option.name) {
28
+ Content(` "${option.name}": os.environ.get("${model.NAME}_APIKEY"),
29
+ `)
30
+ }
31
+ else {
32
+ Content(` # "${option.name}": ${option.kind === 'string' ? "\"...\"" : '...'},
33
+ `)
34
+ }
35
+ })
36
+
37
+ Content(`})
38
+ \`\`\`
39
+
40
+ `)
41
+
42
+ Content(`| Option | Type | Description |
43
+ | --- | --- | --- |
44
+ `)
45
+
46
+ publishedOptions.map((option: any) => {
47
+ Content(`| \`${option.name}\` | \`${option.kind}\` | ${option.short} |
48
+ `)
49
+ })
50
+
51
+ Content(`
52
+ `)
53
+ })
54
+
55
+
56
+ export {
57
+ ReadmeOptions
58
+ }
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, each, Content } from '@voxgig/sdkgen'
2
+ import { cmp, each, Content, isAuthActive } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -18,15 +18,18 @@ const ReadmeQuick = cmp(function ReadmeQuick(props: any) {
18
18
  e.active !== false && e.ancestors && e.ancestors.length > 0
19
19
  ) as any
20
20
 
21
+ const authActive = isAuthActive(model)
22
+ const apikeyImport = authActive ? `import os\n` : ''
23
+ const apikeyArg = authActive
24
+ ? `\n "apikey": os.environ.get("${model.NAME}_APIKEY"),\n`
25
+ : ''
26
+
21
27
  Content(`### 1. Create a client
22
28
 
23
29
  \`\`\`python
24
- import os
25
- from ${model.const.Name.toLowerCase()}_sdk import ${model.const.Name}SDK
30
+ ${apikeyImport}from ${model.const.Name.toLowerCase()}_sdk import ${model.const.Name}SDK
26
31
 
27
- client = ${model.const.Name}SDK({
28
- "apikey": os.environ.get("${model.NAME}_APIKEY"),
29
- })
32
+ client = ${model.const.Name}SDK({${apikeyArg}})
30
33
  \`\`\`
31
34
 
32
35
  `)