@voxgig/sdkgen 0.40.1 → 0.41.0
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/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 +7 -1
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- 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/helpers/buildIdNames.ts +70 -0
- package/src/helpers/collectDeps.ts +70 -0
- package/src/helpers/getMatchEntries.ts +14 -0
- package/src/sdkgen.ts +10 -0
|
@@ -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
|
|
@@ -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,8 +18,6 @@ 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: model.name + '.rockspec' }, () => {
|
|
26
22
|
Content(`package = "${model.name}-sdk"
|
|
27
23
|
version = "0.0-1"
|
|
@@ -37,28 +33,10 @@ dependencies = {
|
|
|
37
33
|
"dkjson >= 2.5",
|
|
38
34
|
`)
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (luaDeps) {
|
|
44
|
-
each(luaDeps, (dep: any) => {
|
|
45
|
-
if (dep.active) {
|
|
46
|
-
Content(` "${dep.key$} >= ${dep.version}",
|
|
47
|
-
`)
|
|
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'}",
|
|
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}",
|
|
59
39
|
`)
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
40
|
}
|
|
63
41
|
|
|
64
42
|
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[]
|
|
@@ -95,7 +89,7 @@ describe("${entity.Name}Entity", function()
|
|
|
95
89
|
if (!flowHasCreate) {
|
|
96
90
|
Content(` -- Bootstrap entity data from existing test data.
|
|
97
91
|
local ${entity.name}_ref01_data_raw = vs.items(helpers.to_map(
|
|
98
|
-
vs.
|
|
92
|
+
vs.getpath(setup.data, "existing.${entity.name}")))
|
|
99
93
|
local ${entity.name}_ref01_data = nil
|
|
100
94
|
if #${entity.name}_ref01_data_raw > 0 then
|
|
101
95
|
${entity.name}_ref01_data = helpers.to_map(${entity.name}_ref01_data_raw[1][2])
|
|
@@ -200,12 +194,6 @@ end
|
|
|
200
194
|
})
|
|
201
195
|
|
|
202
196
|
|
|
203
|
-
function getMatchEntries(step: any): [string, any][] {
|
|
204
|
-
if (!step?.match) return []
|
|
205
|
-
return Object.entries(step.match).filter(([k]: any) => !k.endsWith('$'))
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
197
|
const generateCreate: OpGen = (ctx, step, index) => {
|
|
210
198
|
const { entity, flow } = ctx
|
|
211
199
|
const ref = step.input?.ref ?? entity.name + '_ref01'
|
|
@@ -426,7 +414,7 @@ const generateLoad: OpGen = (ctx, step, index) => {
|
|
|
426
414
|
}
|
|
427
415
|
if (!hasSrcData) {
|
|
428
416
|
Content(` local ${srcdatavar}_raw = vs.items(helpers.to_map(
|
|
429
|
-
vs.
|
|
417
|
+
vs.getpath(setup.data, "existing.${entity.name}")))
|
|
430
418
|
local ${srcdatavar} = nil
|
|
431
419
|
if #${srcdatavar}_raw > 0 then
|
|
432
420
|
${srcdatavar} = helpers.to_map(${srcdatavar}_raw[1][2])
|
|
@@ -67,6 +67,24 @@ class ${model.const.Name}Config
|
|
|
67
67
|
`)
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
+
// PHP can't distinguish empty list from empty map; the SDK runtime
|
|
71
|
+
// validator wants an object for `entity` and `feature.test.entity`. Use
|
|
72
|
+
// `(object)[]` when the map is empty so the merge preserves map shape.
|
|
73
|
+
const entityIsEmpty = Object.keys(entity || {}).length === 0
|
|
74
|
+
if (entityIsEmpty) {
|
|
75
|
+
Content(` ],
|
|
76
|
+
"options" => [
|
|
77
|
+
"base" => "${baseUrl}",
|
|
78
|
+
"auth" => [
|
|
79
|
+
"prefix" => "${authPrefix}",
|
|
80
|
+
],
|
|
81
|
+
"headers" => ${formatPhpArray(headers, 4)},
|
|
82
|
+
"entity" => (object)[],
|
|
83
|
+
],
|
|
84
|
+
"entity" => (object)[],
|
|
85
|
+
];
|
|
86
|
+
`)
|
|
87
|
+
} else {
|
|
70
88
|
Content(` ],
|
|
71
89
|
"options" => [
|
|
72
90
|
"base" => "${baseUrl}",
|
|
@@ -92,8 +110,11 @@ class ${model.const.Name}Config
|
|
|
92
110
|
relations: n.relations,
|
|
93
111
|
}), a), {}), 3)},
|
|
94
112
|
];
|
|
113
|
+
`)
|
|
95
114
|
}
|
|
96
115
|
|
|
116
|
+
Content(` }
|
|
117
|
+
|
|
97
118
|
|
|
98
119
|
public static function make_feature(string $name)
|
|
99
120
|
{
|
|
@@ -3,12 +3,22 @@
|
|
|
3
3
|
import { cmp, Content } from '@voxgig/sdkgen'
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
// Reserved PHP method names on the SDK class that an entity accessor must
|
|
7
|
+
// not collide with. PHP method names are case-insensitive at declaration
|
|
8
|
+
// time, so an entity literally named 'test' would collide with the static
|
|
9
|
+
// `test()` test-mode constructor. Mangle to `<Name>_` in that case.
|
|
10
|
+
const PHP_RESERVED_LOWER = new Set(['test'])
|
|
11
|
+
|
|
6
12
|
const MainEntity = cmp(async function MainEntity(props: any) {
|
|
7
13
|
const { entity } = props
|
|
8
14
|
const { model } = props.ctx$
|
|
9
15
|
|
|
16
|
+
const accessor = PHP_RESERVED_LOWER.has(entity.Name.toLowerCase())
|
|
17
|
+
? entity.Name + '_'
|
|
18
|
+
: entity.Name
|
|
19
|
+
|
|
10
20
|
Content(`
|
|
11
|
-
public function ${
|
|
21
|
+
public function ${accessor}($data = null)
|
|
12
22
|
{
|
|
13
23
|
require_once __DIR__ . '/entity/${entity.name}_entity.php';
|
|
14
24
|
return new ${entity.Name}Entity($this, $data);
|
|
@@ -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,8 +18,6 @@ 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
|
// Generate composer.json
|
|
26
22
|
File({ name: 'composer.json' }, () => {
|
|
27
23
|
Content(`{
|
|
@@ -33,32 +29,10 @@ const Package = cmp(async function Package(props: any) {
|
|
|
33
29
|
"require": {
|
|
34
30
|
"php": ">=8.2"`)
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
each(feature, (f: any) => {
|
|
39
|
-
const phpDeps = f.deps?.php
|
|
40
|
-
if (phpDeps) {
|
|
41
|
-
each(phpDeps, (dep: any) => {
|
|
42
|
-
if (dep.active) {
|
|
43
|
-
deps.push({ name: dep.key$, version: dep.version })
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
// Add target-level deps
|
|
50
|
-
const targetDeps = target.deps
|
|
51
|
-
if (targetDeps) {
|
|
52
|
-
each(targetDeps, (dep: any) => {
|
|
53
|
-
if (dep.active !== false) {
|
|
54
|
-
deps.push({ name: dep.key$, version: dep.version || '0.0' })
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
for (const dep of deps) {
|
|
32
|
+
for (const d of collectDeps(model, target.name, target.deps)) {
|
|
33
|
+
const v = d.source === 'target' ? (d.version || '0.0') : d.version
|
|
60
34
|
Content(`,
|
|
61
|
-
"${
|
|
35
|
+
"${d.name}": "^${v}"`)
|
|
62
36
|
}
|
|
63
37
|
|
|
64
38
|
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
|
|
|
@@ -26,6 +28,7 @@ type GenCtx = {
|
|
|
26
28
|
entity: any
|
|
27
29
|
flow: any
|
|
28
30
|
PROJUPPER: string
|
|
31
|
+
accessor: string
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
|
|
@@ -43,17 +46,16 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
43
46
|
return
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
// PHP method names are case-insensitive — an entity literally named 'test'
|
|
50
|
+
// collides with the static `test()` test-mode constructor on the SDK class.
|
|
51
|
+
// Mirror the mangling done in MainEntity_php.ts.
|
|
52
|
+
const accessor = 'test' === entity.Name.toLowerCase()
|
|
53
|
+
? entity.Name + '_'
|
|
54
|
+
: entity.Name
|
|
49
55
|
|
|
50
|
-
|
|
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
|
+
const PROJUPPER = model.const.Name.toUpperCase().replace(/[^A-Z_]/g, '_')
|
|
56
57
|
|
|
58
|
+
const idnames = buildIdNames(entity, basicflow)
|
|
57
59
|
const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
|
|
58
60
|
|
|
59
61
|
const allSteps = Object.values(basicflow.step) as any[]
|
|
@@ -63,7 +65,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
63
65
|
: []
|
|
64
66
|
const aliases = updateData.map(([k, v]: any) => [k, v])
|
|
65
67
|
|
|
66
|
-
const genCtx: GenCtx = { model, entity, flow: basicflow, PROJUPPER }
|
|
68
|
+
const genCtx: GenCtx = { model, entity, flow: basicflow, PROJUPPER, accessor }
|
|
67
69
|
|
|
68
70
|
File({ name: entity.Name + 'EntityTest.' + target.ext }, () => {
|
|
69
71
|
|
|
@@ -83,7 +85,7 @@ class ${entity.Name}EntityTest extends TestCase
|
|
|
83
85
|
public function test_create_instance(): void
|
|
84
86
|
{
|
|
85
87
|
$testsdk = ${model.const.Name}SDK::test(null, null);
|
|
86
|
-
$ent = $testsdk->${
|
|
88
|
+
$ent = $testsdk->${accessor}(null);
|
|
87
89
|
$this->assertNotNull($ent);
|
|
88
90
|
}
|
|
89
91
|
|
|
@@ -99,7 +101,7 @@ class ${entity.Name}EntityTest extends TestCase
|
|
|
99
101
|
if (!flowHasCreate) {
|
|
100
102
|
Content(` // Bootstrap entity data from existing test data.
|
|
101
103
|
$${entity.name}_ref01_data_raw = Vs::items(Helpers::to_map(
|
|
102
|
-
Vs::
|
|
104
|
+
Vs::getpath($setup["data"], "existing.${entity.name}")));
|
|
103
105
|
$${entity.name}_ref01_data = null;
|
|
104
106
|
if (count($${entity.name}_ref01_data_raw) > 0) {
|
|
105
107
|
$${entity.name}_ref01_data = Helpers::to_map($${entity.name}_ref01_data_raw[0][1]);
|
|
@@ -194,14 +196,8 @@ class ${entity.Name}EntityTest extends TestCase
|
|
|
194
196
|
})
|
|
195
197
|
|
|
196
198
|
|
|
197
|
-
function getMatchEntries(step: any): [string, any][] {
|
|
198
|
-
if (!step?.match) return []
|
|
199
|
-
return Object.entries(step.match).filter(([k]: any) => !k.endsWith('$'))
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
199
|
const generateCreate: OpGen = (ctx, step, index) => {
|
|
204
|
-
const { entity, flow } = ctx
|
|
200
|
+
const { entity, flow, accessor } = ctx
|
|
205
201
|
const ref = step.input?.ref ?? entity.name + '_ref01'
|
|
206
202
|
const entvar = step.input?.entvar ?? ref + '_ent'
|
|
207
203
|
const datavar = step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
|
|
@@ -222,7 +218,7 @@ const generateCreate: OpGen = (ctx, step, index) => {
|
|
|
222
218
|
Content(` // CREATE
|
|
223
219
|
`)
|
|
224
220
|
if (needsEnt) {
|
|
225
|
-
Content(` $${entvar} = $client->${
|
|
221
|
+
Content(` $${entvar} = $client->${accessor}(null);
|
|
226
222
|
`)
|
|
227
223
|
}
|
|
228
224
|
|
|
@@ -253,7 +249,7 @@ const generateCreate: OpGen = (ctx, step, index) => {
|
|
|
253
249
|
|
|
254
250
|
|
|
255
251
|
const generateList: OpGen = (ctx, step, index) => {
|
|
256
|
-
const { entity, flow } = ctx
|
|
252
|
+
const { entity, flow, accessor } = ctx
|
|
257
253
|
const ref = step.input?.ref ?? entity.name + '_ref01'
|
|
258
254
|
const entvar = step.input?.entvar ?? ref + '_ent'
|
|
259
255
|
const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
|
|
@@ -266,7 +262,7 @@ const generateList: OpGen = (ctx, step, index) => {
|
|
|
266
262
|
Content(` // LIST
|
|
267
263
|
`)
|
|
268
264
|
if (needsEnt) {
|
|
269
|
-
Content(` $${entvar} = $client->${
|
|
265
|
+
Content(` $${entvar} = $client->${accessor}(null);
|
|
270
266
|
`)
|
|
271
267
|
}
|
|
272
268
|
|
|
@@ -322,7 +318,7 @@ const generateList: OpGen = (ctx, step, index) => {
|
|
|
322
318
|
|
|
323
319
|
|
|
324
320
|
const generateUpdate: OpGen = (ctx, step, index) => {
|
|
325
|
-
const { entity, flow } = ctx
|
|
321
|
+
const { entity, flow, accessor } = ctx
|
|
326
322
|
const ref = step.input?.ref ?? entity.name + '_ref01'
|
|
327
323
|
const entvar = step.input?.entvar ?? ref + '_ent'
|
|
328
324
|
const datavar = step.input?.datavar ?? (ref + '_data' + (step.input?.suffix ?? ''))
|
|
@@ -337,7 +333,7 @@ const generateUpdate: OpGen = (ctx, step, index) => {
|
|
|
337
333
|
Content(` // UPDATE
|
|
338
334
|
`)
|
|
339
335
|
if (needsEnt) {
|
|
340
|
-
Content(` $${entvar} = $client->${
|
|
336
|
+
Content(` $${entvar} = $client->${accessor}(null);
|
|
341
337
|
`)
|
|
342
338
|
}
|
|
343
339
|
Content(` $${datavar}_up = [
|
|
@@ -389,7 +385,7 @@ const generateUpdate: OpGen = (ctx, step, index) => {
|
|
|
389
385
|
|
|
390
386
|
|
|
391
387
|
const generateLoad: OpGen = (ctx, step, index) => {
|
|
392
|
-
const { entity, flow } = ctx
|
|
388
|
+
const { entity, flow, accessor } = ctx
|
|
393
389
|
const ref = step.input?.ref ?? entity.name + '_ref01'
|
|
394
390
|
const entvar = step.input?.entvar ?? ref + '_ent'
|
|
395
391
|
const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
|
|
@@ -415,12 +411,12 @@ const generateLoad: OpGen = (ctx, step, index) => {
|
|
|
415
411
|
Content(` // LOAD
|
|
416
412
|
`)
|
|
417
413
|
if (!hasEntVar) {
|
|
418
|
-
Content(` $${entvar} = $client->${
|
|
414
|
+
Content(` $${entvar} = $client->${accessor}(null);
|
|
419
415
|
`)
|
|
420
416
|
}
|
|
421
417
|
if (!hasSrcData) {
|
|
422
418
|
Content(` $${srcdatavar}_raw = Vs::items(Helpers::to_map(
|
|
423
|
-
Vs::
|
|
419
|
+
Vs::getpath($setup["data"], "existing.${entity.name}")));
|
|
424
420
|
$${srcdatavar} = null;
|
|
425
421
|
if (count($${srcdatavar}_raw) > 0) {
|
|
426
422
|
$${srcdatavar} = Helpers::to_map($${srcdatavar}_raw[0][1]);
|
|
@@ -440,7 +436,7 @@ const generateLoad: OpGen = (ctx, step, index) => {
|
|
|
440
436
|
|
|
441
437
|
|
|
442
438
|
const generateRemove: OpGen = (ctx, step, index) => {
|
|
443
|
-
const { entity, flow } = ctx
|
|
439
|
+
const { entity, flow, accessor } = ctx
|
|
444
440
|
const ref = step.input?.ref ?? entity.name + '_ref01'
|
|
445
441
|
const entvar = step.input?.entvar ?? ref + '_ent'
|
|
446
442
|
const matchvar = step.input?.matchvar ?? (ref + '_match' + (step.input?.suffix ?? ''))
|
|
@@ -453,7 +449,7 @@ const generateRemove: OpGen = (ctx, step, index) => {
|
|
|
453
449
|
Content(` // REMOVE
|
|
454
450
|
`)
|
|
455
451
|
if (needsEnt) {
|
|
456
|
-
Content(` $${entvar} = $client->${
|
|
452
|
+
Content(` $${entvar} = $client->${accessor}(null);
|
|
457
453
|
`)
|
|
458
454
|
}
|
|
459
455
|
Content(` $${matchvar} = [
|
|
@@ -119,11 +119,11 @@ def _make_feature(name):
|
|
|
119
119
|
`)
|
|
120
120
|
})
|
|
121
121
|
|
|
122
|
-
// Generate __init__.py files for packages
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
// Generate __init__.py files for sub-packages.
|
|
123
|
+
// NOTE: deliberately omit __init__.py at the language-root (py/) level —
|
|
124
|
+
// making py/ a package collides with the third-party `py` module on PyPI
|
|
125
|
+
// (a single-file `py.py`), which causes pytest to construct test module
|
|
126
|
+
// paths as `py.test.<file>` and fail with "'py' is not a package".
|
|
127
127
|
Folder({ name: 'core' }, () => {
|
|
128
128
|
File({ name: '__init__.' + target.ext }, () => {
|
|
129
129
|
Content(``)
|
|
@@ -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])
|