@voxgig/sdkgen 0.39.1 → 0.40.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/target.js +7 -1
- package/dist/action/target.js.map +1 -1
- package/dist/helpers/buildIdNames.d.ts +11 -0
- package/dist/helpers/buildIdNames.js +56 -0
- package/dist/helpers/buildIdNames.js.map +1 -0
- package/dist/helpers/collectDeps.d.ts +9 -0
- package/dist/helpers/collectDeps.js +50 -0
- package/dist/helpers/collectDeps.js.map +1 -0
- package/dist/helpers/getMatchEntries.d.ts +2 -0
- package/dist/helpers/getMatchEntries.js +13 -0
- package/dist/helpers/getMatchEntries.js.map +1 -0
- package/dist/sdkgen.d.ts +6 -2
- package/dist/sdkgen.js +17 -2
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/project/.sdk/model/target/lua.jsonic +1 -1
- package/project/.sdk/model/target/php.jsonic +2 -3
- package/project/.sdk/model/target/py.jsonic +1 -1
- package/project/.sdk/src/cmp/go/Entity_go.ts +15 -3
- package/project/.sdk/src/cmp/go/Main_go.ts +3 -2
- package/project/.sdk/src/cmp/go/Package_go.ts +8 -29
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +14 -7
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -30
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +6 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +5 -27
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +6 -3
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +5 -17
- package/project/.sdk/src/cmp/php/Config_php.ts +21 -0
- package/project/.sdk/src/cmp/php/MainEntity_php.ts +11 -1
- package/project/.sdk/src/cmp/php/Package_php.ts +5 -31
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +6 -3
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +25 -29
- package/project/.sdk/src/cmp/py/Main_py.ts +5 -5
- package/project/.sdk/src/cmp/py/Package_py.ts +11 -28
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +9 -6
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +7 -19
- package/project/.sdk/src/cmp/rb/Package_rb.ts +8 -47
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +6 -3
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +5 -17
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +6 -3
- package/project/.sdk/tm/go/core/helpers.go +10 -0
- package/project/.sdk/tm/lua/Makefile +1 -1
- package/project/.sdk/tm/php/Makefile +1 -1
- package/src/action/target.ts +7 -1
- package/src/helpers/buildIdNames.ts +70 -0
- package/src/helpers/collectDeps.ts +70 -0
- package/src/helpers/getMatchEntries.ts +14 -0
- package/src/sdkgen.ts +22 -1
|
@@ -3,14 +3,12 @@ import {
|
|
|
3
3
|
Content,
|
|
4
4
|
File,
|
|
5
5
|
cmp,
|
|
6
|
-
|
|
6
|
+
collectDeps,
|
|
7
7
|
} from '@voxgig/sdkgen'
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
KIT,
|
|
10
|
+
import type {
|
|
12
11
|
Model,
|
|
13
|
-
getModelPath,
|
|
14
12
|
} from '@voxgig/apidef'
|
|
15
13
|
|
|
16
14
|
|
|
@@ -20,12 +18,10 @@ const Package = cmp(async function Package(props: any) {
|
|
|
20
18
|
|
|
21
19
|
const model: Model = ctx$.model
|
|
22
20
|
|
|
23
|
-
const feature = getModelPath(model, `main.${KIT}.feature`)
|
|
24
|
-
|
|
25
21
|
File({ name: 'pyproject.toml' }, () => {
|
|
26
22
|
Content(`[build-system]
|
|
27
23
|
requires = ["setuptools>=61.0"]
|
|
28
|
-
build-backend = "setuptools.
|
|
24
|
+
build-backend = "setuptools.build_meta"
|
|
29
25
|
|
|
30
26
|
[project]
|
|
31
27
|
name = "${model.name}-sdk"
|
|
@@ -37,34 +33,21 @@ dependencies = [
|
|
|
37
33
|
"requests>=2.33",
|
|
38
34
|
`)
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (pyDeps) {
|
|
44
|
-
each(pyDeps, (dep: any) => {
|
|
45
|
-
if (dep.active) {
|
|
46
|
-
Content(` "${dep.key$}>=${dep.version}",
|
|
36
|
+
for (const d of collectDeps(model, target.name, target.deps)) {
|
|
37
|
+
const v = d.source === 'target' ? (d.version || '0.0') : d.version
|
|
38
|
+
Content(` "${d.name}>=${v}",
|
|
47
39
|
`)
|
|
48
|
-
}
|
|
49
|
-
})
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
// Add target-level deps
|
|
54
|
-
const targetDeps = target.deps
|
|
55
|
-
if (targetDeps) {
|
|
56
|
-
each(targetDeps, (dep: any) => {
|
|
57
|
-
if (dep.active !== false) {
|
|
58
|
-
Content(` "${dep.key$}>=${dep.version || '0.0'}",
|
|
59
|
-
`)
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
40
|
}
|
|
63
41
|
|
|
64
42
|
Content(`]
|
|
65
43
|
|
|
66
44
|
[project.urls]
|
|
67
45
|
Homepage = "https://github.com/voxgig/${model.name}-sdk"
|
|
46
|
+
|
|
47
|
+
# Explicit package list — setuptools auto-discovery refuses to pick when
|
|
48
|
+
# multiple top-level dirs (core/entity/feature/utility) are present.
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
include = ["core*", "entity*", "feature*", "utility*"]
|
|
68
51
|
`)
|
|
69
52
|
})
|
|
70
53
|
})
|
|
@@ -20,10 +20,13 @@ function normalizePathParams(
|
|
|
20
20
|
return part.replace(/\{([^}]+)\}/g, (match: string, rawName: string) => {
|
|
21
21
|
const snaked = snakify(rawName)
|
|
22
22
|
const depluralized = depluralize(snaked)
|
|
23
|
+
// Prefer exact name match — orig matches can collide when one param's
|
|
24
|
+
// original name was renamed to another param's current name (e.g. badge
|
|
25
|
+
// load: param 'group_id' has orig 'id', and another param has name 'id').
|
|
23
26
|
const param = params.find((p: any) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
p.name === snaked || p.name === depluralized) ||
|
|
28
|
+
params.find((p: any) =>
|
|
29
|
+
p.orig === snaked || p.orig === depluralized)
|
|
27
30
|
if (param) return '{' + param.name + '}'
|
|
28
31
|
|
|
29
32
|
if (rename) {
|
|
@@ -95,7 +98,7 @@ class Test${entity.Name}Direct:
|
|
|
95
98
|
|
|
96
99
|
if (hasList && listPoint) {
|
|
97
100
|
Content(` def test_should_direct_list_${entity.name}(self):
|
|
98
|
-
setup = ${entity.name}_direct_setup([
|
|
101
|
+
setup = _${entity.name}_direct_setup([
|
|
99
102
|
{"id": "direct01"},
|
|
100
103
|
{"id": "direct02"},
|
|
101
104
|
])
|
|
@@ -147,7 +150,7 @@ class Test${entity.Name}Direct:
|
|
|
147
150
|
|
|
148
151
|
if (hasLoad && loadPoint) {
|
|
149
152
|
Content(` def test_should_direct_load_${entity.name}(self):
|
|
150
|
-
setup = ${entity.name}_direct_setup({"id": "direct01"})
|
|
153
|
+
setup = _${entity.name}_direct_setup({"id": "direct01"})
|
|
151
154
|
client = setup["client"]
|
|
152
155
|
|
|
153
156
|
`)
|
|
@@ -190,7 +193,7 @@ class Test${entity.Name}Direct:
|
|
|
190
193
|
|
|
191
194
|
Content(`
|
|
192
195
|
|
|
193
|
-
def ${entity.name}_direct_setup(mockres):
|
|
196
|
+
def _${entity.name}_direct_setup(mockres):
|
|
194
197
|
runner.load_env_local()
|
|
195
198
|
|
|
196
199
|
calls = []
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
File,
|
|
17
17
|
cmp,
|
|
18
18
|
each,
|
|
19
|
+
buildIdNames,
|
|
20
|
+
getMatchEntries,
|
|
19
21
|
} from '@voxgig/sdkgen'
|
|
20
22
|
|
|
21
23
|
|
|
@@ -45,15 +47,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
45
47
|
|
|
46
48
|
const PROJUPPER = model.const.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
|
|
47
49
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
// Build idmap names
|
|
51
|
-
const idnames: string[] = []
|
|
52
|
-
for (let i = 1; i <= 3; i++) idnames.push(`${entity.name}0${i}`)
|
|
53
|
-
for (const anc of ancestors) {
|
|
54
|
-
for (let i = 1; i <= 3; i++) idnames.push(`${anc}0${i}`)
|
|
55
|
-
}
|
|
56
|
-
|
|
50
|
+
const idnames = buildIdNames(entity, basicflow)
|
|
57
51
|
const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
|
|
58
52
|
|
|
59
53
|
const allSteps = Object.values(basicflow.step) as any[]
|
|
@@ -91,7 +85,7 @@ class Test${entity.Name}Entity:
|
|
|
91
85
|
assert ent is not None
|
|
92
86
|
|
|
93
87
|
def test_should_run_basic_flow(self):
|
|
94
|
-
setup = ${entity.name}_basic_setup(None)
|
|
88
|
+
setup = _${entity.name}_basic_setup(None)
|
|
95
89
|
client = setup["client"]
|
|
96
90
|
|
|
97
91
|
`)
|
|
@@ -101,7 +95,7 @@ class Test${entity.Name}Entity:
|
|
|
101
95
|
if (!flowHasCreate) {
|
|
102
96
|
Content(` # Bootstrap entity data from existing test data.
|
|
103
97
|
${entity.name}_ref01_data_raw = vs.items(helpers.to_map(
|
|
104
|
-
vs.
|
|
98
|
+
vs.getpath(setup["data"], "existing.${entity.name}")))
|
|
105
99
|
${entity.name}_ref01_data = None
|
|
106
100
|
if len(${entity.name}_ref01_data_raw) > 0:
|
|
107
101
|
${entity.name}_ref01_data = helpers.to_map(${entity.name}_ref01_data_raw[0][1])
|
|
@@ -120,7 +114,7 @@ class Test${entity.Name}Entity:
|
|
|
120
114
|
|
|
121
115
|
Content(`
|
|
122
116
|
|
|
123
|
-
def ${entity.name}_basic_setup(extra):
|
|
117
|
+
def _${entity.name}_basic_setup(extra):
|
|
124
118
|
runner.load_env_local()
|
|
125
119
|
|
|
126
120
|
entity_data_file = os.path.join(_TEST_DIR, "../../.sdk/test/entity/${entity.name}/${entity.Name}TestData.json")
|
|
@@ -193,12 +187,6 @@ def ${entity.name}_basic_setup(extra):
|
|
|
193
187
|
})
|
|
194
188
|
|
|
195
189
|
|
|
196
|
-
function getMatchEntries(step: any): [string, any][] {
|
|
197
|
-
if (!step?.match) return []
|
|
198
|
-
return Object.entries(step.match).filter(([k]: any) => !k.endsWith('$'))
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
190
|
const generateCreate: OpGen = (ctx, step, index) => {
|
|
203
191
|
const { entity, flow } = ctx
|
|
204
192
|
const ref = step.input?.ref ?? entity.name + '_ref01'
|
|
@@ -419,7 +407,7 @@ const generateLoad: OpGen = (ctx, step, index) => {
|
|
|
419
407
|
}
|
|
420
408
|
if (!hasSrcData) {
|
|
421
409
|
Content(` ${srcdatavar}_raw = vs.items(helpers.to_map(
|
|
422
|
-
vs.
|
|
410
|
+
vs.getpath(setup["data"], "existing.${entity.name}")))
|
|
423
411
|
${srcdatavar} = None
|
|
424
412
|
if len(${srcdatavar}_raw) > 0:
|
|
425
413
|
${srcdatavar} = helpers.to_map(${srcdatavar}_raw[0][1])
|
|
@@ -3,14 +3,12 @@ import {
|
|
|
3
3
|
Content,
|
|
4
4
|
File,
|
|
5
5
|
cmp,
|
|
6
|
-
|
|
6
|
+
collectDeps,
|
|
7
7
|
} from '@voxgig/sdkgen'
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
KIT,
|
|
10
|
+
import type {
|
|
12
11
|
Model,
|
|
13
|
-
getModelPath,
|
|
14
12
|
} from '@voxgig/apidef'
|
|
15
13
|
|
|
16
14
|
|
|
@@ -20,7 +18,8 @@ const Package = cmp(async function Package(props: any) {
|
|
|
20
18
|
|
|
21
19
|
const model: Model = ctx$.model
|
|
22
20
|
|
|
23
|
-
const
|
|
21
|
+
const versionOf = (d: { version: string; source: 'feature' | 'target' }) =>
|
|
22
|
+
d.source === 'target' ? (d.version || '0.0') : d.version
|
|
24
23
|
|
|
25
24
|
// Generate Gemfile
|
|
26
25
|
File({ name: 'Gemfile' }, () => {
|
|
@@ -30,28 +29,9 @@ gemspec
|
|
|
30
29
|
|
|
31
30
|
`)
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const rbDeps = f.deps?.rb
|
|
36
|
-
if (rbDeps) {
|
|
37
|
-
each(rbDeps, (dep: any) => {
|
|
38
|
-
if (dep.active) {
|
|
39
|
-
Content(`gem "${dep.key$}", "~> ${dep.version}"
|
|
40
|
-
`)
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
// Add target-level deps
|
|
47
|
-
const targetDeps = target.deps
|
|
48
|
-
if (targetDeps) {
|
|
49
|
-
each(targetDeps, (dep: any) => {
|
|
50
|
-
if (dep.active !== false) {
|
|
51
|
-
Content(`gem "${dep.key$}", "~> ${dep.version || '0.0'}"
|
|
32
|
+
for (const d of collectDeps(model, target.name, target.deps)) {
|
|
33
|
+
Content(`gem "${d.name}", "~> ${versionOf(d)}"
|
|
52
34
|
`)
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
35
|
}
|
|
56
36
|
})
|
|
57
37
|
|
|
@@ -73,28 +53,9 @@ gemspec
|
|
|
73
53
|
spec.add_dependency "json"
|
|
74
54
|
`)
|
|
75
55
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const rbDeps = f.deps?.rb
|
|
79
|
-
if (rbDeps) {
|
|
80
|
-
each(rbDeps, (dep: any) => {
|
|
81
|
-
if (dep.active) {
|
|
82
|
-
Content(` spec.add_dependency "${dep.key$}", "~> ${dep.version}"
|
|
83
|
-
`)
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
// Add target-level deps
|
|
90
|
-
const targetDeps = target.deps
|
|
91
|
-
if (targetDeps) {
|
|
92
|
-
each(targetDeps, (dep: any) => {
|
|
93
|
-
if (dep.active !== false) {
|
|
94
|
-
Content(` spec.add_dependency "${dep.key$}", "~> ${dep.version || '0.0'}"
|
|
56
|
+
for (const d of collectDeps(model, target.name, target.deps)) {
|
|
57
|
+
Content(` spec.add_dependency "${d.name}", "~> ${versionOf(d)}"
|
|
95
58
|
`)
|
|
96
|
-
}
|
|
97
|
-
})
|
|
98
59
|
}
|
|
99
60
|
|
|
100
61
|
Content(`
|
|
@@ -20,10 +20,13 @@ function normalizePathParams(
|
|
|
20
20
|
return part.replace(/\{([^}]+)\}/g, (match: string, rawName: string) => {
|
|
21
21
|
const snaked = snakify(rawName)
|
|
22
22
|
const depluralized = depluralize(snaked)
|
|
23
|
+
// Prefer exact name match — orig matches can collide when one param's
|
|
24
|
+
// original name was renamed to another param's current name (e.g. badge
|
|
25
|
+
// load: param 'group_id' has orig 'id', and another param has name 'id').
|
|
23
26
|
const param = params.find((p: any) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
p.name === snaked || p.name === depluralized) ||
|
|
28
|
+
params.find((p: any) =>
|
|
29
|
+
p.orig === snaked || p.orig === depluralized)
|
|
27
30
|
if (param) return '{' + param.name + '}'
|
|
28
31
|
|
|
29
32
|
if (rename) {
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
File,
|
|
17
17
|
cmp,
|
|
18
18
|
each,
|
|
19
|
+
buildIdNames,
|
|
20
|
+
getMatchEntries,
|
|
19
21
|
} from '@voxgig/sdkgen'
|
|
20
22
|
|
|
21
23
|
|
|
@@ -45,15 +47,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
45
47
|
|
|
46
48
|
const PROJUPPER = model.const.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
|
|
47
49
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
// Build idmap names
|
|
51
|
-
const idnames: string[] = []
|
|
52
|
-
for (let i = 1; i <= 3; i++) idnames.push(`${entity.name}0${i}`)
|
|
53
|
-
for (const anc of ancestors) {
|
|
54
|
-
for (let i = 1; i <= 3; i++) idnames.push(`${anc}0${i}`)
|
|
55
|
-
}
|
|
56
|
-
|
|
50
|
+
const idnames = buildIdNames(entity, basicflow)
|
|
57
51
|
const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
|
|
58
52
|
|
|
59
53
|
const allSteps = Object.values(basicflow.step) as any[]
|
|
@@ -92,7 +86,7 @@ class ${entity.Name}EntityTest < Minitest::Test
|
|
|
92
86
|
if (!flowHasCreate) {
|
|
93
87
|
Content(` # Bootstrap entity data from existing test data.
|
|
94
88
|
${entity.name}_ref01_data_raw = Vs.items(Helpers.to_map(
|
|
95
|
-
Vs.
|
|
89
|
+
Vs.getpath(setup[:data], "existing.${entity.name}")))
|
|
96
90
|
${entity.name}_ref01_data = nil
|
|
97
91
|
if ${entity.name}_ref01_data_raw.length > 0
|
|
98
92
|
${entity.name}_ref01_data = Helpers.to_map(${entity.name}_ref01_data_raw[0][1])
|
|
@@ -191,12 +185,6 @@ end
|
|
|
191
185
|
})
|
|
192
186
|
|
|
193
187
|
|
|
194
|
-
function getMatchEntries(step: any): [string, any][] {
|
|
195
|
-
if (!step?.match) return []
|
|
196
|
-
return Object.entries(step.match).filter(([k]: any) => !k.endsWith('$'))
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
188
|
const generateCreate: OpGen = (ctx, step, index) => {
|
|
201
189
|
const { entity, flow } = ctx
|
|
202
190
|
const ref = step.input?.ref ?? entity.name + '_ref01'
|
|
@@ -417,7 +405,7 @@ const generateLoad: OpGen = (ctx, step, index) => {
|
|
|
417
405
|
}
|
|
418
406
|
if (!hasSrcData) {
|
|
419
407
|
Content(` ${srcdatavar}_raw = Vs.items(Helpers.to_map(
|
|
420
|
-
Vs.
|
|
408
|
+
Vs.getpath(setup[:data], "existing.${entity.name}")))
|
|
421
409
|
${srcdatavar} = nil
|
|
422
410
|
if ${srcdatavar}_raw.length > 0
|
|
423
411
|
${srcdatavar} = Helpers.to_map(${srcdatavar}_raw[0][1])
|
|
@@ -302,10 +302,13 @@ function normalizePathParams(
|
|
|
302
302
|
return part.replace(/\{([^}]+)\}/g, (match: string, rawName: string) => {
|
|
303
303
|
const snaked = snakify(rawName)
|
|
304
304
|
const depluralized = depluralize(snaked)
|
|
305
|
+
// Prefer exact name match — orig matches can collide when one param's
|
|
306
|
+
// original name was renamed to another param's current name (e.g. badge
|
|
307
|
+
// load: param 'group_id' has orig 'id', and another param has name 'id').
|
|
305
308
|
const param = params.find((p: any) =>
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
+
p.name === snaked || p.name === depluralized) ||
|
|
310
|
+
params.find((p: any) =>
|
|
311
|
+
p.orig === snaked || p.orig === depluralized)
|
|
309
312
|
if (param) return '{' + param.name + '}'
|
|
310
313
|
|
|
311
314
|
// Reverse-lookup through rename mapping: if rawName is a renamed value
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
package core
|
|
2
2
|
|
|
3
|
+
import "fmt"
|
|
4
|
+
|
|
5
|
+
// UnsupportedOp is returned by entity stub methods for operations the
|
|
6
|
+
// underlying API spec doesn't define. The static ProjectNameEntity interface
|
|
7
|
+
// requires every CRUD method on every entity, so absent ops must still be
|
|
8
|
+
// callable — they error at runtime instead of failing to compile.
|
|
9
|
+
func UnsupportedOp(opname, entityname string) (any, error) {
|
|
10
|
+
return nil, fmt.Errorf("operation '%s' not supported by entity '%s'", opname, entityname)
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
func ToMapAny(v any) map[string]any {
|
|
4
14
|
if v == nil {
|
|
5
15
|
return nil
|
package/src/action/target.ts
CHANGED
|
@@ -91,7 +91,13 @@ async function target_add(targets: string[], actx: ActionContext): Promise<Actio
|
|
|
91
91
|
|
|
92
92
|
showChanges(opts.log, 'target-result', jres)
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
// The `test` feature is required by every generated target (SDK.test()
|
|
95
|
+
// depends on it), so ensure it is added even if the model does not yet
|
|
96
|
+
// declare it.
|
|
97
|
+
const features = Array.from(new Set([
|
|
98
|
+
'test',
|
|
99
|
+
...Object.keys(actx.model.main[KIT].feature),
|
|
100
|
+
]))
|
|
95
101
|
await feature_add(features, actx)
|
|
96
102
|
|
|
97
103
|
opts.log.info({
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Build the list of placeholder ID names that the test setup populates into
|
|
2
|
+
// `setup.idmap`. The set is the union of:
|
|
3
|
+
// - the entity's own ids (`<entity>01..03`)
|
|
4
|
+
// - every ancestor entity's ids (`<anc>01..03`)
|
|
5
|
+
// - every literal string value referenced by `step.match` across the flow
|
|
6
|
+
// (e.g. path-parameter aliases like `year01` for `/{year}/domain` — apidef
|
|
7
|
+
// doesn't always populate `relations.ancestors` when the parent in the
|
|
8
|
+
// path is a bare parameter rather than an entity, so we have to mine the
|
|
9
|
+
// flow steps directly to avoid KeyError-ing the idmap from generated test
|
|
10
|
+
// code)
|
|
11
|
+
//
|
|
12
|
+
// Identical helper was previously inlined in TestEntity_{go,py,lua,rb,php}.ts.
|
|
13
|
+
|
|
14
|
+
const COUNT = 3 // 3 ids per name: <name>01, <name>02, <name>03
|
|
15
|
+
|
|
16
|
+
type FlowLike = {
|
|
17
|
+
step?: Record<string, any> | any[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type EntityLike = {
|
|
21
|
+
name: string
|
|
22
|
+
relations?: {
|
|
23
|
+
ancestors?: any
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildIdNames(entity: EntityLike, flow: FlowLike): string[] {
|
|
28
|
+
const idnames: string[] = []
|
|
29
|
+
const seen = new Set<string>()
|
|
30
|
+
const push = (n: string) => {
|
|
31
|
+
if (!seen.has(n)) {
|
|
32
|
+
seen.add(n)
|
|
33
|
+
idnames.push(n)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (let i = 1; i <= COUNT; i++) push(`${entity.name}0${i}`)
|
|
38
|
+
|
|
39
|
+
const ancestors: string[] = (entity.relations?.ancestors || []).flat()
|
|
40
|
+
for (const anc of ancestors) {
|
|
41
|
+
for (let i = 1; i <= COUNT; i++) push(`${anc}0${i}`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const steps = Array.isArray(flow?.step)
|
|
45
|
+
? flow.step
|
|
46
|
+
: Object.values(flow?.step || {})
|
|
47
|
+
for (const step of steps) {
|
|
48
|
+
if (step?.match) {
|
|
49
|
+
for (const v of Object.values(step.match)) {
|
|
50
|
+
if (typeof v === 'string' && v && !v.endsWith('$')) push(v)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// step.data values can also be aliased via setup (e.g. update step.data
|
|
54
|
+
// = {data_type_id: 'data_type01'} → setup adds idmap[data_type_id] =
|
|
55
|
+
// idmap[data_type01]). The right-hand side `data_type01` must be in the
|
|
56
|
+
// idmap or the alias resolves to undefined.
|
|
57
|
+
if (step?.data) {
|
|
58
|
+
for (const v of Object.values(step.data)) {
|
|
59
|
+
if (typeof v === 'string' && v && !v.endsWith('$')) push(v)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return idnames
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
buildIdNames,
|
|
70
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Collect target-language dependencies from features and from the target's
|
|
2
|
+
// own `deps` block, applying the active-flag semantics that every Package_*.ts
|
|
3
|
+
// template was hand-rolling identically:
|
|
4
|
+
//
|
|
5
|
+
// - feature deps : included when `dep.active === true` (default off)
|
|
6
|
+
// - target deps : included when `dep.active !== false` (default on)
|
|
7
|
+
//
|
|
8
|
+
// The two sources are kept distinct via the `source` field so callers can
|
|
9
|
+
// apply their own version defaults / formatting (e.g. go uses `v0.0.0`,
|
|
10
|
+
// python `0.0`). The original dep object is exposed as `raw` for callers that
|
|
11
|
+
// need extra fields like `dep.replace` (go module replace directives) or
|
|
12
|
+
// `dep.kind` (prod/dev/peer).
|
|
13
|
+
|
|
14
|
+
import { each } from 'jostraca'
|
|
15
|
+
import { KIT, getModelPath } from '@voxgig/apidef'
|
|
16
|
+
|
|
17
|
+
type DepEntry = {
|
|
18
|
+
name: string
|
|
19
|
+
version: string
|
|
20
|
+
source: 'feature' | 'target'
|
|
21
|
+
raw: any
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function collectDeps(
|
|
25
|
+
model: any,
|
|
26
|
+
targetName: string,
|
|
27
|
+
targetDeps: any,
|
|
28
|
+
): DepEntry[] {
|
|
29
|
+
const out: DepEntry[] = []
|
|
30
|
+
const feature = getModelPath(model, `main.${KIT}.feature`)
|
|
31
|
+
|
|
32
|
+
each(feature, (f: any) => {
|
|
33
|
+
const langDeps = f?.deps?.[targetName]
|
|
34
|
+
if (!langDeps) return
|
|
35
|
+
each(langDeps, (dep: any) => {
|
|
36
|
+
if (dep?.active) {
|
|
37
|
+
out.push({
|
|
38
|
+
name: dep.key$,
|
|
39
|
+
version: dep.version,
|
|
40
|
+
source: 'feature',
|
|
41
|
+
raw: dep,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
if (targetDeps) {
|
|
48
|
+
each(targetDeps, (dep: any) => {
|
|
49
|
+
if (dep?.active !== false) {
|
|
50
|
+
out.push({
|
|
51
|
+
name: dep.key$,
|
|
52
|
+
version: dep.version,
|
|
53
|
+
source: 'target',
|
|
54
|
+
raw: dep,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return out
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
export type {
|
|
65
|
+
DepEntry,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
collectDeps,
|
|
70
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Return the user-facing entries of a flow step's `match` object. Keys ending
|
|
2
|
+
// in `$` are jostraca/aontu metadata sentinels and are skipped.
|
|
3
|
+
//
|
|
4
|
+
// Identical helper was previously inlined in TestEntity_*.ts and TestDirect_*.ts.
|
|
5
|
+
|
|
6
|
+
function getMatchEntries(step: any): [string, any][] {
|
|
7
|
+
if (!step?.match) return []
|
|
8
|
+
return Object.entries(step.match).filter(([k]: any) => !k.endsWith('$'))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
getMatchEntries,
|
|
14
|
+
}
|
package/src/sdkgen.ts
CHANGED
|
@@ -38,6 +38,11 @@ import { ReadmeExplanation } from './cmp/ReadmeExplanation'
|
|
|
38
38
|
import { ReadmeRef } from './cmp/ReadmeRef'
|
|
39
39
|
import { FeatureHook } from './cmp/FeatureHook'
|
|
40
40
|
|
|
41
|
+
import { buildIdNames } from './helpers/buildIdNames'
|
|
42
|
+
import { getMatchEntries } from './helpers/getMatchEntries'
|
|
43
|
+
import { collectDeps } from './helpers/collectDeps'
|
|
44
|
+
import type { DepEntry } from './helpers/collectDeps'
|
|
45
|
+
|
|
41
46
|
|
|
42
47
|
import {
|
|
43
48
|
action_target,
|
|
@@ -90,7 +95,7 @@ const ACTION_MAP: any = {
|
|
|
90
95
|
const dlog = getdlog('sdkgen', __filename)
|
|
91
96
|
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
let aontu: any = null
|
|
94
99
|
|
|
95
100
|
|
|
96
101
|
function SdkGen(opts: SdkGenOptions) {
|
|
@@ -195,6 +200,17 @@ function SdkGen(opts: SdkGenOptions) {
|
|
|
195
200
|
function resolveModel() {
|
|
196
201
|
const path = './model/sdk.jsonic'
|
|
197
202
|
const errs: any[] = []
|
|
203
|
+
|
|
204
|
+
if (null == aontu) {
|
|
205
|
+
aontu = new Aontu({
|
|
206
|
+
preload: {
|
|
207
|
+
folders: ['./model'],
|
|
208
|
+
ext: ['.jsonic', '.json'],
|
|
209
|
+
recursive: true,
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
|
|
198
214
|
const aopts = { path, errs }
|
|
199
215
|
const src = fs.readFileSync(path, 'utf8')
|
|
200
216
|
|
|
@@ -319,6 +335,7 @@ function clear(path: string) {
|
|
|
319
335
|
|
|
320
336
|
export type {
|
|
321
337
|
SdkGenOptions,
|
|
338
|
+
DepEntry,
|
|
322
339
|
}
|
|
323
340
|
|
|
324
341
|
|
|
@@ -378,4 +395,8 @@ export {
|
|
|
378
395
|
SdkGen,
|
|
379
396
|
|
|
380
397
|
requirePath,
|
|
398
|
+
|
|
399
|
+
buildIdNames,
|
|
400
|
+
getMatchEntries,
|
|
401
|
+
collectDeps,
|
|
381
402
|
}
|