@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,6 +23,7 @@ import {
18
23
  each,
19
24
  buildIdNames,
20
25
  getMatchEntries,
26
+ isAuthActive,
21
27
  } from '@voxgig/sdkgen'
22
28
 
23
29
 
@@ -27,33 +33,42 @@ function goVar(name: string): string {
27
33
  }
28
34
 
29
35
 
30
- type OpGen = (ctx: GenCtx, step: any, index: any) => void
31
-
36
+ // Go's GenCtx mirrors the shared shape (see TestEntity_ts.ts) plus a
37
+ // `gomodule` slot used to build qualified package paths in emitted code.
32
38
  type GenCtx = {
33
- model: any
34
- entity: any
39
+ model: Model
40
+ entity: ModelEntity
35
41
  gomodule: string
36
- flow: any
42
+ flow: ModelEntityFlow
37
43
  PROJUPPER: string
38
44
  }
39
45
 
46
+ type OpGen = (ctx: GenCtx, step: ModelEntityFlowStep, index: number) => void
47
+
40
48
 
41
49
  const TestEntity = cmp(function TestEntity(props: any) {
42
50
  const ctx$ = props.ctx$
43
- const model = ctx$.model
51
+ const model: Model = ctx$.model
44
52
 
45
53
  const target = props.target
46
- const entity = props.entity
47
- const gomodule = props.gomodule
48
-
49
- const basicflow = getModelPath(model, `main.${KIT}.flow.Basic${entity.Name}Flow`)
50
- const dobasic = basicflow && true === basicflow.active
54
+ const entity: ModelEntity = props.entity
55
+ const gomodule: string = props.gomodule
51
56
 
52
- if (!dobasic) {
57
+ const basicflow: ModelEntityFlow | undefined =
58
+ getModelPath(model, `main.${KIT}.flow.Basic${nom(entity, 'Name')}Flow`)
59
+ if (null == basicflow || true !== basicflow.active) {
53
60
  return
54
61
  }
55
62
 
56
- const PROJUPPER = model.const.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
63
+ const PROJUPPER = nom(model.const, 'Name').toUpperCase().replace(/[^A-Z_]/g, '_')
64
+
65
+ const authActive = isAuthActive(model)
66
+ const apikeyEnvEntry = authActive
67
+ ? `\n\t\t"${PROJUPPER}_APIKEY": "NONE",`
68
+ : ''
69
+ const apikeyLiveField = authActive
70
+ ? `\n\t\t\t\t"apikey": env["${PROJUPPER}_APIKEY"],`
71
+ : ''
57
72
 
58
73
  const idnames = buildIdNames(entity, basicflow)
59
74
  const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
@@ -72,7 +87,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
72
87
  // when no step needs it, otherwise Go's strict unused-import check fails.
73
88
  const needsFmt = allSteps.some((s: any) =>
74
89
  s.op === 'update' &&
75
- s.input?.textfield &&
90
+ s.input.textfield &&
76
91
  Array.isArray(s.spec) &&
77
92
  s.spec.some((sp: any) => sp.apply === 'TextFieldMark'))
78
93
 
@@ -85,6 +100,7 @@ import (
85
100
  "os"
86
101
  "path/filepath"
87
102
  "runtime"
103
+ "strings"
88
104
  "testing"
89
105
  "time"
90
106
 
@@ -105,6 +121,27 @@ func Test${entity.Name}Entity(t *testing.T) {
105
121
 
106
122
  t.Run("basic", func(t *testing.T) {
107
123
  setup := ${entity.name}BasicSetup(nil)
124
+ // Per-op sdk-test-control.json skip — basic test exercises a flow
125
+ // with multiple ops; skipping any op skips the whole flow.
126
+ _mode := "unit"
127
+ if setup.live {
128
+ _mode = "live"
129
+ }
130
+ for _, _op := range []string{${(Array.from(new Set((allSteps as any[]).map((s: any) => s.op).filter(Boolean)))).map(o => `"${o}"`).join(', ')}} {
131
+ if _shouldSkip, _reason := isControlSkipped("entityOp", "${entity.name}." + _op, _mode); _shouldSkip {
132
+ if _reason == "" {
133
+ _reason = "skipped via sdk-test-control.json"
134
+ }
135
+ t.Skip(_reason)
136
+ return
137
+ }
138
+ }
139
+ // The basic flow consumes synthetic IDs from the fixture. In live mode
140
+ // without an *_ENTID env override, those IDs hit the live API and 4xx.
141
+ if setup.syntheticOnly {
142
+ t.Skip("live entity test uses synthetic IDs from fixture — set ${PROJUPPER}_TEST_${entity.name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID JSON to run live")
143
+ return
144
+ }
108
145
  ${allSteps.length > 0 ? '\t\tclient := setup.client\n\n' : ''}`)
109
146
 
110
147
  // Check if the flow has a create step; if not, bootstrap entity data
@@ -178,11 +215,16 @@ ${allSteps.length > 0 ? '\t\tclient := setup.client\n\n' : ''}`)
178
215
  Content('\t)\n')
179
216
 
180
217
  Content(`
218
+ // Detect ENTID env override before envOverride consumes it. When live
219
+ // mode is on without a real override, the basic test runs against synthetic
220
+ // IDs from the fixture and 4xx's. Surface this so the test can skip.
221
+ entidEnvRaw := os.Getenv("${PROJUPPER}_TEST_${entity.name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID")
222
+ idmapOverridden := entidEnvRaw != "" && strings.HasPrefix(strings.TrimSpace(entidEnvRaw), "{")
223
+
181
224
  env := envOverride(map[string]any{
182
225
  "${PROJUPPER}_TEST_${entity.name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID": idmap,
183
226
  "${PROJUPPER}_TEST_LIVE": "FALSE",
184
- "${PROJUPPER}_TEST_EXPLAIN": "FALSE",
185
- "${PROJUPPER}_APIKEY": "NONE",
227
+ "${PROJUPPER}_TEST_EXPLAIN": "FALSE",${apikeyEnvEntry}
186
228
  })
187
229
 
188
230
  idmapResolved := core.ToMapAny(env["${PROJUPPER}_TEST_${entity.name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID"])
@@ -203,21 +245,23 @@ ${allSteps.length > 0 ? '\t\tclient := setup.client\n\n' : ''}`)
203
245
  Content(`
204
246
  if env["${PROJUPPER}_TEST_LIVE"] == "TRUE" {
205
247
  mergedOpts := vs.Merge([]any{
206
- map[string]any{
207
- "apikey": env["${PROJUPPER}_APIKEY"],
248
+ map[string]any{${apikeyLiveField}
208
249
  },
209
250
  extra,
210
251
  })
211
252
  client = sdk.New${model.const.Name}SDK(core.ToMapAny(mergedOpts))
212
253
  }
213
254
 
255
+ live := env["${PROJUPPER}_TEST_LIVE"] == "TRUE"
214
256
  return &entityTestSetup{
215
- client: client,
216
- data: entityData,
217
- idmap: idmapResolved,
218
- env: env,
219
- explain: env["${PROJUPPER}_TEST_EXPLAIN"] == "TRUE",
220
- now: time.Now().UnixMilli(),
257
+ client: client,
258
+ data: entityData,
259
+ idmap: idmapResolved,
260
+ env: env,
261
+ explain: env["${PROJUPPER}_TEST_EXPLAIN"] == "TRUE",
262
+ live: live,
263
+ syntheticOnly: live && !idmapOverridden,
264
+ now: time.Now().UnixMilli(),
221
265
  }
222
266
  }
223
267
  `)
@@ -227,9 +271,9 @@ ${allSteps.length > 0 ? '\t\tclient := setup.client\n\n' : ''}`)
227
271
 
228
272
  const generateCreate: OpGen = (ctx, step, index) => {
229
273
  const { entity, flow } = ctx
230
- const ref = step.input?.ref ?? entity.name + '_ref01'
231
- const entvar = goVar(step.input?.entvar ?? ref + '_ent')
232
- const datavar = goVar(step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? '')))
274
+ const ref = step.input.ref ?? entity.name + '_ref01'
275
+ const entvar = goVar(step.input.entvar ?? ref + '_ent')
276
+ const datavar = goVar(step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? '')))
233
277
 
234
278
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
235
279
  const needsEnt = !priorSteps.some((s: any) =>
@@ -237,8 +281,8 @@ const generateCreate: OpGen = (ctx, step, index) => {
237
281
 
238
282
  const hasDatvar = priorSteps.some((s: any) => {
239
283
  if ('create' === s.op) {
240
- const priorRef = s.input?.ref ?? entity.name + '_ref01'
241
- const priorDatvar = goVar(s.input?.datavar ?? (priorRef + '_data' + (s.input?.suffix ?? '')))
284
+ const priorRef = s.input.ref ?? entity.name + '_ref01'
285
+ const priorDatvar = goVar(s.input.datavar ?? (priorRef + '_data' + (s.input.suffix ?? '')))
242
286
  return priorDatvar === datavar
243
287
  }
244
288
  return false
@@ -278,19 +322,22 @@ const generateCreate: OpGen = (ctx, step, index) => {
278
322
  if ${datavar} == nil {
279
323
  t.Fatal("expected create result to be a map")
280
324
  }
281
- if ${datavar}["id"] == nil {
325
+ `)
326
+ if (null != ctx.entity.id) {
327
+ Content(` if ${datavar}["id"] == nil {
282
328
  t.Fatal("expected created entity to have an id")
283
329
  }
284
330
  `)
331
+ }
285
332
  }
286
333
 
287
334
 
288
335
  const generateList: OpGen = (ctx, step, index) => {
289
336
  const { entity, flow } = ctx
290
- const ref = step.input?.ref ?? entity.name + '_ref01'
291
- const entvar = goVar(step.input?.entvar ?? ref + '_ent')
292
- const matchvar = goVar(step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? '')))
293
- const listvar = goVar(step.input?.listvar ?? (ref + '_list' + (step.input?.suffix ?? '')))
337
+ const ref = step.input.ref ?? entity.name + '_ref01'
338
+ const entvar = goVar(step.input.entvar ?? ref + '_ent')
339
+ const matchvar = goVar(step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? '')))
340
+ const listvar = goVar(step.input.listvar ?? (ref + '_list' + (step.input.suffix ?? '')))
294
341
 
295
342
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
296
343
  const needsEnt = !priorSteps.some((s: any) =>
@@ -326,7 +373,7 @@ const generateList: OpGen = (ctx, step, index) => {
326
373
  if ('ItemExists' !== v.apply && 'ItemNotExists' !== v.apply) return false
327
374
  const validRef = v.def?.ref
328
375
  return validRef && allSteps.some((s: any) => 'create' === s.op &&
329
- ((s.input?.ref ?? entity.name + '_ref01') === validRef))
376
+ ((s.input.ref ?? entity.name + '_ref01') === validRef))
330
377
  })
331
378
  const listvarBind = listvarUsed ? listvar : '_'
332
379
 
@@ -349,7 +396,7 @@ const generateList: OpGen = (ctx, step, index) => {
349
396
  for (const validator of step.valid) {
350
397
  const validRef = validator.def?.ref
351
398
  const hasRefData = validRef && allSteps.some((s: any) => 'create' === s.op &&
352
- ((s.input?.ref ?? entity.name + '_ref01') === validRef))
399
+ ((s.input.ref ?? entity.name + '_ref01') === validRef))
353
400
 
354
401
  if ('ItemExists' === validator.apply && hasRefData) {
355
402
  const refDataVar = goVar(validRef + '_data')
@@ -375,17 +422,19 @@ const generateList: OpGen = (ctx, step, index) => {
375
422
 
376
423
  const generateUpdate: OpGen = (ctx, step, index) => {
377
424
  const { entity, flow } = ctx
378
- const ref = step.input?.ref ?? entity.name + '_ref01'
379
- const entvar = goVar(step.input?.entvar ?? ref + '_ent')
380
- const datavar = goVar(step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? '')))
381
- const resdatavar = goVar(step.input?.resdatavar ?? (ref + '_resdata' + (step.input?.suffix ?? '')))
382
- const markdefvar = goVar(step.input?.markdefvar ?? (ref + '_markdef' + (step.input?.suffix ?? '')))
383
- const srcdatavar = goVar(step.input?.srcdatavar ?? (ref + '_data' + (step.input?.suffix ?? '')))
425
+ const ref = step.input.ref ?? entity.name + '_ref01'
426
+ const entvar = goVar(step.input.entvar ?? ref + '_ent')
427
+ const datavar = goVar(step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? '')))
428
+ const resdatavar = goVar(step.input.resdatavar ?? (ref + '_resdata' + (step.input.suffix ?? '')))
429
+ const markdefvar = goVar(step.input.markdefvar ?? (ref + '_markdef' + (step.input.suffix ?? '')))
430
+ const srcdatavar = goVar(step.input.srcdatavar ?? (ref + '_data' + (step.input.suffix ?? '')))
384
431
 
385
432
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
386
433
  const needsEnt = !priorSteps.some((s: any) =>
387
434
  ['create', 'list', 'load', 'update', 'remove'].includes(s.op))
388
435
 
436
+ const hasEntIdU = null != entity.id
437
+
389
438
  Content(` // UPDATE
390
439
  `)
391
440
  if (needsEnt) {
@@ -393,8 +442,11 @@ const generateUpdate: OpGen = (ctx, step, index) => {
393
442
  `)
394
443
  }
395
444
  Content(` ${datavar}Up := map[string]any{
396
- "id": ${srcdatavar}["id"],
397
445
  `)
446
+ if (hasEntIdU) {
447
+ Content(` "id": ${srcdatavar}["id"],
448
+ `)
449
+ }
398
450
 
399
451
  // Add data entries from step.data
400
452
  if (step.data) {
@@ -411,7 +463,7 @@ const generateUpdate: OpGen = (ctx, step, index) => {
411
463
  // Handle TextFieldMark spec
412
464
  if (step.spec) {
413
465
  for (const spec of step.spec) {
414
- if ('TextFieldMark' === spec.apply && null != step.input?.textfield) {
466
+ if ('TextFieldMark' === spec.apply && null != step.input.textfield) {
415
467
  const fieldname = step.input.textfield
416
468
  const fieldvalue = spec.def?.mark ?? `Mark01-${ref}`
417
469
  Content(`
@@ -432,15 +484,18 @@ const generateUpdate: OpGen = (ctx, step, index) => {
432
484
  if ${resdatavar} == nil {
433
485
  t.Fatal("expected update result to be a map")
434
486
  }
435
- if ${resdatavar}["id"] != ${datavar}Up["id"] {
487
+ `)
488
+ if (hasEntIdU) {
489
+ Content(` if ${resdatavar}["id"] != ${datavar}Up["id"] {
436
490
  t.Fatal("expected update result id to match")
437
491
  }
438
492
  `)
493
+ }
439
494
 
440
495
  // Assert TextFieldMark
441
496
  if (step.spec) {
442
497
  for (const spec of step.spec) {
443
- if ('TextFieldMark' === spec.apply && null != step.input?.textfield) {
498
+ if ('TextFieldMark' === spec.apply && null != step.input.textfield) {
444
499
  Content(` if ${resdatavar}[${markdefvar}Name] != ${markdefvar}Value {
445
500
  t.Fatalf("expected %s to be updated, got %v", ${markdefvar}Name, ${resdatavar}[${markdefvar}Name])
446
501
  }
@@ -453,11 +508,11 @@ const generateUpdate: OpGen = (ctx, step, index) => {
453
508
 
454
509
  const generateLoad: OpGen = (ctx, step, index) => {
455
510
  const { entity, flow } = ctx
456
- const ref = step.input?.ref ?? entity.name + '_ref01'
457
- const entvar = goVar(step.input?.entvar ?? ref + '_ent')
458
- const matchvar = goVar(step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? '')))
459
- const datavar = goVar(step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? '')))
460
- const srcdatavar = goVar(step.input?.srcdatavar ?? (ref + '_data' + (step.input?.suffix ?? '')))
511
+ const ref = step.input.ref ?? entity.name + '_ref01'
512
+ const entvar = goVar(step.input.entvar ?? ref + '_ent')
513
+ const matchvar = goVar(step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? '')))
514
+ const datavar = goVar(step.input.datavar ?? (ref + '_data' + (step.input.suffix ?? '')))
515
+ const srcdatavar = goVar(step.input.srcdatavar ?? (ref + '_data' + (step.input.suffix ?? '')))
461
516
 
462
517
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
463
518
  const hasEntVar = priorSteps.some((s: any) =>
@@ -469,20 +524,22 @@ const generateLoad: OpGen = (ctx, step, index) => {
469
524
  const hasSrcData = (!flowHasCreate && srcdatavar === goVar(preambleRef + '_data')) ||
470
525
  priorSteps.some((s: any) => {
471
526
  if ('create' === s.op) {
472
- const priorRef = s.input?.ref ?? entity.name + '_ref01'
473
- const priorDatvar = goVar(s.input?.datavar ?? (priorRef + '_data' + (s.input?.suffix ?? '')))
527
+ const priorRef = s.input.ref ?? entity.name + '_ref01'
528
+ const priorDatvar = goVar(s.input.datavar ?? (priorRef + '_data' + (s.input.suffix ?? '')))
474
529
  return priorDatvar === srcdatavar
475
530
  }
476
531
  return false
477
532
  })
478
533
 
534
+ const hasEntId = null != entity.id
535
+
479
536
  Content(` // LOAD
480
537
  `)
481
538
  if (!hasEntVar) {
482
539
  Content(` ${entvar} := client.${entity.Name}(nil)
483
540
  `)
484
541
  }
485
- if (!hasSrcData) {
542
+ if (!hasSrcData && hasEntId) {
486
543
  Content(` ${srcdatavar}Raw := vs.Items(core.ToMapAny(vs.GetPath("existing.${entity.name}", setup.data)))
487
544
  var ${srcdatavar} map[string]any
488
545
  if len(${srcdatavar}Raw) > 0 {
@@ -490,7 +547,8 @@ const generateLoad: OpGen = (ctx, step, index) => {
490
547
  }
491
548
  `)
492
549
  }
493
- Content(` ${matchvar} := map[string]any{
550
+ if (hasEntId) {
551
+ Content(` ${matchvar} := map[string]any{
494
552
  "id": ${srcdatavar}["id"],
495
553
  }
496
554
  ${datavar}Loaded, err := ${entvar}.Load(${matchvar}, nil)
@@ -505,15 +563,27 @@ const generateLoad: OpGen = (ctx, step, index) => {
505
563
  t.Fatal("expected load result id to match")
506
564
  }
507
565
  `)
566
+ }
567
+ else {
568
+ Content(` ${matchvar} := map[string]any{}
569
+ ${datavar}Loaded, err := ${entvar}.Load(${matchvar}, nil)
570
+ if err != nil {
571
+ t.Fatalf("load failed: %v", err)
572
+ }
573
+ if ${datavar}Loaded == nil {
574
+ t.Fatal("expected load result to be non-nil")
575
+ }
576
+ `)
577
+ }
508
578
  }
509
579
 
510
580
 
511
581
  const generateRemove: OpGen = (ctx, step, index) => {
512
582
  const { entity, flow } = ctx
513
- const ref = step.input?.ref ?? entity.name + '_ref01'
514
- const entvar = goVar(step.input?.entvar ?? ref + '_ent')
515
- const matchvar = goVar(step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? '')))
516
- const srcdatavar = goVar(step.input?.srcdatavar ?? (ref + '_data'))
583
+ const ref = step.input.ref ?? entity.name + '_ref01'
584
+ const entvar = goVar(step.input.entvar ?? ref + '_ent')
585
+ const matchvar = goVar(step.input.matchvar ?? (ref + '_match' + (step.input.suffix ?? '')))
586
+ const srcdatavar = goVar(step.input.srcdatavar ?? (ref + '_data'))
517
587
 
518
588
  const priorSteps = Object.values(flow.step).slice(0, Number(index)) as any[]
519
589
  const needsEnt = !priorSteps.some((s: any) =>
@@ -523,13 +593,16 @@ const generateRemove: OpGen = (ctx, step, index) => {
523
593
  // otherwise reuse the `err` from a prior op step.
524
594
  const errOp = needsEnt ? ':=' : '='
525
595
 
596
+ const hasEntIdR = null != entity.id
597
+
526
598
  Content(` // REMOVE
527
599
  `)
528
600
  if (needsEnt) {
529
601
  Content(` ${entvar} := client.${entity.Name}(nil)
530
602
  `)
531
603
  }
532
- Content(` ${matchvar} := map[string]any{
604
+ if (hasEntIdR) {
605
+ Content(` ${matchvar} := map[string]any{
533
606
  "id": ${srcdatavar}["id"],
534
607
  }
535
608
  _, err ${errOp} ${entvar}.Remove(${matchvar}, nil)
@@ -537,6 +610,15 @@ const generateRemove: OpGen = (ctx, step, index) => {
537
610
  t.Fatalf("remove failed: %v", err)
538
611
  }
539
612
  `)
613
+ }
614
+ else {
615
+ Content(` ${matchvar} := map[string]any{}
616
+ _, err ${errOp} ${entvar}.Remove(${matchvar}, nil)
617
+ if err != nil {
618
+ t.Fatalf("remove failed: %v", err)
619
+ }
620
+ `)
621
+ }
540
622
  }
541
623
 
542
624
 
@@ -20,8 +20,8 @@ const Test = cmp(function Test(props: any) {
20
20
  const { target } = props
21
21
 
22
22
  // Module name: concatenated lowercase
23
- const orgPrefix = (model.origin || '').replace(/-sdk$/, '').replace(/[^a-z0-9]/gi, '')
24
- const gomodule = orgPrefix + model.name.replace(/[^a-z0-9]/gi, '').toLowerCase() + 'sdk'
23
+ // Go module path == repo path on GitHub (org from model.origin).
24
+ const gomodule = `github.com/${model.origin || 'voxgig-sdk'}/${model.name}-sdk`
25
25
 
26
26
  Folder({ name: 'test' }, () => {
27
27
 
@@ -1,6 +1,8 @@
1
1
  package core
2
2
 
3
3
  import (
4
+ "fmt"
5
+
4
6
  vs "github.com/voxgig/struct"
5
7
  )
6
8
 
@@ -209,17 +211,32 @@ func (sdk *ProjectNameSDK) Direct(fetchargs map[string]any) (map[string]any, err
209
211
 
210
212
  if fm, ok := fetched.(map[string]any); ok {
211
213
  status := ToInt(vs.GetProp(fm, "status"))
214
+ headers := vs.GetProp(fm, "headers")
215
+
216
+ // No-body responses (204, 304) and explicit zero content-length
217
+ // must skip JSON parsing — calling json() on an empty body errors.
218
+ var contentLength string
219
+ if hm, ok := headers.(map[string]any); ok {
220
+ if cl, ok := hm["content-length"]; ok {
221
+ contentLength = fmt.Sprintf("%v", cl)
222
+ }
223
+ }
224
+ noBody := status == 204 || status == 304 || contentLength == "0"
225
+
212
226
  var jsonData any
213
- if jf := vs.GetProp(fm, "json"); jf != nil {
214
- if f, ok := jf.(func() any); ok {
215
- jsonData = f()
227
+ if !noBody {
228
+ if jf := vs.GetProp(fm, "json"); jf != nil {
229
+ if f, ok := jf.(func() any); ok {
230
+ // f() returns nil on parse error in our fetcher.
231
+ jsonData = f()
232
+ }
216
233
  }
217
234
  }
218
235
 
219
236
  return map[string]any{
220
237
  "ok": status >= 200 && status < 300,
221
238
  "status": status,
222
- "headers": vs.GetProp(fm, "headers"),
239
+ "headers": headers,
223
240
  "data": jsonData,
224
241
  }, nil
225
242
  }
@@ -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) => {
@@ -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
+ }