@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
@@ -0,0 +1,358 @@
1
+
2
+ import { cmp, each, Content, File, isAuthActive } from '@voxgig/sdkgen'
3
+
4
+ import {
5
+ KIT,
6
+ getModelPath,
7
+ } from '@voxgig/apidef'
8
+
9
+
10
+ const OP_SIGNATURES: Record<string, { sig: string, returns: string, desc: string }> = {
11
+ load: {
12
+ sig: 'load(array $reqmatch, ?array $ctrl = null): array',
13
+ returns: 'array [$result, $err]',
14
+ desc: 'Load a single entity matching the given criteria.',
15
+ },
16
+ list: {
17
+ sig: 'list(array $reqmatch, ?array $ctrl = null): array',
18
+ returns: 'array [$result, $err]',
19
+ desc: 'List entities matching the given criteria. Returns an array.',
20
+ },
21
+ create: {
22
+ sig: 'create(array $reqdata, ?array $ctrl = null): array',
23
+ returns: 'array [$result, $err]',
24
+ desc: 'Create a new entity with the given data.',
25
+ },
26
+ update: {
27
+ sig: 'update(array $reqdata, ?array $ctrl = null): array',
28
+ returns: 'array [$result, $err]',
29
+ desc: 'Update an existing entity. The data must include the entity `id`.',
30
+ },
31
+ remove: {
32
+ sig: 'remove(array $reqmatch, ?array $ctrl = null): array',
33
+ returns: 'array [$result, $err]',
34
+ desc: 'Remove the entity matching the given criteria.',
35
+ },
36
+ }
37
+
38
+
39
+ const ReadmeRef = cmp(function ReadmeRef(props: any) {
40
+ const { target } = props
41
+ const { model } = props.ctx$
42
+
43
+ const entity = getModelPath(model, `main.${KIT}.entity`)
44
+ const feature = getModelPath(model, `main.${KIT}.feature`)
45
+
46
+ const publishedEntities = each(entity).filter((e: any) => e.active !== false)
47
+
48
+
49
+ File({ name: 'REFERENCE.md' }, () => {
50
+
51
+ Content(`# ${model.Name} ${target.title} SDK Reference
52
+
53
+ Complete API reference for the ${model.Name} ${target.title} SDK.
54
+
55
+
56
+ ## ${model.Name}SDK
57
+
58
+ ### Constructor
59
+
60
+ `)
61
+
62
+ Content(`\`\`\`php
63
+ require_once __DIR__ . '/${model.name}_sdk.php';
64
+
65
+ $client = new ${model.const.Name}SDK($options);
66
+ \`\`\`
67
+
68
+ Create a new SDK client instance.
69
+
70
+ **Parameters:**
71
+
72
+ | Name | Type | Description |
73
+ | --- | --- | --- |
74
+ | \`$options\` | \`array\` | SDK configuration options. |
75
+ | \`$options["apikey"]\` | \`string\` | API key for authentication. |
76
+ | \`$options["base"]\` | \`string\` | Base URL for API requests. |
77
+ | \`$options["prefix"]\` | \`string\` | URL prefix appended after base. |
78
+ | \`$options["suffix"]\` | \`string\` | URL suffix appended after path. |
79
+ | \`$options["headers"]\` | \`array\` | Custom headers for all requests. |
80
+ | \`$options["feature"]\` | \`array\` | Feature configuration. |
81
+ | \`$options["system"]\` | \`array\` | System overrides (e.g. custom fetch). |
82
+
83
+ `)
84
+
85
+
86
+ Content(`
87
+ ### Static Methods
88
+
89
+ `)
90
+
91
+ Content(`#### \`${model.const.Name}SDK::test($testopts = null, $sdkopts = null)\`
92
+
93
+ Create a test client with mock features active. Both arguments may be \`null\`.
94
+
95
+ \`\`\`php
96
+ $client = ${model.const.Name}SDK::test();
97
+ \`\`\`
98
+
99
+ `)
100
+
101
+
102
+ Content(`
103
+ ### Instance Methods
104
+
105
+ `)
106
+
107
+
108
+ // Entity factory methods
109
+ publishedEntities.map((ent: any) => {
110
+ Content(`#### \`${ent.Name}($data = null)\`
111
+
112
+ Create a new \`${ent.Name}Entity\` instance. Pass \`null\` for no initial data.
113
+
114
+ `)
115
+ })
116
+
117
+
118
+ Content(`#### \`optionsMap(): array\`
119
+
120
+ Return a deep copy of the current SDK options.
121
+
122
+ #### \`getUtility(): ProjectNameUtility\`
123
+
124
+ Return a copy of the SDK utility object.
125
+
126
+ #### \`direct(array $fetchargs = []): array\`
127
+
128
+ Make a direct HTTP request to any API endpoint. Returns \`[$result, $err]\`.
129
+
130
+ **Parameters:**
131
+
132
+ | Name | Type | Description |
133
+ | --- | --- | --- |
134
+ | \`$fetchargs["path"]\` | \`string\` | URL path with optional \`{param}\` placeholders. |
135
+ | \`$fetchargs["method"]\` | \`string\` | HTTP method (default: \`"GET"\`). |
136
+ | \`$fetchargs["params"]\` | \`array\` | Path parameter values for \`{param}\` substitution. |
137
+ | \`$fetchargs["query"]\` | \`array\` | Query string parameters. |
138
+ | \`$fetchargs["headers"]\` | \`array\` | Request headers (merged with defaults). |
139
+ | \`$fetchargs["body"]\` | \`mixed\` | Request body (arrays are JSON-serialized). |
140
+ | \`$fetchargs["ctrl"]\` | \`array\` | Control options. |
141
+
142
+ **Returns:** \`array [$result, $err]\`
143
+
144
+ #### \`prepare(array $fetchargs = []): array\`
145
+
146
+ Prepare a fetch definition without sending the request. Returns \`[$fetchdef, $err]\`.
147
+
148
+ `)
149
+
150
+
151
+ // Entity reference sections
152
+ publishedEntities.map((ent: any) => {
153
+ const opnames = Object.keys(ent.op || {})
154
+ const fields = ent.fields || []
155
+
156
+ Content(`
157
+ ---
158
+
159
+ ## ${ent.Name}Entity
160
+
161
+ `)
162
+
163
+ if (ent.short) {
164
+ Content(`${ent.short}
165
+
166
+ `)
167
+ }
168
+
169
+ Content(`\`\`\`php
170
+ $${ent.name} = $client->${ent.Name}();
171
+ \`\`\`
172
+
173
+ `)
174
+
175
+
176
+ // Field schema
177
+ if (fields.length > 0) {
178
+ Content(`### Fields
179
+
180
+ | Field | Type | Required | Description |
181
+ | --- | --- | --- | --- |
182
+ `)
183
+ each(fields, (field: any) => {
184
+ const req = field.req ? 'Yes' : 'No'
185
+ const desc = field.short || ''
186
+ Content(`| \`${field.name}\` | \`${field.type || 'any'}\` | ${req} | ${desc} |
187
+ `)
188
+ })
189
+
190
+ Content(`
191
+ `)
192
+
193
+ // Field operations breakdown
194
+ const hasFieldOps = fields.some((f: any) => f.op && Object.keys(f.op).length > 0)
195
+ if (hasFieldOps) {
196
+ Content(`### Field Usage by Operation
197
+
198
+ | Field | load | list | create | update | remove |
199
+ | --- | --- | --- | --- | --- | --- |
200
+ `)
201
+ each(fields, (field: any) => {
202
+ const fops = field.op || {}
203
+ const cols = ['load', 'list', 'create', 'update', 'remove'].map((op: string) => {
204
+ if (!opnames.includes(op)) return '-'
205
+ const fop = fops[op]
206
+ if (null == fop) return '-'
207
+ if (fop.active === false) return '-'
208
+ return 'Yes'
209
+ })
210
+ Content(`| \`${field.name}\` | ${cols.join(' | ')} |
211
+ `)
212
+ })
213
+
214
+ Content(`
215
+ `)
216
+ }
217
+ }
218
+
219
+
220
+ // Operation details
221
+ if (opnames.length > 0) {
222
+ Content(`### Operations
223
+
224
+ `)
225
+
226
+ opnames.map((opname: string) => {
227
+ const info = OP_SIGNATURES[opname]
228
+ if (!info) return
229
+
230
+ Content(`#### \`${info.sig}\`
231
+
232
+ ${info.desc}
233
+
234
+ `)
235
+
236
+ // Show example
237
+ if ('load' === opname || 'remove' === opname) {
238
+ Content(`\`\`\`php
239
+ [$result, $err] = $client->${ent.Name}()->${opname}(["id" => "${ent.name}_id"]);
240
+ \`\`\`
241
+
242
+ `)
243
+ }
244
+ else if ('list' === opname) {
245
+ Content(`\`\`\`php
246
+ [$results, $err] = $client->${ent.Name}()->list([]);
247
+ \`\`\`
248
+
249
+ `)
250
+ }
251
+ else if ('create' === opname) {
252
+ Content(`\`\`\`php
253
+ [$result, $err] = $client->${ent.Name}()->create([
254
+ `)
255
+ each(fields, (field: any) => {
256
+ if ('id' !== field.name && field.req) {
257
+ Content(` "${field.name}" => /* ${field.type || 'value'} */,
258
+ `)
259
+ }
260
+ })
261
+ Content(`]);
262
+ \`\`\`
263
+
264
+ `)
265
+ }
266
+ else if ('update' === opname) {
267
+ Content(`\`\`\`php
268
+ [$result, $err] = $client->${ent.Name}()->update([
269
+ "id" => "${ent.name}_id",
270
+ // Fields to update
271
+ ]);
272
+ \`\`\`
273
+
274
+ `)
275
+ }
276
+ })
277
+ }
278
+
279
+
280
+ // Common methods
281
+ Content(`### Common Methods
282
+
283
+ #### \`dataGet(): array\`
284
+
285
+ Get the entity data. Returns a copy of the current data.
286
+
287
+ #### \`dataSet($data): void\`
288
+
289
+ Set the entity data.
290
+
291
+ #### \`matchGet(): array\`
292
+
293
+ Get the entity match criteria.
294
+
295
+ #### \`matchSet($match): void\`
296
+
297
+ Set the entity match criteria.
298
+
299
+ #### \`make(): ${ent.Name}Entity\`
300
+
301
+ Create a new \`${ent.Name}Entity\` instance with the same client and
302
+ options.
303
+
304
+ #### \`getName(): string\`
305
+
306
+ Return the entity name.
307
+
308
+ `)
309
+ })
310
+
311
+
312
+ // Features section
313
+ const activeFeatures = each(feature).filter((f: any) => f.active)
314
+ if (activeFeatures.length > 0) {
315
+ Content(`
316
+ ---
317
+
318
+ ## Features
319
+
320
+ | Feature | Version | Description |
321
+ | --- | --- | --- |
322
+ `)
323
+
324
+ activeFeatures.map((f: any) => {
325
+ Content(`| \`${f.name}\` | ${f.version || '0.0.1'} | ${f.title || ''} |
326
+ `)
327
+ })
328
+
329
+ Content(`
330
+
331
+ Features are activated via the \`feature\` option:
332
+
333
+ `)
334
+
335
+ Content(`\`\`\`php
336
+ $client = new ${model.const.Name}SDK([
337
+ "feature" => [
338
+ `)
339
+ activeFeatures.map((f: any) => {
340
+ Content(` "${f.name}" => ["active" => true],
341
+ `)
342
+ })
343
+ Content(` ],
344
+ ]);
345
+ \`\`\`
346
+
347
+ `)
348
+ }
349
+
350
+ })
351
+ })
352
+
353
+
354
+
355
+
356
+ export {
357
+ ReadmeRef
358
+ }
@@ -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,
@@ -15,13 +15,15 @@ const ReadmeTopQuick = cmp(function ReadmeTopQuick(props: any) {
15
15
 
16
16
  const exampleEntity = Object.values(entity).find((e: any) => e.active !== false) as any
17
17
 
18
+ const apikeyArg = isAuthActive(model)
19
+ ? `\n "apikey" => getenv("${model.NAME}_APIKEY"),\n`
20
+ : ''
21
+
18
22
  Content(`\`\`\`php
19
23
  <?php
20
24
  require_once '${model.const.Name.toLowerCase()}_sdk.php';
21
25
 
22
- $client = new ${model.const.Name}SDK([
23
- "apikey" => getenv("${model.NAME}_APIKEY"),
24
- ]);
26
+ $client = new ${model.const.Name}SDK([${apikeyArg}]);
25
27
 
26
28
  `)
27
29
 
@@ -1,5 +1,8 @@
1
1
 
2
2
  import {
3
+ Model,
4
+ ModelEntity,
5
+ nom,
3
6
  depluralize,
4
7
  } from '@voxgig/apidef'
5
8
 
@@ -8,6 +11,7 @@ import {
8
11
  File,
9
12
  cmp,
10
13
  snakify,
14
+ isAuthActive,
11
15
  } from '@voxgig/sdkgen'
12
16
 
13
17
 
@@ -51,12 +55,20 @@ function normalizePathParams(
51
55
 
52
56
  const TestDirect = cmp(function TestDirect(props: any) {
53
57
  const ctx$ = props.ctx$
54
- const model = ctx$.model
58
+ const model: Model = ctx$.model
55
59
 
56
60
  const target = props.target
57
- const entity = props.entity
61
+ const entity: ModelEntity = props.entity
58
62
 
59
- const PROJECTNAME = model.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
63
+ const PROJECTNAME = nom(model, 'Name').toUpperCase().replace(/[^A-Z_]/g, '_')
64
+
65
+ const authActive = isAuthActive(model)
66
+ const apikeyEnvEntry = authActive
67
+ ? `\n "${PROJECTNAME}_APIKEY" => "NONE",`
68
+ : ''
69
+ const apikeyLiveField = authActive
70
+ ? `\n "apikey" => $env["${PROJECTNAME}_APIKEY"],`
71
+ : ''
60
72
 
61
73
  const opnames = Object.keys(entity.op)
62
74
  const hasLoad = opnames.includes('load')
@@ -71,13 +83,49 @@ const TestDirect = cmp(function TestDirect(props: any) {
71
83
 
72
84
  const loadPoint = loadOp?.points?.[0]
73
85
  const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
74
- const loadParams = loadPoint?.args?.params || []
86
+ const allLoadParams = loadPoint?.args?.params || []
87
+ // Some upstream OpenAPI specs declare a parameter as `in: path` even when
88
+ // that path has no `{name}` placeholder for it. Only path params that
89
+ // actually appear in the URL template should drive direct-test path-param
90
+ // setup and URL-substitution asserts; otherwise the SDK silently drops
91
+ // them and the URL-includes assert fails.
92
+ const _pathPlaceholders = new Set<string>()
93
+ for (const part of (loadPoint?.parts || [])) {
94
+ if (typeof part === 'string' && part.startsWith('{') && part.endsWith('}')) {
95
+ _pathPlaceholders.add(part.slice(1, -1))
96
+ }
97
+ }
98
+ const _renameMap = (loadPoint?.rename?.param || {}) as Record<string, string>
99
+ const _renamedPlaceholders = new Set<string>()
100
+ for (const ph of _pathPlaceholders) {
101
+ _renamedPlaceholders.add(ph)
102
+ for (const [orig, renamed] of Object.entries(_renameMap)) {
103
+ if (renamed === ph) _renamedPlaceholders.add(orig)
104
+ }
105
+ }
106
+ const loadParams = allLoadParams.filter((p: any) =>
107
+ _renamedPlaceholders.has(p.name) || _renamedPlaceholders.has(p.orig))
75
108
 
76
109
  const listPoint = listOp?.points?.[0]
77
110
  const listPath = listPoint ? normalizePathParams(listPoint.parts || [], listPoint?.args?.params || [], listPoint?.rename?.param) : ''
78
111
  const listParams = listPoint?.args?.params || []
79
112
 
80
- const entidEnvVar = `${PROJECTNAME}_TEST_${entity.Name.toUpperCase().replace(/[^A-Z_]/g, '_')}_ENTID`
113
+ // Required query params with spec-provided examples — needed in live mode.
114
+ const loadQuery = loadPoint?.args?.query || []
115
+ const loadLiveQueryEntries = loadQuery
116
+ .filter((q: any) => q.reqd && undefined !== q.example && null !== q.example)
117
+ const loadLiveQueryLines = loadLiveQueryEntries
118
+ .map((q: any) => ` $query["${q.name}"] = ${JSON.stringify(q.example)};`)
119
+ .join('\n')
120
+
121
+ const loadAllHaveExamples =
122
+ loadParams.length > 0 &&
123
+ loadParams.every((p: any) => undefined !== p.example && null !== p.example)
124
+ const loadExampleLines = loadAllHaveExamples
125
+ ? loadParams.map((p: any) => ` $params["${p.name}"] = ${JSON.stringify(p.example)};`).join('\n')
126
+ : ''
127
+
128
+ const entidEnvVar = `${PROJECTNAME}_TEST_${nom(entity, 'NAME').replace(/[^A-Z_]/g, '_')}_ENTID`
81
129
 
82
130
  File({ name: entity.Name + 'DirectTest.' + target.ext }, () => {
83
131
 
@@ -96,13 +144,34 @@ class ${entity.Name}DirectTest extends TestCase
96
144
  `)
97
145
 
98
146
  if (hasList && listPoint) {
147
+ const listLiveIdKeys: string[] = listParams.map((lp: any) => {
148
+ return lp.name === 'id'
149
+ ? entity.name + '01'
150
+ : lp.name.replace(/_id$/, '') + '01'
151
+ })
152
+ const listSkipBlock = listLiveIdKeys.length > 0
153
+ ? ` if ($setup["live"]) {
154
+ foreach ([${listLiveIdKeys.map(k => `"${k}"`).join(', ')}] as $_liveKey) {
155
+ if (!isset($setup["idmap"][$_liveKey]) || $setup["idmap"][$_liveKey] === null) {
156
+ $this->markTestSkipped("live test needs $_liveKey via *_ENTID env var (synthetic IDs only)");
157
+ return;
158
+ }
159
+ }
160
+ }
161
+ `
162
+ : ''
99
163
  Content(` public function test_direct_list_${entity.name}(): void
100
164
  {
101
165
  $setup = ${entity.name}_direct_setup([
102
166
  ["id" => "direct01"],
103
167
  ["id" => "direct02"],
104
168
  ]);
105
- $client = $setup["client"];
169
+ [$_shouldSkip, $_reason] = Runner::is_control_skipped("direct", "direct-list-${entity.name}", $setup["live"] ? "live" : "unit");
170
+ if ($_shouldSkip) {
171
+ $this->markTestSkipped($_reason ?? "skipped via sdk-test-control.json");
172
+ return;
173
+ }
174
+ ${listSkipBlock} $client = $setup["client"];
106
175
 
107
176
  `)
108
177
 
@@ -137,11 +206,27 @@ class ${entity.Name}DirectTest extends TestCase
137
206
  `)
138
207
  }
139
208
 
140
- Content(` $this->assertNull($err);
141
- $this->assertTrue($result["ok"]);
142
- $this->assertEquals(200, Helpers::to_int($result["status"]));
143
-
144
- if (!$setup["live"]) {
209
+ Content(` if ($setup["live"]) {
210
+ // Live mode is lenient: synthetic IDs frequently 4xx and the
211
+ // list-response shape varies wildly across public APIs. Skip
212
+ // rather than fail when the call doesn't return a usable list.
213
+ if ($err !== null) {
214
+ $this->markTestSkipped("list call failed (likely synthetic IDs against live API): " . (string)$err);
215
+ return;
216
+ }
217
+ if (empty($result["ok"])) {
218
+ $this->markTestSkipped("list call not ok (likely synthetic IDs against live API)");
219
+ return;
220
+ }
221
+ $status = Helpers::to_int($result["status"]);
222
+ if ($status < 200 || $status >= 300) {
223
+ $this->markTestSkipped("expected 2xx status, got " . $status);
224
+ return;
225
+ }
226
+ } else {
227
+ $this->assertNull($err);
228
+ $this->assertTrue($result["ok"]);
229
+ $this->assertEquals(200, Helpers::to_int($result["status"]));
145
230
  $this->assertIsArray($result["data"]);
146
231
  $this->assertCount(2, $result["data"]);
147
232
  $this->assertCount(1, $setup["calls"]);
@@ -152,23 +237,66 @@ class ${entity.Name}DirectTest extends TestCase
152
237
  }
153
238
 
154
239
  if (hasLoad && loadPoint) {
240
+ // Skip live direct-load only when there's no way to fill path params:
241
+ // no spec examples and no list-bootstrap. Spec examples win first.
242
+ const loadSkipBlock = (loadParams.length > 0 && !loadAllHaveExamples)
243
+ ? ` if ($setup["live"]) {
244
+ $this->markTestSkipped("live direct-load needs real ID — set *_ENTID env var with real IDs to run");
245
+ return;
246
+ }
247
+ `
248
+ : ''
155
249
  Content(` public function test_direct_load_${entity.name}(): void
156
250
  {
157
251
  $setup = ${entity.name}_direct_setup(["id" => "direct01"]);
158
- $client = $setup["client"];
252
+ [$_shouldSkip, $_reason] = Runner::is_control_skipped("direct", "direct-load-${entity.name}", $setup["live"] ? "live" : "unit");
253
+ if ($_shouldSkip) {
254
+ $this->markTestSkipped($_reason ?? "skipped via sdk-test-control.json");
255
+ return;
256
+ }
257
+ ${loadSkipBlock} $client = $setup["client"];
159
258
 
160
259
  `)
161
260
 
162
- if (loadParams.length > 0) {
261
+ const needsQuery = loadParams.length > 0 || loadLiveQueryLines !== ''
262
+ if (needsQuery) {
163
263
  Content(` $params = [];
164
- if (!$setup["live"]) {
264
+ $query = [];
265
+ `)
266
+ if (loadAllHaveExamples) {
267
+ Content(` if ($setup["live"]) {
268
+ `)
269
+ if (loadLiveQueryLines) Content(loadLiveQueryLines + '\n')
270
+ Content(loadExampleLines + '\n')
271
+ Content(` } else {
272
+ `)
273
+ for (let i = 0; i < loadParams.length; i++) {
274
+ Content(` $params["${loadParams[i].name}"] = "direct0${i + 1}";
275
+ `)
276
+ }
277
+ Content(` }
278
+ `)
279
+ } else if (loadParams.length > 0) {
280
+ if (loadLiveQueryLines) {
281
+ Content(` if ($setup["live"]) {
282
+ ${loadLiveQueryLines}
283
+ }
284
+ `)
285
+ }
286
+ Content(` if (!$setup["live"]) {
165
287
  `)
166
- for (let i = 0; i < loadParams.length; i++) {
167
- Content(` $params["${loadParams[i].name}"] = "direct0${i + 1}";
288
+ for (let i = 0; i < loadParams.length; i++) {
289
+ Content(` $params["${loadParams[i].name}"] = "direct0${i + 1}";
168
290
  `)
291
+ }
292
+ Content(` }
293
+ `)
294
+ } else if (loadLiveQueryLines) {
295
+ Content(` if ($setup["live"]) {
296
+ ${loadLiveQueryLines}
169
297
  }
170
- Content(` }
171
298
  `)
299
+ }
172
300
  }
173
301
 
174
302
  Content(`
@@ -176,20 +304,37 @@ class ${entity.Name}DirectTest extends TestCase
176
304
  "path" => "${loadPath}",
177
305
  "method" => "GET",
178
306
  `)
179
- if (loadParams.length > 0) {
307
+ if (needsQuery) {
180
308
  Content(` "params" => $params,
309
+ "query" => $query,
181
310
  `)
182
311
  } else {
183
312
  Content(` "params" => [],
184
313
  `)
185
314
  }
186
315
  Content(` ]);
187
- $this->assertNull($err);
188
- $this->assertTrue($result["ok"]);
189
- $this->assertEquals(200, Helpers::to_int($result["status"]));
190
- $this->assertNotNull($result["data"]);
191
-
192
- if (!$setup["live"]) {
316
+ if ($setup["live"]) {
317
+ // Live mode is lenient: synthetic IDs frequently 4xx. Skip
318
+ // rather than fail when the load endpoint isn't reachable
319
+ // with the IDs we can construct from setup.idmap.
320
+ if ($err !== null) {
321
+ $this->markTestSkipped("load call failed (likely synthetic IDs against live API): " . (string)$err);
322
+ return;
323
+ }
324
+ if (empty($result["ok"])) {
325
+ $this->markTestSkipped("load call not ok (likely synthetic IDs against live API)");
326
+ return;
327
+ }
328
+ $status = Helpers::to_int($result["status"]);
329
+ if ($status < 200 || $status >= 300) {
330
+ $this->markTestSkipped("expected 2xx status, got " . $status);
331
+ return;
332
+ }
333
+ } else {
334
+ $this->assertNull($err);
335
+ $this->assertTrue($result["ok"]);
336
+ $this->assertEquals(200, Helpers::to_int($result["status"]));
337
+ $this->assertNotNull($result["data"]);
193
338
  if (is_array($result["data"]) && isset($result["data"]["id"])) {
194
339
  $this->assertEquals("direct01", $result["data"]["id"]);
195
340
  }
@@ -211,15 +356,13 @@ function ${entity.name}_direct_setup($mockres)
211
356
 
212
357
  $env = Runner::env_override([
213
358
  "${entidEnvVar}" => [],
214
- "${PROJECTNAME}_TEST_LIVE" => "FALSE",
215
- "${PROJECTNAME}_APIKEY" => "NONE",
359
+ "${PROJECTNAME}_TEST_LIVE" => "FALSE",${apikeyEnvEntry}
216
360
  ]);
217
361
 
218
362
  $live = $env["${PROJECTNAME}_TEST_LIVE"] === "TRUE";
219
363
 
220
364
  if ($live) {
221
- $merged_opts = [
222
- "apikey" => $env["${PROJECTNAME}_APIKEY"],
365
+ $merged_opts = [${apikeyLiveField}
223
366
  ];
224
367
  $client = new ${model.const.Name}SDK($merged_opts);
225
368
  return [