@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.
Files changed (160) 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 +3 -3
  19. package/project/.sdk/src/cmp/go/Config_go.ts +9 -4
  20. package/project/.sdk/src/cmp/go/Entity_go.ts +2 -2
  21. package/project/.sdk/src/cmp/go/Main_go.ts +8 -4
  22. package/project/.sdk/src/cmp/go/Package_go.ts +2 -2
  23. package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +138 -0
  24. package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +2 -2
  25. package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +8 -5
  26. package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +2 -2
  27. package/project/.sdk/src/cmp/go/ReadmeIntro_go.ts +18 -0
  28. package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +8 -5
  29. package/project/.sdk/src/cmp/go/ReadmeOptions_go.ts +58 -0
  30. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +13 -9
  31. package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +354 -0
  32. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +8 -6
  33. package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +2 -2
  34. package/project/.sdk/src/cmp/go/TestDirect_go.ts +222 -41
  35. package/project/.sdk/src/cmp/go/TestEntity_go.ts +142 -60
  36. package/project/.sdk/src/cmp/go/Test_go.ts +2 -2
  37. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +21 -4
  38. package/project/.sdk/src/cmp/js/Config_js.ts +18 -0
  39. package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +138 -0
  40. package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +11 -6
  41. package/project/.sdk/src/cmp/js/ReadmeIntro_js.ts +18 -0
  42. package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +6 -3
  43. package/project/.sdk/src/cmp/js/ReadmeOptions_js.ts +58 -0
  44. package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +6 -4
  45. package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +384 -0
  46. package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +6 -4
  47. package/project/.sdk/src/cmp/js/TestDirect_js.ts +23 -12
  48. package/project/.sdk/src/cmp/js/TestEntity_js.ts +107 -74
  49. package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +1 -5
  50. package/project/.sdk/src/cmp/lua/Config_lua.ts +9 -4
  51. package/project/.sdk/src/cmp/lua/Package_lua.ts +9 -2
  52. package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +138 -0
  53. package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +6 -3
  54. package/project/.sdk/src/cmp/lua/ReadmeIntro_lua.ts +18 -0
  55. package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +6 -3
  56. package/project/.sdk/src/cmp/lua/ReadmeOptions_lua.ts +58 -0
  57. package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +6 -4
  58. package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +360 -0
  59. package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +6 -4
  60. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +172 -29
  61. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +120 -52
  62. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +20 -4
  63. package/project/.sdk/src/cmp/php/Config_php.ts +10 -8
  64. package/project/.sdk/src/cmp/php/Package_php.ts +7 -1
  65. package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +138 -0
  66. package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +6 -3
  67. package/project/.sdk/src/cmp/php/ReadmeIntro_php.ts +18 -0
  68. package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +6 -3
  69. package/project/.sdk/src/cmp/php/ReadmeOptions_php.ts +58 -0
  70. package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +6 -4
  71. package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +358 -0
  72. package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +6 -4
  73. package/project/.sdk/src/cmp/php/TestDirect_php.ts +171 -28
  74. package/project/.sdk/src/cmp/php/TestEntity_php.ts +126 -55
  75. package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +17 -3
  76. package/project/.sdk/src/cmp/py/Config_py.ts +9 -4
  77. package/project/.sdk/src/cmp/py/Package_py.ts +8 -1
  78. package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +138 -0
  79. package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +6 -3
  80. package/project/.sdk/src/cmp/py/ReadmeIntro_py.ts +18 -0
  81. package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +6 -3
  82. package/project/.sdk/src/cmp/py/ReadmeOptions_py.ts +58 -0
  83. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +9 -6
  84. package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +356 -0
  85. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +9 -6
  86. package/project/.sdk/src/cmp/py/TestDirect_py.ts +164 -27
  87. package/project/.sdk/src/cmp/py/TestEntity_py.ts +125 -51
  88. package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +19 -4
  89. package/project/.sdk/src/cmp/rb/Config_rb.ts +9 -4
  90. package/project/.sdk/src/cmp/rb/Package_rb.ts +9 -2
  91. package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +138 -0
  92. package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +6 -3
  93. package/project/.sdk/src/cmp/rb/ReadmeIntro_rb.ts +18 -0
  94. package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +6 -3
  95. package/project/.sdk/src/cmp/rb/ReadmeOptions_rb.ts +58 -0
  96. package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +6 -4
  97. package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +361 -0
  98. package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +6 -4
  99. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +172 -29
  100. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +120 -52
  101. package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +19 -3
  102. package/project/.sdk/src/cmp/ts/Config_ts.ts +18 -0
  103. package/project/.sdk/src/cmp/ts/Package_ts.ts +1 -1
  104. package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +138 -0
  105. package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +11 -6
  106. package/project/.sdk/src/cmp/ts/ReadmeIntro_ts.ts +18 -0
  107. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +9 -5
  108. package/project/.sdk/src/cmp/ts/ReadmeOptions_ts.ts +58 -0
  109. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +6 -4
  110. package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +384 -0
  111. package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +6 -4
  112. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +213 -42
  113. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +168 -75
  114. package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +1 -5
  115. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +8 -1
  116. package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +8 -2
  117. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +21 -1
  118. package/project/.sdk/tm/go/feature/test_feature.go +51 -3
  119. package/project/.sdk/tm/go/test/runner_test.go +106 -6
  120. package/project/.sdk/tm/go/test/sdk-test-control.json +19 -0
  121. package/project/.sdk/tm/go/utility/fetcher.go +10 -0
  122. package/project/.sdk/tm/go/utility/make_url.go +12 -0
  123. package/project/.sdk/tm/go/utility/prepare_auth.go +15 -1
  124. package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +7 -1
  125. package/project/.sdk/tm/lua/feature/test_feature.lua +41 -3
  126. package/project/.sdk/tm/lua/test/runner.lua +74 -0
  127. package/project/.sdk/tm/lua/test/sdk-test-control.json +19 -0
  128. package/project/.sdk/tm/lua/utility/fetcher.lua +13 -0
  129. package/project/.sdk/tm/lua/utility/make_url.lua +16 -0
  130. package/project/.sdk/tm/lua/utility/prepare_auth.lua +9 -1
  131. package/project/.sdk/tm/php/feature/TestFeature.php +185 -43
  132. package/project/.sdk/tm/php/test/Runner.php +62 -0
  133. package/project/.sdk/tm/php/test/sdk-test-control.json +19 -0
  134. package/project/.sdk/tm/php/utility/Fetcher.php +132 -9
  135. package/project/.sdk/tm/php/utility/MakeUrl.php +16 -0
  136. package/project/.sdk/tm/php/utility/PrepareAuth.php +11 -1
  137. package/project/.sdk/tm/py/feature/test_feature.py +35 -3
  138. package/project/.sdk/tm/py/test/runner.py +60 -0
  139. package/project/.sdk/tm/py/test/sdk-test-control.json +19 -0
  140. package/project/.sdk/tm/py/utility/fetcher.py +13 -0
  141. package/project/.sdk/tm/py/utility/make_url.py +13 -0
  142. package/project/.sdk/tm/py/utility/prepare_auth.py +10 -1
  143. package/project/.sdk/tm/rb/feature/test_feature.rb +36 -3
  144. package/project/.sdk/tm/rb/test/runner.rb +46 -0
  145. package/project/.sdk/tm/rb/test/sdk-test-control.json +19 -0
  146. package/project/.sdk/tm/rb/utility/fetcher.rb +49 -28
  147. package/project/.sdk/tm/rb/utility/make_url.rb +16 -0
  148. package/project/.sdk/tm/rb/utility/prepare_auth.rb +8 -1
  149. package/project/.sdk/tm/ts/src/utility/MakeUrlUtility.ts +7 -8
  150. package/project/.sdk/tm/ts/src/utility/PrepareAuthUtility.ts +7 -1
  151. package/project/.sdk/tm/ts/test/sdk-test-control.json +19 -0
  152. package/project/.sdk/tm/ts/test/utility.ts +120 -2
  153. package/src/cmp/ReadmeEntity.ts +11 -178
  154. package/src/cmp/ReadmeIntro.ts +11 -25
  155. package/src/cmp/ReadmeModel.ts +7 -5
  156. package/src/cmp/ReadmeOptions.ts +12 -74
  157. package/src/cmp/ReadmeRef.ts +11 -1372
  158. package/src/sdkgen.ts +2 -1
  159. package/src/utility.ts +12 -0
  160. /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,34 +23,43 @@ 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
+ // See TestEntity_ts.ts for the GenCtx/OpGen contract.
26
31
  type GenCtx = {
27
- model: any
28
- entity: any
29
- flow: any
32
+ model: Model
33
+ entity: ModelEntity
34
+ flow: ModelEntityFlow
30
35
  PROJUPPER: string
31
36
  }
32
37
 
38
+ type OpGen = (ctx: GenCtx, step: ModelEntityFlowStep, index: number) => void
39
+
33
40
 
34
41
  const TestEntity = cmp(function TestEntity(props: any) {
35
42
  const ctx$ = props.ctx$
36
- const model = ctx$.model
43
+ const model: Model = ctx$.model
37
44
 
38
45
  const target = props.target
39
- const entity = props.entity
46
+ const entity: ModelEntity = props.entity
40
47
 
41
- const basicflow = getModelPath(model, `main.${KIT}.flow.Basic${entity.Name}Flow`)
42
- const dobasic = basicflow && true === basicflow.active
43
-
44
- if (!dobasic) {
48
+ const basicflow: ModelEntityFlow | undefined =
49
+ getModelPath(model, `main.${KIT}.flow.Basic${nom(entity, 'Name')}Flow`)
50
+ if (null == basicflow || true !== basicflow.active) {
45
51
  return
46
52
  }
47
53
 
48
- const PROJUPPER = model.const.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
54
+ const PROJUPPER = nom(model.const, 'Name').toUpperCase().replace(/[^A-Z_]/g, '_')
55
+
56
+ const authActive = isAuthActive(model)
57
+ const apikeyEnvEntry = authActive
58
+ ? `\n "${PROJUPPER}_APIKEY" => "NONE",`
59
+ : ''
60
+ const apikeyLiveField = authActive
61
+ ? `\n "apikey" => env["${PROJUPPER}_APIKEY"],`
62
+ : ''
49
63
 
50
64
  const idnames = buildIdNames(entity, basicflow)
51
65
  const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
@@ -77,6 +91,21 @@ class ${entity.Name}EntityTest < Minitest::Test
77
91
 
78
92
  def test_basic_flow
79
93
  setup = ${entity.name}_basic_setup(nil)
94
+ # Per-op sdk-test-control.json skip.
95
+ _live = setup[:live] || false
96
+ [${(Array.from(new Set((allSteps as any[]).map((s: any) => s.op).filter(Boolean)))).map(o => `"${o}"`).join(', ')}].each do |_op|
97
+ _should_skip, _reason = Runner.is_control_skipped("entityOp", "${entity.name}." + _op, _live ? "live" : "unit")
98
+ if _should_skip
99
+ skip(_reason || "skipped via sdk-test-control.json")
100
+ return
101
+ end
102
+ end
103
+ # The basic flow consumes synthetic IDs from the fixture. In live mode
104
+ # without an *_ENTID env override, those IDs hit the live API and 4xx.
105
+ if setup[:synthetic_only]
106
+ skip "live entity test uses synthetic IDs from fixture — set ${PROJUPPER}_TEST_${entity.name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID JSON to run live"
107
+ return
108
+ end
80
109
  client = setup[:client]
81
110
 
82
111
  `)
@@ -138,11 +167,16 @@ end
138
167
 
139
168
  `)
140
169
 
141
- Content(` env = Runner.env_override({
170
+ Content(` # Detect ENTID env override before envOverride consumes it. When live
171
+ # mode is on without a real override, the basic test runs against synthetic
172
+ # IDs from the fixture and 4xx's. Surface this so the test can skip.
173
+ entid_env_raw = ENV["${PROJUPPER}_TEST_${entity.name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID"]
174
+ idmap_overridden = !entid_env_raw.nil? && entid_env_raw.strip.start_with?("{")
175
+
176
+ env = Runner.env_override({
142
177
  "${PROJUPPER}_TEST_${entity.name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID" => idmap,
143
178
  "${PROJUPPER}_TEST_LIVE" => "FALSE",
144
- "${PROJUPPER}_TEST_EXPLAIN" => "FALSE",
145
- "${PROJUPPER}_APIKEY" => "NONE",
179
+ "${PROJUPPER}_TEST_EXPLAIN" => "FALSE",${apikeyEnvEntry}
146
180
  })
147
181
 
148
182
  idmap_resolved = Helpers.to_map(
@@ -163,20 +197,22 @@ end
163
197
  Content(`
164
198
  if env["${PROJUPPER}_TEST_LIVE"] == "TRUE"
165
199
  merged_opts = Vs.merge([
166
- {
167
- "apikey" => env["${PROJUPPER}_APIKEY"],
200
+ {${apikeyLiveField}
168
201
  },
169
202
  extra || {},
170
203
  ])
171
204
  client = ${model.const.Name}SDK.new(Helpers.to_map(merged_opts))
172
205
  end
173
206
 
207
+ live = env["${PROJUPPER}_TEST_LIVE"] == "TRUE"
174
208
  {
175
209
  client: client,
176
210
  data: entity_data,
177
211
  idmap: idmap_resolved,
178
212
  env: env,
179
213
  explain: env["${PROJUPPER}_TEST_EXPLAIN"] == "TRUE",
214
+ live: live,
215
+ synthetic_only: live && !idmap_overridden,
180
216
  now: (Time.now.to_f * 1000).to_i,
181
217
  }
182
218
  end
@@ -187,9 +223,9 @@ end
187
223
 
188
224
  const generateCreate: OpGen = (ctx, step, index) => {
189
225
  const { entity, flow } = ctx
190
- const ref = step.input?.ref ?? entity.name + '_ref01'
191
- const entvar = step.input?.entvar ?? ref + '_ent'
192
- const datavar = step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
226
+ const ref = step.input.ref ?? entity.name + '_ref01'
227
+ const entvar = step.input.entvar ?? ref + '_ent'
228
+ const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
193
229
 
194
230
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
195
231
  const needsEnt = !priorSteps.some((s: any) =>
@@ -197,8 +233,8 @@ const generateCreate: OpGen = (ctx, step, index) => {
197
233
 
198
234
  const hasDatvar = priorSteps.some((s: any) => {
199
235
  if ('create' === s.op) {
200
- const priorRef = s.input?.ref ?? entity.name + '_ref01'
201
- const priorDatvar = s.input?.datavar ?? (priorRef + '_data' + (s.input?.suffix ?? ''))
236
+ const priorRef = s.input.ref ?? entity.name + '_ref01'
237
+ const priorDatvar = s.input.datavar ?? (priorRef + '_data' + (s.input.suffix ?? ''))
202
238
  return priorDatvar === datavar
203
239
  }
204
240
  return false
@@ -232,17 +268,20 @@ const generateCreate: OpGen = (ctx, step, index) => {
232
268
  assert_nil err
233
269
  ${datavar} = Helpers.to_map(${datavar}_result)
234
270
  assert !${datavar}.nil?
235
- assert !${datavar}["id"].nil?
236
271
  `)
272
+ if (null != ctx.entity.id) {
273
+ Content(` assert !${datavar}["id"].nil?
274
+ `)
275
+ }
237
276
  }
238
277
 
239
278
 
240
279
  const generateList: OpGen = (ctx, step, index) => {
241
280
  const { entity, flow } = ctx
242
- const ref = step.input?.ref ?? entity.name + '_ref01'
243
- const entvar = step.input?.entvar ?? ref + '_ent'
244
- const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
245
- const listvar = step.input?.listvar ?? (ref + '_list' + (step.input?.suffix ?? ''))
281
+ const ref = step.input.ref ?? entity.name + '_ref01'
282
+ const entvar = step.input.entvar ?? ref + '_ent'
283
+ const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
284
+ const listvar = step.input.listvar ?? (ref + '_list' + (step.input.suffix ?? ''))
246
285
 
247
286
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
248
287
  const needsEnt = !priorSteps.some((s: any) =>
@@ -282,7 +321,7 @@ const generateList: OpGen = (ctx, step, index) => {
282
321
  for (const validator of step.valid) {
283
322
  const validRef = validator.def?.ref
284
323
  const hasRefData = validRef && allSteps.some((s: any) => 'create' === s.op &&
285
- ((s.input?.ref ?? entity.name + '_ref01') === validRef))
324
+ ((s.input.ref ?? entity.name + '_ref01') === validRef))
286
325
 
287
326
  if ('ItemExists' === validator.apply && hasRefData) {
288
327
  const refDataVar = validRef + '_data'
@@ -308,17 +347,19 @@ const generateList: OpGen = (ctx, step, index) => {
308
347
 
309
348
  const generateUpdate: OpGen = (ctx, step, index) => {
310
349
  const { entity, flow } = ctx
311
- const ref = step.input?.ref ?? entity.name + '_ref01'
312
- const entvar = step.input?.entvar ?? ref + '_ent'
313
- const datavar = step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
314
- const resdatavar = step.input?.resdatavar ?? (ref + '_resdata' + (step.input?.suffix ?? ''))
315
- const markdefvar = step.input?.markdefvar ?? (ref + '_markdef' + (step.input?.suffix ?? ''))
316
- const srcdatavar = step.input?.srcdatavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
350
+ const ref = step.input.ref ?? entity.name + '_ref01'
351
+ const entvar = step.input.entvar ?? ref + '_ent'
352
+ const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
353
+ const resdatavar = step.input.resdatavar ?? (ref + '_resdata' + (step.input.suffix ?? ''))
354
+ const markdefvar = step.input.markdefvar ?? (ref + '_markdef' + (step.input.suffix ?? ''))
355
+ const srcdatavar = step.input.srcdatavar ?? (ref + '_data' + (step.input.suffix ?? ''))
317
356
 
318
357
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
319
358
  const needsEnt = !priorSteps.some((s: any) =>
320
359
  ['create', 'list', 'load', 'update', 'remove'].includes(s.op))
321
360
 
361
+ const hasEntIdU = null != entity.id
362
+
322
363
  Content(` # UPDATE
323
364
  `)
324
365
  if (needsEnt) {
@@ -326,8 +367,11 @@ const generateUpdate: OpGen = (ctx, step, index) => {
326
367
  `)
327
368
  }
328
369
  Content(` ${datavar}_up = {
329
- "id" => ${srcdatavar}["id"],
330
370
  `)
371
+ if (hasEntIdU) {
372
+ Content(` "id" => ${srcdatavar}["id"],
373
+ `)
374
+ }
331
375
 
332
376
  if (step.data) {
333
377
  const dataEntries = Object.entries(step.data).filter(([k]: any) => k !== 'id' && !k.endsWith('$'))
@@ -342,7 +386,7 @@ const generateUpdate: OpGen = (ctx, step, index) => {
342
386
 
343
387
  if (step.spec) {
344
388
  for (const spec of step.spec) {
345
- if ('TextFieldMark' === spec.apply && null != step.input?.textfield) {
389
+ if ('TextFieldMark' === spec.apply && null != step.input.textfield) {
346
390
  const fieldname = step.input.textfield
347
391
  const fieldvalue = spec.def?.mark ?? `Mark01-${ref}`
348
392
  Content(`
@@ -359,12 +403,15 @@ const generateUpdate: OpGen = (ctx, step, index) => {
359
403
  assert_nil err
360
404
  ${resdatavar} = Helpers.to_map(${resdatavar}_result)
361
405
  assert !${resdatavar}.nil?
362
- assert_equal ${resdatavar}["id"], ${datavar}_up["id"]
363
406
  `)
407
+ if (hasEntIdU) {
408
+ Content(` assert_equal ${resdatavar}["id"], ${datavar}_up["id"]
409
+ `)
410
+ }
364
411
 
365
412
  if (step.spec) {
366
413
  for (const spec of step.spec) {
367
- if ('TextFieldMark' === spec.apply && null != step.input?.textfield) {
414
+ if ('TextFieldMark' === spec.apply && null != step.input.textfield) {
368
415
  Content(` assert_equal ${resdatavar}[${markdefvar}_name], ${markdefvar}_value
369
416
  `)
370
417
  }
@@ -375,11 +422,11 @@ const generateUpdate: OpGen = (ctx, step, index) => {
375
422
 
376
423
  const generateLoad: OpGen = (ctx, step, index) => {
377
424
  const { entity, flow } = ctx
378
- const ref = step.input?.ref ?? entity.name + '_ref01'
379
- const entvar = step.input?.entvar ?? ref + '_ent'
380
- const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
381
- const datavar = step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
382
- const srcdatavar = step.input?.srcdatavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
425
+ const ref = step.input.ref ?? entity.name + '_ref01'
426
+ const entvar = step.input.entvar ?? ref + '_ent'
427
+ const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
428
+ const datavar = step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? ''))
429
+ const srcdatavar = step.input.srcdatavar ?? (ref + '_data' + (step.input.suffix ?? ''))
383
430
 
384
431
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
385
432
  const hasEntVar = priorSteps.some((s: any) =>
@@ -390,20 +437,22 @@ const generateLoad: OpGen = (ctx, step, index) => {
390
437
  const hasSrcData = (!flowHasCreate && srcdatavar === (preambleRef + '_data')) ||
391
438
  priorSteps.some((s: any) => {
392
439
  if ('create' === s.op) {
393
- const priorRef = s.input?.ref ?? entity.name + '_ref01'
394
- const priorDatvar = s.input?.datavar ?? (priorRef + '_data' + (s.input?.suffix ?? ''))
440
+ const priorRef = s.input.ref ?? entity.name + '_ref01'
441
+ const priorDatvar = s.input.datavar ?? (priorRef + '_data' + (s.input.suffix ?? ''))
395
442
  return priorDatvar === srcdatavar
396
443
  }
397
444
  return false
398
445
  })
399
446
 
447
+ const hasEntId = null != entity.id
448
+
400
449
  Content(` # LOAD
401
450
  `)
402
451
  if (!hasEntVar) {
403
452
  Content(` ${entvar} = client.${entity.Name}(nil)
404
453
  `)
405
454
  }
406
- if (!hasSrcData) {
455
+ if (!hasSrcData && hasEntId) {
407
456
  Content(` ${srcdatavar}_raw = Vs.items(Helpers.to_map(
408
457
  Vs.getpath(setup[:data], "existing.${entity.name}")))
409
458
  ${srcdatavar} = nil
@@ -412,7 +461,8 @@ const generateLoad: OpGen = (ctx, step, index) => {
412
461
  end
413
462
  `)
414
463
  }
415
- Content(` ${matchvar} = {
464
+ if (hasEntId) {
465
+ Content(` ${matchvar} = {
416
466
  "id" => ${srcdatavar}["id"],
417
467
  }
418
468
  ${datavar}_loaded, err = ${entvar}.load(${matchvar}, nil)
@@ -421,32 +471,50 @@ const generateLoad: OpGen = (ctx, step, index) => {
421
471
  assert !${datavar}_load_result.nil?
422
472
  assert_equal ${datavar}_load_result["id"], ${srcdatavar}["id"]
423
473
  `)
474
+ }
475
+ else {
476
+ Content(` ${matchvar} = {}
477
+ ${datavar}_loaded, err = ${entvar}.load(${matchvar}, nil)
478
+ assert_nil err
479
+ assert !${datavar}_loaded.nil?
480
+ `)
481
+ }
424
482
  }
425
483
 
426
484
 
427
485
  const generateRemove: OpGen = (ctx, step, index) => {
428
486
  const { entity, flow } = ctx
429
- const ref = step.input?.ref ?? entity.name + '_ref01'
430
- const entvar = step.input?.entvar ?? ref + '_ent'
431
- const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
432
- const srcdatavar = step.input?.srcdatavar ?? (ref + '_data')
487
+ const ref = step.input.ref ?? entity.name + '_ref01'
488
+ const entvar = step.input.entvar ?? ref + '_ent'
489
+ const matchvar = step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? ''))
490
+ const srcdatavar = step.input.srcdatavar ?? (ref + '_data')
433
491
 
434
492
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
435
493
  const needsEnt = !priorSteps.some((s: any) =>
436
494
  ['create', 'list', 'load', 'update', 'remove'].includes(s.op))
437
495
 
496
+ const hasEntIdR = null != entity.id
497
+
438
498
  Content(` # REMOVE
439
499
  `)
440
500
  if (needsEnt) {
441
501
  Content(` ${entvar} = client.${entity.Name}(nil)
442
502
  `)
443
503
  }
444
- Content(` ${matchvar} = {
504
+ if (hasEntIdR) {
505
+ Content(` ${matchvar} = {
445
506
  "id" => ${srcdatavar}["id"],
446
507
  }
447
508
  _, err = ${entvar}.remove(${matchvar}, nil)
448
509
  assert_nil err
449
510
  `)
511
+ }
512
+ else {
513
+ Content(` ${matchvar} = {}
514
+ _, err = ${entvar}.remove(${matchvar}, nil)
515
+ assert_nil err
516
+ `)
517
+ }
450
518
  }
451
519
 
452
520
 
@@ -164,14 +164,30 @@ class ProjectNameSDK
164
164
 
165
165
  if fetched.is_a?(Hash)
166
166
  status = ProjectNameHelpers.to_int(VoxgigStruct.getprop(fetched, "status"))
167
+ headers = VoxgigStruct.getprop(fetched, "headers") || {}
168
+
169
+ # No-body responses (204, 304) and explicit zero content-length must
170
+ # skip JSON parsing — calling json() on an empty body errors.
171
+ content_length = headers.is_a?(Hash) ? headers["content-length"] : nil
172
+ no_body = status == 204 || status == 304 || content_length.to_s == "0"
173
+
167
174
  json_data = nil
168
- jf = VoxgigStruct.getprop(fetched, "json")
169
- json_data = jf.call if jf.is_a?(Proc)
175
+ unless no_body
176
+ jf = VoxgigStruct.getprop(fetched, "json")
177
+ if jf.is_a?(Proc)
178
+ begin
179
+ json_data = jf.call
180
+ rescue StandardError
181
+ # Non-JSON body — leave data nil, keep status/headers.
182
+ json_data = nil
183
+ end
184
+ end
185
+ end
170
186
 
171
187
  return {
172
188
  "ok" => status >= 200 && status < 300,
173
189
  "status" => status,
174
- "headers" => VoxgigStruct.getprop(fetched, "headers"),
190
+ "headers" => headers,
175
191
  "data" => json_data,
176
192
  }, nil
177
193
  end
@@ -10,6 +10,7 @@ import {
10
10
  cmp,
11
11
  each,
12
12
  indent,
13
+ isAuthActive,
13
14
  } from '@voxgig/sdkgen'
14
15
 
15
16
 
@@ -40,6 +41,21 @@ const Config = cmp(async function Config(props: any) {
40
41
 
41
42
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
42
43
 
44
+ const authActive = isAuthActive(model)
45
+ let authPrefix = 'Bearer'
46
+ try {
47
+ const v = getModelPath(model, `main.${KIT}.config.auth.prefix`,
48
+ { only_active: false, required: false })
49
+ if (null != v) authPrefix = v
50
+ } catch (_e) { /* ignore */ }
51
+ const authBlock = authActive
52
+ ? `auth: {
53
+ prefix: '${authPrefix}',
54
+ },
55
+
56
+ `
57
+ : ''
58
+
43
59
  File({ name: 'Config.' + target.ext }, () => {
44
60
 
45
61
  Fragment({
@@ -47,6 +63,8 @@ const Config = cmp(async function Config(props: any) {
47
63
 
48
64
  replace: {
49
65
 
66
+ "'AUTHBLOCK'": authBlock,
67
+
50
68
  "'HEADERS'": indent(JSON.stringify(headers, null, 2), 4).trim(),
51
69
 
52
70
  '// #ImportFeatures': () => each(feature, (f: any) => {
@@ -57,7 +57,7 @@ const Package = cmp(async function Package(props: any) {
57
57
  type: 'commonjs',
58
58
  types: `dist/${SdkName}SDK.d.ts`,
59
59
  scripts: {
60
- 'test': 'node --enable-source-maps --test \'dist-test/**/*.test.js\'',
60
+ 'test': 'node --enable-source-maps --test-concurrency=1 --test \'dist-test/**/*.test.js\'',
61
61
  'test-some': 'node --enable-source-maps --experimental-test-isolation=none ' +
62
62
  '--test-name-pattern=\"$TEST_PATTERN\" --test \'dist-test/**/*.test.js\'',
63
63
  'test-utility': 'node --enable-source-maps --test test/utility/*.test.ts',
@@ -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,5 +1,5 @@
1
1
 
2
- import { cmp, Content } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isAuthActive } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -10,6 +10,13 @@ import {
10
10
  const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
11
11
  const { target, ctx$: { model } } = props
12
12
 
13
+ const authActive = isAuthActive(model)
14
+ const apikeyTesterCtor = authActive
15
+ ? `new ${model.const.Name}SDK({ apikey: '...' })`
16
+ : `new ${model.const.Name}SDK()`
17
+ const apikeyExtendField = authActive ? `\n apikey: '...',` : ''
18
+ const apikeyEnvLine = authActive ? `\n${model.NAME}_APIKEY=<your-key>` : ''
19
+
13
20
  Content(`### Make a direct HTTP request
14
21
 
15
22
  For endpoints not covered by entity methods:
@@ -57,7 +64,7 @@ const result = await client.Planet().load({ id: 'test01' })
57
64
  You can also use the instance method:
58
65
 
59
66
  \`\`\`ts
60
- const client = new ${model.const.Name}SDK({ apikey: '...' })
67
+ const client = ${apikeyTesterCtor}
61
68
  const testClient = client.tester()
62
69
  \`\`\`
63
70
 
@@ -92,8 +99,7 @@ const logger = {
92
99
  },
93
100
  }
94
101
 
95
- const client = new ${model.const.Name}SDK({
96
- apikey: '...',
102
+ const client = new ${model.const.Name}SDK({${apikeyExtendField}
97
103
  extend: [logger],
98
104
  })
99
105
  \`\`\`
@@ -103,8 +109,7 @@ const client = new ${model.const.Name}SDK({
103
109
  Create a \`.env.local\` file at the project root:
104
110
 
105
111
  \`\`\`
106
- ${model.NAME}_TEST_LIVE=TRUE
107
- ${model.NAME}_APIKEY=<your-key>
112
+ ${model.NAME}_TEST_LIVE=TRUE${apikeyEnvLine}
108
113
  \`\`\`
109
114
 
110
115
  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 a type-safe, entity-oriented interface with full async/await support.
11
+
12
+ `)
13
+ })
14
+
15
+
16
+ export {
17
+ ReadmeIntro
18
+ }