@voxgig/sdkgen 1.3.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/action/action.d.ts +2 -1
- package/dist/action/action.js +13 -3
- package/dist/action/action.js.map +1 -1
- package/dist/action/feature.js +3 -5
- package/dist/action/feature.js.map +1 -1
- package/dist/action/target.js +9 -7
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Deploy.js +3 -3
- package/dist/cmp/Deploy.js.map +1 -1
- package/dist/helpers/opShape.js +38 -6
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +5 -4
- package/dist/sdkgen.js.map +1 -1
- package/dist/utility.d.ts +2 -1
- package/dist/utility.js +20 -1
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aontu +1 -1
- package/package.json +1 -1
- package/project/.sdk/model/feature/feature-index.aontu +5 -0
- package/project/.sdk/src/cmp/go/Config_go.ts +3 -2
- package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +35 -9
- package/project/.sdk/src/cmp/go/ReadmeExamplesTest_go.ts +18 -14
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +6 -2
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +6 -2
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +51 -30
- package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +66 -13
- package/project/.sdk/src/cmp/go/ReadmeTopHowto_go.ts +4 -0
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +51 -16
- package/project/.sdk/src/cmp/go/utility_go.ts +70 -0
- package/project/.sdk/src/cmp/js/Config_js.ts +3 -6
- package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +23 -6
- package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +31 -7
- package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +33 -10
- package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +24 -14
- package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +15 -3
- package/project/.sdk/src/cmp/lua/Config_lua.ts +3 -2
- package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +41 -6
- package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +94 -20
- package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +51 -8
- package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +31 -2
- package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +13 -2
- package/project/.sdk/src/cmp/php/Config_php.ts +3 -2
- package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +34 -6
- package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +95 -19
- package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +44 -8
- package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +26 -2
- package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +13 -2
- package/project/.sdk/src/cmp/py/Config_py.ts +3 -2
- package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +26 -8
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +95 -21
- package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +37 -11
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +28 -2
- package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +16 -3
- package/project/.sdk/src/cmp/rb/Config_rb.ts +3 -2
- package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +26 -8
- package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +96 -21
- package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +36 -10
- package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +28 -2
- package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +13 -2
- package/project/.sdk/src/cmp/ts/Config_ts.ts +3 -6
- package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +22 -6
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +56 -18
- package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +33 -10
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +21 -14
- package/project/.sdk/src/cmp/ts/ReadmeTopTest_ts.ts +15 -3
- package/project/.sdk/tm/py/test/test_struct_utility.py +1 -1
- package/project/.sdk/tm/rb/Makefile +3 -1
- package/src/action/action.ts +15 -3
- package/src/action/feature.ts +4 -8
- package/src/action/target.ts +11 -9
- package/src/cmp/Deploy.ts +3 -3
- package/src/helpers/opShape.ts +45 -7
- package/src/sdkgen.ts +4 -3
- package/src/utility.ts +24 -1
- package/project/.sdk/model/feature/feature-index.jsonic +0 -5
- /package/project/.sdk/model/feature/{log.jsonic → log.aontu} +0 -0
- /package/project/.sdk/model/feature/{test.jsonic → test.aontu} +0 -0
- /package/project/.sdk/model/target/{go-cli.jsonic → go-cli.aontu} +0 -0
- /package/project/.sdk/model/target/{go-mcp.jsonic → go-mcp.aontu} +0 -0
- /package/project/.sdk/model/target/{go.jsonic → go.aontu} +0 -0
- /package/project/.sdk/model/target/{js.jsonic → js.aontu} +0 -0
- /package/project/.sdk/model/target/{lua.jsonic → lua.aontu} +0 -0
- /package/project/.sdk/model/target/{php.jsonic → php.aontu} +0 -0
- /package/project/.sdk/model/target/{py.jsonic → py.aontu} +0 -0
- /package/project/.sdk/model/target/{rb.jsonic → rb.aontu} +0 -0
- /package/project/.sdk/model/target/{ts.jsonic → ts.aontu} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, each, Content, canonToType, File, isAuthActive, entityIdField, safeVarName } from '@voxgig/sdkgen'
|
|
2
|
+
import { cmp, each, Content, canonToType, File, isAuthActive, entityIdField, opRequestShape, safeVarName } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -272,8 +272,20 @@ ${info.desc}
|
|
|
272
272
|
|
|
273
273
|
// Show example
|
|
274
274
|
if ('load' === opname || 'remove' === opname) {
|
|
275
|
+
// The id key plus every REQUIRED match key (parent path params
|
|
276
|
+
// like page_id) — the same shape that generates <Name><Op>Match,
|
|
277
|
+
// so the example always type-checks.
|
|
278
|
+
const matchItems = opRequestShape(ent, opname).items
|
|
279
|
+
.filter((it: any) => !it.optional || it.name === idF)
|
|
280
|
+
.sort((a: any, b: any) =>
|
|
281
|
+
(a.name === idF ? 0 : 1) - (b.name === idF ? 0 : 1))
|
|
282
|
+
const arg = 0 < matchItems.length
|
|
283
|
+
? `{ ${matchItems.map((it: any) =>
|
|
284
|
+
`${it.name}: ${exampleValue(ent, ent.op && ent.op[opname], it.name,
|
|
285
|
+
it.name === idF ? ent.name + '_id' : it.name)}`).join(', ')} }`
|
|
286
|
+
: ''
|
|
275
287
|
Content(`\`\`\`ts
|
|
276
|
-
const result = await client.${ent.Name}().${opname}(${
|
|
288
|
+
const result = await client.${ent.Name}().${opname}(${arg})
|
|
277
289
|
\`\`\`
|
|
278
290
|
|
|
279
291
|
`)
|
|
@@ -286,14 +298,19 @@ const results = await client.${ent.Name}().${opname}()
|
|
|
286
298
|
`)
|
|
287
299
|
}
|
|
288
300
|
else if ('create' === opname) {
|
|
301
|
+
// Members come from the SAME shape that generates
|
|
302
|
+
// <Name>CreateData: every required member must appear (the
|
|
303
|
+
// /* type */ placeholders also mark the block as an illustration
|
|
304
|
+
// for the doc gates); an all-optional create renders an empty —
|
|
305
|
+
// still assignable — literal.
|
|
306
|
+
const createItems = opRequestShape(ent, 'create').items
|
|
307
|
+
.filter((it: any) => !it.optional)
|
|
289
308
|
Content(`\`\`\`ts
|
|
290
309
|
const result = await client.${ent.Name}().create({
|
|
291
310
|
`)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
Content(` ${field.name}: /* ${canonToType(field.type, target.name)} */,
|
|
311
|
+
createItems.map((it: any) => {
|
|
312
|
+
Content(` ${it.name}: /* ${canonToType(it.type, target.name)} */,
|
|
295
313
|
`)
|
|
296
|
-
}
|
|
297
314
|
})
|
|
298
315
|
Content(`})
|
|
299
316
|
\`\`\`
|
|
@@ -301,12 +318,18 @@ const result = await client.${ent.Name}().create({
|
|
|
301
318
|
`)
|
|
302
319
|
}
|
|
303
320
|
else if ('update' === opname) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
321
|
+
// The id key plus every REQUIRED data member — the same shape
|
|
322
|
+
// that generates <Name>UpdateData — then the patch-fields note.
|
|
323
|
+
const updateItems = opRequestShape(ent, 'update').items
|
|
324
|
+
.filter((it: any) => !it.optional || it.name === idF)
|
|
325
|
+
.sort((a: any, b: any) =>
|
|
326
|
+
(a.name === idF ? 0 : 1) - (b.name === idF ? 0 : 1))
|
|
327
|
+
const updateLines = updateItems.map((it: any) =>
|
|
328
|
+
` ${it.name}: ${exampleValue(ent, ent.op && ent.op.update, it.name,
|
|
329
|
+
it.name === idF ? ent.name + '_id' : it.name)},\n`).join('')
|
|
307
330
|
Content(`\`\`\`ts
|
|
308
331
|
const result = await client.${ent.Name}().update({
|
|
309
|
-
${
|
|
332
|
+
${updateLines} // Fields to update
|
|
310
333
|
})
|
|
311
334
|
\`\`\`
|
|
312
335
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, Content, isAuthActive, packageName, envName, entityIdField, safeVarName } from '@voxgig/sdkgen'
|
|
2
|
+
import { cmp, Content, isAuthActive, packageName, envName, entityIdField, entityOps, opRequestShape, safeVarName } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -31,7 +31,7 @@ const client = ${ctor}
|
|
|
31
31
|
if (exampleEntity) {
|
|
32
32
|
const eName = nom(exampleEntity, 'Name')
|
|
33
33
|
const eVar = safeVarName(eName.toLowerCase(), 'ts')
|
|
34
|
-
const opnames =
|
|
34
|
+
const opnames = entityOps(exampleEntity)
|
|
35
35
|
|
|
36
36
|
let hasCall = false
|
|
37
37
|
|
|
@@ -45,26 +45,33 @@ for (const ${eVar} of ${eVar}s) {
|
|
|
45
45
|
hasCall = true
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
// Find a nested entity for a more interesting example
|
|
48
|
+
// Find a nested entity for a more interesting example: one with a parent
|
|
49
|
+
// chain, an active load op of its OWN, and a required non-id load param
|
|
50
|
+
// to demonstrate (the parent key, e.g. page_id).
|
|
49
51
|
const nestedEntity = Object.values(entity).find((e: any) =>
|
|
50
|
-
e.active !== false &&
|
|
52
|
+
e.active !== false &&
|
|
53
|
+
e.relations && e.relations.ancestors && 0 < e.relations.ancestors.length &&
|
|
54
|
+
entityOps(e).includes('load') &&
|
|
55
|
+
opRequestShape(e, 'load').items.some((it: any) =>
|
|
56
|
+
!it.optional && it.name !== entityIdField(e))
|
|
51
57
|
) as any
|
|
52
58
|
|
|
53
|
-
if (nestedEntity
|
|
59
|
+
if (nestedEntity) {
|
|
54
60
|
const neName = nom(nestedEntity, 'Name')
|
|
55
61
|
const neVar = safeVarName(neName.toLowerCase(), 'ts')
|
|
56
|
-
const parentFields = (nestedEntity.fields || [])
|
|
57
|
-
.filter((f: any) => f.name !== 'id' && f.name.endsWith('_id'))
|
|
58
|
-
const parentParam = parentFields.length > 0 ? parentFields[0].name : 'parent_id'
|
|
59
62
|
const loadOp = nestedEntity.op && nestedEntity.op.load
|
|
60
63
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
64
|
+
// Every REQUIRED load-match key (parent keys first, own id last) — the
|
|
65
|
+
// same shape that generates <Name>LoadMatch, so the example
|
|
66
|
+
// type-checks.
|
|
63
67
|
const neIdF = entityIdField(nestedEntity)
|
|
64
|
-
const neMatchLines =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
const neMatchLines = opRequestShape(nestedEntity, 'load').items
|
|
69
|
+
.filter((it: any) => !it.optional || it.name === neIdF)
|
|
70
|
+
.sort((a: any, b: any) =>
|
|
71
|
+
(a.name === neIdF ? 1 : 0) - (b.name === neIdF ? 1 : 0))
|
|
72
|
+
.map((it: any) =>
|
|
73
|
+
` ${it.name}: ${exampleValue(nestedEntity, loadOp, it.name,
|
|
74
|
+
it.name === neIdF ? 'example_id' : 'example_' + it.name)},`)
|
|
68
75
|
|
|
69
76
|
Content(`
|
|
70
77
|
// Load a specific ${neName.toLowerCase()} (returns a ${neName})
|
|
@@ -22,15 +22,27 @@ const client = ${model.const.Name}SDK.test()
|
|
|
22
22
|
|
|
23
23
|
if (exampleEntity) {
|
|
24
24
|
const eName = nom(exampleEntity, 'Name')
|
|
25
|
-
const eVar = safeVarName(eName.toLowerCase(), 'ts')
|
|
26
25
|
// Drive the test-mode example off the entity's PRIMARY op (never a
|
|
27
26
|
// hardcoded `load` a create-only entity lacks).
|
|
28
27
|
const primaryOp = entityPrimaryOp(exampleEntity) || 'load'
|
|
28
|
+
// A list() result is an array — name the variable accordingly.
|
|
29
|
+
const eVar = safeVarName(eName.toLowerCase(), 'ts') +
|
|
30
|
+
('list' === primaryOp ? 's' : '')
|
|
29
31
|
const primaryOpDef = exampleEntity.op && exampleEntity.op[primaryOp]
|
|
30
32
|
const idF = entityIdField(exampleEntity)
|
|
31
33
|
let arg = ''
|
|
32
34
|
if ('load' === primaryOp || 'remove' === primaryOp) {
|
|
33
|
-
|
|
35
|
+
// Every REQUIRED match key (id first) — the same shape that generates
|
|
36
|
+
// the op's Match type, so the block type-checks.
|
|
37
|
+
const items = opRequestShape(exampleEntity, primaryOp).items
|
|
38
|
+
.filter((it: any) => !it.optional || it.name === idF)
|
|
39
|
+
.sort((a: any, b: any) =>
|
|
40
|
+
(a.name === idF ? 0 : 1) - (b.name === idF ? 0 : 1))
|
|
41
|
+
arg = 0 < items.length
|
|
42
|
+
? `{ ${items.map((it: any) =>
|
|
43
|
+
`${it.name}: ${exampleValue(exampleEntity, primaryOpDef, it.name,
|
|
44
|
+
it.name === idF ? 'test01' : 'example_' + it.name)}`).join(', ')} }`
|
|
45
|
+
: ''
|
|
34
46
|
} else if ('create' === primaryOp || 'update' === primaryOp) {
|
|
35
47
|
const items = opRequestShape(exampleEntity, primaryOp).items
|
|
36
48
|
.filter((it: any) => it.name !== idF && it.name !== 'id')
|
|
@@ -40,7 +52,7 @@ const client = ${model.const.Name}SDK.test()
|
|
|
40
52
|
`${it.name}: ${exampleValue(exampleEntity, primaryOpDef, it.name, 'example_' + it.name)}`).join(', ')} }`
|
|
41
53
|
}
|
|
42
54
|
Content(`const ${eVar} = await client.${eName}().${primaryOp}(${arg})
|
|
43
|
-
// ${eVar} is a bare ${eName} populated with mock data
|
|
55
|
+
// ${eVar} is ${'list' === primaryOp ? `an array of bare ${eName} records` : `a bare ${eName}`} populated with mock data
|
|
44
56
|
console.log(${eVar})
|
|
45
57
|
`)
|
|
46
58
|
}
|
|
@@ -516,7 +516,7 @@ class TestStructUtility(unittest.TestCase):
|
|
|
516
516
|
|
|
517
517
|
# -------------------------------------------------
|
|
518
518
|
# transform tests
|
|
519
|
-
# Inputs and expected outputs: build/test/transform.
|
|
519
|
+
# Inputs and expected outputs: build/test/transform.aontu
|
|
520
520
|
# -------------------------------------------------
|
|
521
521
|
|
|
522
522
|
def test_transform_basic(self):
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
.PHONY: test build clean publish
|
|
2
2
|
|
|
3
|
+
# Run the WHOLE suite (entity, direct, utility, and readme-example tests) —
|
|
4
|
+
# a single-file invocation here leaves the rest of test/ silently unrun.
|
|
3
5
|
test:
|
|
4
|
-
ruby -Ilib test
|
|
6
|
+
ruby -Ilib -Itest -e 'Dir.glob("./test/*_test.rb").each { |f| require f }'
|
|
5
7
|
|
|
6
8
|
build:
|
|
7
9
|
@echo "No build step for Ruby"
|
package/src/action/action.ts
CHANGED
|
@@ -12,14 +12,14 @@ import type {
|
|
|
12
12
|
} from '../types'
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
// Append `@"<name>.
|
|
15
|
+
// Append `@"<name>.aontu"` import lines for each name not already present in
|
|
16
16
|
// the index content. Checking against the accumulating result (not the
|
|
17
17
|
// original) means duplicate names in the same call are added at most once.
|
|
18
18
|
function appendIndexEntries(content: string, names: string[]): string {
|
|
19
19
|
let out = content
|
|
20
20
|
|
|
21
21
|
for (const n of names) {
|
|
22
|
-
const entry = `@"${n}.
|
|
22
|
+
const entry = `@"${n}.aontu"`
|
|
23
23
|
if (!out.includes(entry)) {
|
|
24
24
|
out += '\n' + entry
|
|
25
25
|
}
|
|
@@ -34,6 +34,17 @@ const UpdateIndex = cmp(function UpdateIndex(props: any) {
|
|
|
34
34
|
})
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
// Names given to an `add` action: every positional after the subcommand is
|
|
38
|
+
// a name, each possibly comma-separated — `target add ts,py,go` and
|
|
39
|
+
// `target add ts py go` are equivalent (space-separated extras used to be
|
|
40
|
+
// silently dropped).
|
|
41
|
+
function parseAddNames(args: any[]): string[] {
|
|
42
|
+
return args.slice(2)
|
|
43
|
+
.flatMap((a: any) => 'string' === typeof a ? a.split(',') : a)
|
|
44
|
+
.filter((n: any) => null != n && '' !== n)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
37
48
|
function loadContent(actx: ActionContext, which: string | string[]) {
|
|
38
49
|
which = Array.isArray(which) ? which : [which]
|
|
39
50
|
|
|
@@ -43,7 +54,7 @@ function loadContent(actx: ActionContext, which: string | string[]) {
|
|
|
43
54
|
const modelfolder = Path.dirname(actx.url)
|
|
44
55
|
|
|
45
56
|
which.map((w: string) => {
|
|
46
|
-
const indexfile = Path.join(modelfolder, w, w + '-index.
|
|
57
|
+
const indexfile = Path.join(modelfolder, w, w + '-index.aontu')
|
|
47
58
|
const indexcontent = fs.readFileSync(indexfile, 'utf8')
|
|
48
59
|
content[`${w}_index`] = indexcontent
|
|
49
60
|
})
|
|
@@ -56,5 +67,6 @@ function loadContent(actx: ActionContext, which: string | string[]) {
|
|
|
56
67
|
export {
|
|
57
68
|
UpdateIndex,
|
|
58
69
|
appendIndexEntries,
|
|
70
|
+
parseAddNames,
|
|
59
71
|
loadContent
|
|
60
72
|
}
|
package/src/action/feature.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { SdkGenError } from '../utility'
|
|
|
26
26
|
|
|
27
27
|
import {
|
|
28
28
|
UpdateIndex,
|
|
29
|
+
parseAddNames,
|
|
29
30
|
loadContent,
|
|
30
31
|
} from './action'
|
|
31
32
|
|
|
@@ -52,12 +53,7 @@ async function action_feature(args: string[], actx: ActionContext): Promise<Acti
|
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
async function cmd_feature_add(args: string[], actx: ActionContext): Promise<ActionResult> {
|
|
55
|
-
|
|
56
|
-
const features_arg = args[2]
|
|
57
|
-
const features: string[] =
|
|
58
|
-
'string' === typeof features_arg ? features_arg.split(',') : features_arg
|
|
59
|
-
|
|
60
|
-
return feature_add(features, actx)
|
|
56
|
+
return feature_add(parseAddNames(args), actx)
|
|
61
57
|
}
|
|
62
58
|
|
|
63
59
|
|
|
@@ -122,10 +118,10 @@ const FeatureRoot = cmp(function FeatureRoot(props: any) {
|
|
|
122
118
|
Folder({ name: 'model/feature' }, () => {
|
|
123
119
|
Copy({
|
|
124
120
|
// TODO: these paths needs to be parameterised
|
|
125
|
-
from: BASE + '/project/.sdk/model/feature/' + fname + '.
|
|
121
|
+
from: BASE + '/project/.sdk/model/feature/' + fname + '.aontu',
|
|
126
122
|
exclude: true
|
|
127
123
|
})
|
|
128
|
-
File({ name: 'feature-index.
|
|
124
|
+
File({ name: 'feature-index.aontu' }, () => UpdateIndex({
|
|
129
125
|
content: ctx$.meta.content.feature_index,
|
|
130
126
|
names: features,
|
|
131
127
|
}))
|
package/src/action/target.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
|
|
32
32
|
import {
|
|
33
33
|
UpdateIndex,
|
|
34
|
+
parseAddNames,
|
|
34
35
|
loadContent,
|
|
35
36
|
} from './action'
|
|
36
37
|
|
|
@@ -55,11 +56,7 @@ async function action_target(args: string[], actx: ActionContext): Promise<Actio
|
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
async function cmd_target_add(args: string[], actx: ActionContext): Promise<ActionResult> {
|
|
58
|
-
|
|
59
|
-
const targets: string[] =
|
|
60
|
-
'string' === typeof targets_arg ? targets_arg.split(',') : targets_arg
|
|
61
|
-
|
|
62
|
-
return target_add(targets, actx)
|
|
59
|
+
return target_add(parseAddNames(args), actx)
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
|
|
@@ -124,6 +121,11 @@ const TargetRoot = cmp(function TargetRoot(props: any) {
|
|
|
124
121
|
// const tfolder = 'node_modules/@voxgig/sdkgen/project/.sdk'
|
|
125
122
|
|
|
126
123
|
Project({}, () => {
|
|
124
|
+
// Resolved names of every target in this run. The index File is
|
|
125
|
+
// re-rendered per target and the last render wins, so each render must
|
|
126
|
+
// carry all names seen so far, not just its own.
|
|
127
|
+
const tnames: string[] = []
|
|
128
|
+
|
|
127
129
|
each(targets, (n) => {
|
|
128
130
|
const tref = n.val$
|
|
129
131
|
|
|
@@ -134,6 +136,7 @@ const TargetRoot = cmp(function TargetRoot(props: any) {
|
|
|
134
136
|
})
|
|
135
137
|
|
|
136
138
|
const { tname, tfolder, torigname, base } = resolveTarget(tref, ctx$)
|
|
139
|
+
tnames.push(tname)
|
|
137
140
|
const targetNote = tname + (tname != tref ? ' ref:' + tref : '')
|
|
138
141
|
|
|
139
142
|
log.info({
|
|
@@ -145,16 +148,15 @@ const TargetRoot = cmp(function TargetRoot(props: any) {
|
|
|
145
148
|
|
|
146
149
|
Folder({ name: 'model/target' }, () => {
|
|
147
150
|
Copy({
|
|
148
|
-
from: tfolder + '/model/target/' + torigname + '.
|
|
151
|
+
from: tfolder + '/model/target/' + torigname + '.aontu',
|
|
149
152
|
// exclude: true
|
|
150
153
|
replace: {
|
|
151
154
|
"'BASE'": "'" + base + "'"
|
|
152
155
|
}
|
|
153
156
|
})
|
|
154
|
-
File({ name: 'target-index.
|
|
157
|
+
File({ name: 'target-index.aontu' }, () => UpdateIndex({
|
|
155
158
|
content: ctx$.meta.content.target_index,
|
|
156
|
-
|
|
157
|
-
names: [tname]
|
|
159
|
+
names: tnames,
|
|
158
160
|
}))
|
|
159
161
|
})
|
|
160
162
|
|
package/src/cmp/Deploy.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
// Generate the SDK root deployment Makefile from the model's per-target
|
|
10
|
-
// `publish` sections (see model/sdkgen.
|
|
10
|
+
// `publish` sections (see model/sdkgen.aontu):
|
|
11
11
|
//
|
|
12
12
|
// publish.tag — git release tag `<prefix>/vX.Y.Z`; every port has
|
|
13
13
|
// one, pushed with a token from the aql key vault
|
|
@@ -179,7 +179,7 @@ deploy-dry-${t.name}:
|
|
|
179
179
|
|
|
180
180
|
return `# ${model.Name} SDK deployment. GENERATED by @voxgig/sdkgen — regenerated
|
|
181
181
|
# on every \`npm run generate\` in .sdk/; publication details live in the
|
|
182
|
-
# model (.sdk/model/target/<target>.
|
|
182
|
+
# model (.sdk/model/target/<target>.aontu, \`publish\` section).
|
|
183
183
|
#
|
|
184
184
|
# Every port gets a git release tag <target>/vX.Y.Z. Ports with an ACTIVE
|
|
185
185
|
# registry also publish a package there. Credentials are injected at exec
|
|
@@ -218,7 +218,7 @@ TARGETS := ${targets.map((t: any) => t.name).join(' ')}
|
|
|
218
218
|
deploy:
|
|
219
219
|
\t@echo "Deployment is per-target — pick one (each upload is irreversible):"
|
|
220
220
|
\t@echo " make deploy-<target> targets: $(TARGETS)"
|
|
221
|
-
\t@echo "Registry state is set in the model (.sdk/model/target/<t>.
|
|
221
|
+
\t@echo "Registry state is set in the model (.sdk/model/target/<t>.aontu):"
|
|
222
222
|
${targets.map((t: any) => {
|
|
223
223
|
const reg = registryOf(t)
|
|
224
224
|
const state = reg ? `${reg.name} ${regIsActive(reg) ? 'ACTIVE' : 'publish pending (deploy = git tag only)'}` : 'tag-only'
|
package/src/helpers/opShape.ts
CHANGED
|
@@ -60,20 +60,58 @@ type OpShapeItem = {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
// Collect an op's params, deduped by name across
|
|
63
|
+
// Collect an op's params, deduped by name across its points.
|
|
64
|
+
//
|
|
65
|
+
// Points tagged with `select.$action` are sub-resource/action routes folded
|
|
66
|
+
// into the op (e.g. POST .../components/{component_id}/page_access_groups on
|
|
67
|
+
// the create op); they do not describe the op's canonical request payload,
|
|
68
|
+
// so they are excluded — unless the op consists only of action points.
|
|
69
|
+
//
|
|
70
|
+
// Requiredness merges by INTERSECTION: with several alternative canonical
|
|
71
|
+
// points, a param the caller must always supply is one required by every
|
|
72
|
+
// point; a param only some routes use is optional in the merged shape.
|
|
73
|
+
// (Union-required makes the common route untypeable — e.g. a plain
|
|
74
|
+
// list-by-parent-id call failing because sibling routes' ids are demanded —
|
|
75
|
+
// and steers callers into passing keys that flip runtime point dispatch to
|
|
76
|
+
// the wrong route.)
|
|
64
77
|
function opParams(op: any): any[] {
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
let points: any[] = op && op.points ? each(op.points) : []
|
|
79
|
+
|
|
80
|
+
const canonical = points.filter((pt: any) =>
|
|
81
|
+
null == (pt && pt.select && pt.select['$action']))
|
|
82
|
+
if (0 < canonical.length) {
|
|
83
|
+
points = canonical
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const seen: Record<string, any> = {}
|
|
87
|
+
const requiredOnAll: Record<string, boolean> = {}
|
|
67
88
|
const out: any[] = []
|
|
68
|
-
|
|
89
|
+
|
|
90
|
+
points.forEach((pt: any, pointIndex: number) => {
|
|
69
91
|
const params = pt && pt.args && pt.args.params ? each(pt.args.params) : []
|
|
92
|
+
const requiredHere: Record<string, boolean> = {}
|
|
70
93
|
params.forEach((p: any) => {
|
|
71
|
-
if (p && null != p.name
|
|
72
|
-
|
|
73
|
-
|
|
94
|
+
if (p && null != p.name) {
|
|
95
|
+
requiredHere[p.name] = false !== p.reqd
|
|
96
|
+
if (!seen[p.name]) {
|
|
97
|
+
seen[p.name] = { ...p }
|
|
98
|
+
// A param first seen on a later point was absent earlier: optional.
|
|
99
|
+
requiredOnAll[p.name] = 0 === pointIndex
|
|
100
|
+
out.push(seen[p.name])
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
Object.keys(requiredOnAll).forEach((name) => {
|
|
105
|
+
if (true !== requiredHere[name]) {
|
|
106
|
+
requiredOnAll[name] = false
|
|
74
107
|
}
|
|
75
108
|
})
|
|
76
109
|
})
|
|
110
|
+
|
|
111
|
+
out.forEach((p: any) => {
|
|
112
|
+
p.reqd = true === requiredOnAll[p.name]
|
|
113
|
+
})
|
|
114
|
+
|
|
77
115
|
return out
|
|
78
116
|
}
|
|
79
117
|
|
package/src/sdkgen.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type {
|
|
|
19
19
|
ActionResult,
|
|
20
20
|
} from './types'
|
|
21
21
|
|
|
22
|
-
import { SdkGenError, requirePath, isAuthActive } from './utility'
|
|
22
|
+
import { SdkGenError, requirePath, isAuthActive, resolveAuthPrefix } from './utility'
|
|
23
23
|
|
|
24
24
|
import { Main } from './cmp/Main'
|
|
25
25
|
import { Deploy } from './cmp/Deploy'
|
|
@@ -226,7 +226,7 @@ function SdkGen(opts: SdkGenOptions) {
|
|
|
226
226
|
|
|
227
227
|
|
|
228
228
|
function resolveModel() {
|
|
229
|
-
const path = './model/sdk.
|
|
229
|
+
const path = './model/sdk.aontu'
|
|
230
230
|
const errs: any[] = []
|
|
231
231
|
|
|
232
232
|
if (null == aontu) {
|
|
@@ -299,7 +299,7 @@ SdkGen.makeBuild = async function(opts: SdkGenOptions) {
|
|
|
299
299
|
root: opts.root,
|
|
300
300
|
def: opts.def || 'no-def',
|
|
301
301
|
kind: 'openapi-3',
|
|
302
|
-
model: opts.model ? (opts.model.folder + '/api.
|
|
302
|
+
model: opts.model ? (opts.model.folder + '/api.aontu') : 'no-model',
|
|
303
303
|
meta: opts.meta || {},
|
|
304
304
|
}
|
|
305
305
|
|
|
@@ -432,6 +432,7 @@ export {
|
|
|
432
432
|
|
|
433
433
|
requirePath,
|
|
434
434
|
isAuthActive,
|
|
435
|
+
resolveAuthPrefix,
|
|
435
436
|
|
|
436
437
|
buildIdNames,
|
|
437
438
|
getMatchEntries,
|
package/src/utility.ts
CHANGED
|
@@ -15,7 +15,7 @@ function resolvePath(ctx$: any, path: string): any {
|
|
|
15
15
|
// True unless the model declares auth off. Templates use this to gate
|
|
16
16
|
// apikey-related code, docs, and examples for public APIs that need no
|
|
17
17
|
// authentication. Two opt-outs, in priority order:
|
|
18
|
-
// 1. main.kit.info.auth: false (user-facing, set in api-info.
|
|
18
|
+
// 1. main.kit.info.auth: false (user-facing, set in api-info.aontu)
|
|
19
19
|
// 2. main.kit.config.auth.active: false
|
|
20
20
|
function isAuthActive(model: any): boolean {
|
|
21
21
|
const info = getModelPath(model, `main.${KIT}.info`,
|
|
@@ -28,6 +28,28 @@ function isAuthActive(model: any): boolean {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
// The credential prefix for the Authorization header value, resolved in
|
|
32
|
+
// priority order:
|
|
33
|
+
// 1. main.kit.config.auth.prefix (per-SDK user override)
|
|
34
|
+
// 2. main.kit.info.security.prefix (spec-derived, set by apidef from the
|
|
35
|
+
// API's securityScheme — e.g. 'OAuth' for Statuspage)
|
|
36
|
+
// 3. 'Bearer' (conventional fallback)
|
|
37
|
+
// '' is a valid resolved value: it means a raw credential with no prefix
|
|
38
|
+
// (e.g. an apiKey scheme in a custom header). Config generators for every
|
|
39
|
+
// language target must use this instead of hardcoding 'Bearer'.
|
|
40
|
+
function resolveAuthPrefix(model: any): string {
|
|
41
|
+
const auth = getModelPath(model, `main.${KIT}.config.auth`,
|
|
42
|
+
{ only_active: false, required: false })
|
|
43
|
+
if (null != auth && null != auth.prefix) return String(auth.prefix)
|
|
44
|
+
|
|
45
|
+
const security = getModelPath(model, `main.${KIT}.info.security`,
|
|
46
|
+
{ only_active: false, required: false })
|
|
47
|
+
if (null != security && null != security.prefix) return String(security.prefix)
|
|
48
|
+
|
|
49
|
+
return 'Bearer'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
31
53
|
function requirePath(ctx$: any, path: string, flags?: { ignore?: boolean }): any {
|
|
32
54
|
const fullpath = resolvePath(ctx$, path)
|
|
33
55
|
const ignore = null == flags?.ignore ? false : flags.ignore
|
|
@@ -63,5 +85,6 @@ export {
|
|
|
63
85
|
resolvePath,
|
|
64
86
|
requirePath,
|
|
65
87
|
isAuthActive,
|
|
88
|
+
resolveAuthPrefix,
|
|
66
89
|
SdkGenError,
|
|
67
90
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|