@voxgig/sdkgen 1.3.4 → 1.3.6
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/cmp/ReadmeErrors.js +15 -15
- package/dist/cmp/ReadmeErrors.js.map +1 -1
- package/dist/cmp/ReadmeExplanation.js +37 -35
- package/dist/cmp/ReadmeExplanation.js.map +1 -1
- package/dist/cmp/ReadmeTop.js +58 -29
- package/dist/cmp/ReadmeTop.js.map +1 -1
- package/dist/helpers/naming.js +29 -2
- package/dist/helpers/naming.js.map +1 -1
- package/dist/helpers/opShape.d.ts +6 -1
- package/dist/helpers/opShape.js +78 -0
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +3 -1
- package/dist/sdkgen.js.map +1 -1
- package/package.json +1 -1
- package/project/.sdk/src/cmp/go/EntityOperation_go.ts +8 -1
- package/project/.sdk/src/cmp/go/Entity_go.ts +20 -3
- package/project/.sdk/src/cmp/go/Main_go.ts +2 -1
- package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +5 -5
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +23 -12
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +27 -17
- package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +5 -5
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +5 -6
- package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +11 -11
- package/project/.sdk/src/cmp/go/fragment/EntityCreateOp.fragment.go +2 -2
- package/project/.sdk/src/cmp/go/fragment/EntityListOp.fragment.go +2 -2
- package/project/.sdk/src/cmp/go/fragment/EntityLoadOp.fragment.go +2 -2
- package/project/.sdk/src/cmp/go/fragment/EntityRemoveOp.fragment.go +2 -2
- package/project/.sdk/src/cmp/go/fragment/EntityUpdateOp.fragment.go +2 -2
- package/project/.sdk/src/cmp/js/Entity_js.ts +18 -2
- package/project/.sdk/src/cmp/js/MainEntity_js.ts +12 -2
- package/project/.sdk/src/cmp/js/Main_js.ts +5 -2
- package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +5 -5
- package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +37 -26
- package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +5 -5
- package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +5 -6
- package/project/.sdk/src/cmp/js/fragment/Entity.fragment.js +3 -3
- package/project/.sdk/src/cmp/lua/EntityOperation_lua.ts +6 -1
- package/project/.sdk/src/cmp/lua/Entity_lua.ts +20 -2
- package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +8 -5
- package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +14 -7
- package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +9 -6
- package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +5 -2
- package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +8 -5
- package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +5 -6
- package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +13 -13
- package/project/.sdk/src/cmp/lua/fragment/EntityCreateOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityListOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityLoadOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityRemoveOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityUpdateOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/php/Entity_php.ts +17 -1
- package/project/.sdk/src/cmp/php/MainEntity_php.ts +12 -3
- package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +15 -8
- package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +5 -6
- package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +2 -2
- package/project/.sdk/src/cmp/py/Entity_py.ts +18 -1
- package/project/.sdk/src/cmp/py/MainEntity_py.ts +14 -4
- package/project/.sdk/src/cmp/py/Main_py.ts +2 -1
- package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +8 -5
- package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +18 -8
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +10 -7
- package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +7 -4
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +9 -6
- package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +6 -7
- package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +2 -2
- package/project/.sdk/src/cmp/rb/Entity_rb.ts +17 -1
- package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +11 -2
- package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +8 -5
- package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +20 -8
- package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +9 -6
- package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +5 -2
- package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +8 -5
- package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +10 -8
- package/project/.sdk/src/cmp/rb/fragment/Entity.fragment.rb +2 -2
- package/project/.sdk/src/cmp/ts/Entity_ts.ts +18 -2
- package/project/.sdk/src/cmp/ts/MainEntity_ts.ts +12 -2
- package/project/.sdk/src/cmp/ts/Main_ts.ts +5 -2
- package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +5 -5
- package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +36 -26
- package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +4 -5
- package/project/.sdk/src/cmp/ts/ReadmeTopTest_ts.ts +5 -6
- package/project/.sdk/src/cmp/ts/fragment/Entity.fragment.ts +4 -4
- package/src/cmp/ReadmeErrors.ts +16 -17
- package/src/cmp/ReadmeExplanation.ts +37 -35
- package/src/cmp/ReadmeTop.ts +60 -31
- package/src/helpers/naming.ts +32 -2
- package/src/helpers/opShape.ts +89 -0
- package/src/sdkgen.ts +3 -1
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import { cmp, Content } from '@voxgig/sdkgen'
|
|
3
|
+
import { cmp, Content, entityClassName } from '@voxgig/sdkgen'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
KIT,
|
|
7
|
+
getModelPath
|
|
8
|
+
} from '@voxgig/apidef'
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
const MainEntity = cmp(async function MainEntity(props: any) {
|
|
7
12
|
const { entity } = props
|
|
13
|
+
const { model } = props.ctx$
|
|
14
|
+
|
|
15
|
+
// Return the collision-free class (entityClassName); the accessor METHOD name
|
|
16
|
+
// (entity.Name) is unchanged so callers still write client.<Name>().
|
|
17
|
+
const cls = entityClassName(entity, getModelPath(model, `main.${KIT}.entity`))
|
|
8
18
|
|
|
9
19
|
Content(`
|
|
10
20
|
// Entity access: \`client.${entity.Name}().list()\` / \`client.${entity.Name}().load({ id })\`.
|
|
11
21
|
${entity.Name}(data) {
|
|
12
22
|
const self = this
|
|
13
|
-
return new ${
|
|
23
|
+
return new ${cls}(self,data)
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
`)
|
|
@@ -4,6 +4,7 @@ import * as Path from 'node:path'
|
|
|
4
4
|
import {
|
|
5
5
|
cmp, each, names, cmap,
|
|
6
6
|
List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
|
|
7
|
+
entityClassName,
|
|
7
8
|
} from '@voxgig/sdkgen'
|
|
8
9
|
|
|
9
10
|
|
|
@@ -55,8 +56,10 @@ const Main = cmp(async function Main(props: any) {
|
|
|
55
56
|
|
|
56
57
|
Line(`// ${model.const.Name} ${target.Name} SDK\n`)
|
|
57
58
|
|
|
58
|
-
List({ item: entity }, ({ item }: any) =>
|
|
59
|
-
|
|
59
|
+
List({ item: entity }, ({ item }: any) => {
|
|
60
|
+
const cls = entityClassName(item, entity)
|
|
61
|
+
return Line(`const { ${cls} } = require('./entity/${cls}')`)
|
|
62
|
+
})
|
|
60
63
|
|
|
61
64
|
Fragment(
|
|
62
65
|
{
|
|
@@ -132,10 +132,10 @@ const ${eVar}s = await client.${entity.Name}().list()
|
|
|
132
132
|
|
|
133
133
|
if (opnames.includes('create')) {
|
|
134
134
|
// Members come from the SAME shape the runtime validates
|
|
135
|
-
// (opRequestShape): every required member
|
|
136
|
-
// required id and parent keys like page_id
|
|
137
|
-
//
|
|
138
|
-
//
|
|
135
|
+
// (opRequestShape): every required member appears — including a
|
|
136
|
+
// required id and parent keys like page_id — each with a type-correct
|
|
137
|
+
// example VALUE via exampleValue — a `name: /* type */` comment is not
|
|
138
|
+
// a value and yields invalid code.
|
|
139
139
|
const createItems = opRequestShape(entity, 'create').items
|
|
140
140
|
.filter((it: any) => !it.optional)
|
|
141
141
|
Content(`#### Example: Create
|
|
@@ -144,7 +144,7 @@ const ${eVar}s = await client.${entity.Name}().list()
|
|
|
144
144
|
const ${eVar} = await client.${entity.Name}().create({
|
|
145
145
|
`)
|
|
146
146
|
createItems.map((it: any) => {
|
|
147
|
-
Content(` ${it.name}:
|
|
147
|
+
Content(` ${it.name}: ${exampleValue(entity, entity.op && entity.op.create, it.name, 'example_' + it.name)},
|
|
148
148
|
`)
|
|
149
149
|
})
|
|
150
150
|
Content(`})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, Content, isAuthActive, envName, entityIdField, entityDataIdField,
|
|
2
|
+
import { cmp, Content, isAuthActive, envName, entityIdField, entityDataIdField, pickExampleEntity, opRequestShape, safeVarName } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -14,14 +14,14 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
|
|
|
14
14
|
const { target, ctx$: { model } } = props
|
|
15
15
|
|
|
16
16
|
const entity = getModelPath(model, `main.${KIT}.entity`)
|
|
17
|
-
|
|
17
|
+
// Pick an entity with a real op (prefer a read op) — never fabricate a
|
|
18
|
+
// `load` on an op-less entity like Cloudsmith's `Abort`. primaryOp is null
|
|
19
|
+
// only when NO entity exposes any op (a direct()-only SDK).
|
|
20
|
+
const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
|
|
18
21
|
const eName = exampleEntity ? nom(exampleEntity, 'Name') : 'Entity'
|
|
19
22
|
const eVar = safeVarName(eName.toLowerCase(), 'js')
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
// it actually exposes — never a hardcoded `load` a create-only entity lacks.
|
|
23
|
-
const primaryOp = exampleEntity ? (entityPrimaryOp(exampleEntity) || 'load') : 'load'
|
|
24
|
-
const primaryOpDef = exampleEntity && exampleEntity.op && exampleEntity.op[primaryOp]
|
|
24
|
+
const primaryOpDef = exampleEntity && primaryOp && exampleEntity.op && exampleEntity.op[primaryOp]
|
|
25
25
|
const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
|
|
26
26
|
// idF is the MATCH key; dataIdF is the id on the RETURNED record's data type
|
|
27
27
|
// (guard `.id` reads off a returned record on this, not the match key).
|
|
@@ -29,7 +29,7 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
|
|
|
29
29
|
const dataIdF = exampleEntity ? entityDataIdField(exampleEntity) : null
|
|
30
30
|
|
|
31
31
|
const primaryArg = (idPlaceholder: string): string => {
|
|
32
|
-
if (!exampleEntity) return ''
|
|
32
|
+
if (!exampleEntity || !primaryOp) return ''
|
|
33
33
|
if ('list' === primaryOp) return ''
|
|
34
34
|
if (isMatchOp) {
|
|
35
35
|
return idF ? `{ ${idF}: ${exampleValue(exampleEntity, primaryOpDef, idF, idPlaceholder)} }` : ''
|
|
@@ -48,6 +48,34 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
|
|
|
48
48
|
? `console.log(data.${dataIdF})`
|
|
49
49
|
: `console.log(data)`
|
|
50
50
|
|
|
51
|
+
// The op-driven example lines, shown only when the SDK has an entity op.
|
|
52
|
+
// A direct()-only SDK (no ops anywhere) shows a direct() test call instead
|
|
53
|
+
// and omits the stateful section (there is no op to retain state across).
|
|
54
|
+
const testModeExample = primaryOp
|
|
55
|
+
? `const ${eVar} = await client.${eName}().${primaryOp}(${testCallArg})
|
|
56
|
+
// ${eVar} is a bare entity populated with mock response data
|
|
57
|
+
console.log(${eVar})`
|
|
58
|
+
: `const result = await client.direct({ path: '/api/resource', method: 'GET' })
|
|
59
|
+
console.log(result)`
|
|
60
|
+
const stateSection = primaryOp
|
|
61
|
+
? `### Retain entity state across calls
|
|
62
|
+
|
|
63
|
+
Entity instances remember their last match and data:
|
|
64
|
+
|
|
65
|
+
\`\`\`js
|
|
66
|
+
const entity = client.${eName}()
|
|
67
|
+
|
|
68
|
+
// First call runs the operation and stores its result
|
|
69
|
+
await entity.${primaryOp}(${stateCallArg})
|
|
70
|
+
|
|
71
|
+
// Subsequent calls reuse the stored state
|
|
72
|
+
const data = entity.data()
|
|
73
|
+
${stateDataLine}
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
`
|
|
77
|
+
: ''
|
|
78
|
+
|
|
51
79
|
const authActive = isAuthActive(model)
|
|
52
80
|
const apikeyTesterCtor = authActive
|
|
53
81
|
? `new ${model.const.Name}SDK({ apikey: '...' })`
|
|
@@ -97,9 +125,7 @@ Create a mock client for unit testing — no server required:
|
|
|
97
125
|
\`\`\`js
|
|
98
126
|
const client = ${model.const.Name}SDK.test()
|
|
99
127
|
|
|
100
|
-
|
|
101
|
-
// ${eVar} is a bare entity populated with mock response data
|
|
102
|
-
console.log(${eVar})
|
|
128
|
+
${testModeExample}
|
|
103
129
|
\`\`\`
|
|
104
130
|
|
|
105
131
|
You can also use the instance method:
|
|
@@ -109,22 +135,7 @@ const client = ${apikeyTesterCtor}
|
|
|
109
135
|
const testClient = client.tester()
|
|
110
136
|
\`\`\`
|
|
111
137
|
|
|
112
|
-
###
|
|
113
|
-
|
|
114
|
-
Entity instances remember their last match and data:
|
|
115
|
-
|
|
116
|
-
\`\`\`js
|
|
117
|
-
const entity = client.${eName}()
|
|
118
|
-
|
|
119
|
-
// First call runs the operation and stores its result
|
|
120
|
-
await entity.${primaryOp}(${stateCallArg})
|
|
121
|
-
|
|
122
|
-
// Subsequent calls reuse the stored state
|
|
123
|
-
const data = entity.data()
|
|
124
|
-
${stateDataLine}
|
|
125
|
-
\`\`\`
|
|
126
|
-
|
|
127
|
-
### Add custom middleware
|
|
138
|
+
${stateSection}### Add custom middleware
|
|
128
139
|
|
|
129
140
|
Pass features via the \`extend\` option:
|
|
130
141
|
|
|
@@ -299,17 +299,17 @@ const results = await client.${ent.Name}().${opname}()
|
|
|
299
299
|
}
|
|
300
300
|
else if ('create' === opname) {
|
|
301
301
|
// Members come from the SAME shape the runtime validates
|
|
302
|
-
// (opRequestShape): every required member
|
|
303
|
-
// a required id and parent keys like page_id
|
|
304
|
-
//
|
|
305
|
-
//
|
|
302
|
+
// (opRequestShape): every required member appears — including
|
|
303
|
+
// a required id and parent keys like page_id — each with a
|
|
304
|
+
// type-correct example VALUE via exampleValue — a
|
|
305
|
+
// `name: /* type */` comment is not a value and yields invalid code.
|
|
306
306
|
const createItems = opRequestShape(ent, 'create').items
|
|
307
307
|
.filter((it: any) => !it.optional)
|
|
308
308
|
Content(`\`\`\`ts
|
|
309
309
|
const result = await client.${ent.Name}().create({
|
|
310
310
|
`)
|
|
311
311
|
createItems.map((it: any) => {
|
|
312
|
-
Content(` ${it.name}:
|
|
312
|
+
Content(` ${it.name}: ${exampleValue(ent, ent.op && ent.op.create, it.name, 'example_' + it.name)},
|
|
313
313
|
`)
|
|
314
314
|
})
|
|
315
315
|
Content(`})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, Content, entityIdField,
|
|
2
|
+
import { cmp, Content, entityIdField, pickExampleEntity, opRequestShape, safeVarName } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -14,17 +14,16 @@ const ReadmeTopTest = cmp(function ReadmeTopTest(props: any) {
|
|
|
14
14
|
const { target, ctx$: { model } } = props
|
|
15
15
|
|
|
16
16
|
const entity = getModelPath(model, `main.${KIT}.entity`)
|
|
17
|
-
|
|
17
|
+
// Pick an entity with a real op (prefer a read op) — never fabricate a
|
|
18
|
+
// `load` on an op-less entity like Cloudsmith's `Abort`.
|
|
19
|
+
const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
|
|
18
20
|
|
|
19
21
|
Content(`\`\`\`js
|
|
20
22
|
const client = ${model.const.Name}SDK.test()
|
|
21
23
|
`)
|
|
22
24
|
|
|
23
|
-
if (exampleEntity) {
|
|
25
|
+
if (exampleEntity && primaryOp) {
|
|
24
26
|
const eName = nom(exampleEntity, 'Name')
|
|
25
|
-
// Drive the test-mode example off the entity's PRIMARY op (never a
|
|
26
|
-
// hardcoded `load` a create-only entity lacks).
|
|
27
|
-
const primaryOp = entityPrimaryOp(exampleEntity) || 'load'
|
|
28
27
|
// A list() result is an array — name the variable accordingly.
|
|
29
28
|
const eVar = safeVarName(eName.toLowerCase(), 'js') +
|
|
30
29
|
('list' === primaryOp ? 's' : '')
|
|
@@ -5,7 +5,7 @@ const { ProjectNameEntityBase } = require('../ProjectNameEntityBase')
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
// TODO: needs Entity superclass
|
|
8
|
-
class
|
|
8
|
+
class EntyClass extends ProjectNameEntityBase {
|
|
9
9
|
|
|
10
10
|
constructor(client, entopts) {
|
|
11
11
|
super(client, entopts)
|
|
@@ -16,7 +16,7 @@ class EntityNameEntity extends ProjectNameEntityBase {
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
make() {
|
|
19
|
-
return new
|
|
19
|
+
return new EntyClass(this._client, this.entopts())
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
|
|
@@ -34,5 +34,5 @@ class EntityNameEntity extends ProjectNameEntityBase {
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
module.exports = {
|
|
37
|
-
|
|
37
|
+
EntyClass
|
|
38
38
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
const EntityOperation = cmp(function Operation(props: any) {
|
|
9
9
|
const { model } = props.ctx$
|
|
10
|
-
const { ff, opname, entity, entrep } = props
|
|
10
|
+
const { ff, opname, entity, entrep, cls } = props
|
|
11
11
|
|
|
12
12
|
let { indent } = props
|
|
13
13
|
|
|
@@ -29,6 +29,11 @@ const EntityOperation = cmp(function Operation(props: any) {
|
|
|
29
29
|
ProjectName: model.const.Name,
|
|
30
30
|
EntityName: entity.Name,
|
|
31
31
|
entityname: entity.name,
|
|
32
|
+
|
|
33
|
+
// Class receiver token decoupled from the EntityName data-type token in
|
|
34
|
+
// the Entity<Op>Op.fragment.lua files (receiver -> cls).
|
|
35
|
+
EntyClass: cls,
|
|
36
|
+
|
|
32
37
|
'#Feature-Hook': ({ name, indent }: any) =>
|
|
33
38
|
Content({ indent }, `
|
|
34
39
|
u.feature_hook(ctx, "${name}")
|
|
@@ -3,9 +3,15 @@ import * as Path from 'node:path'
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
cmp, each, camelify, names,
|
|
6
|
-
File, Content, Folder, Fragment, Line, FeatureHook, Slot
|
|
6
|
+
File, Content, Folder, Fragment, Line, FeatureHook, Slot,
|
|
7
|
+
entityClassName,
|
|
7
8
|
} from '@voxgig/sdkgen'
|
|
8
9
|
|
|
10
|
+
import {
|
|
11
|
+
KIT,
|
|
12
|
+
getModelPath
|
|
13
|
+
} from '@voxgig/apidef'
|
|
14
|
+
|
|
9
15
|
import { EntityOperation } from './EntityOperation_lua'
|
|
10
16
|
|
|
11
17
|
|
|
@@ -13,6 +19,14 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
13
19
|
const { model, stdrep } = props.ctx$
|
|
14
20
|
const { target, entity } = props
|
|
15
21
|
|
|
22
|
+
// Collision-free entity CLASS name (see entityClassName): normally
|
|
23
|
+
// `<Name>Entity`, disambiguated when it would clash with another entity's
|
|
24
|
+
// data-type name. The class is a module-local table (snake-cased file name),
|
|
25
|
+
// so this is used only for the local identifier — kept uniform with the other
|
|
26
|
+
// languages.
|
|
27
|
+
const entityColl = getModelPath(model, `main.${KIT}.entity`)
|
|
28
|
+
const cls = entityClassName(entity, entityColl)
|
|
29
|
+
|
|
16
30
|
const entrep = {
|
|
17
31
|
...stdrep,
|
|
18
32
|
}
|
|
@@ -33,7 +47,7 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
33
47
|
.reduce((a: any, opname: string) =>
|
|
34
48
|
(a['-- #' + camelify(opname) + 'Op'] =
|
|
35
49
|
!opnames.includes(opname) ? '' : ({ indent }: any) => {
|
|
36
|
-
EntityOperation({ ff, opname, indent, entity, entrep })
|
|
50
|
+
EntityOperation({ ff, opname, indent, entity, entrep, cls })
|
|
37
51
|
}, a), {}))
|
|
38
52
|
|
|
39
53
|
Fragment({
|
|
@@ -44,6 +58,10 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
44
58
|
EntityName: entity.Name,
|
|
45
59
|
entityname: entity.name,
|
|
46
60
|
|
|
61
|
+
// Class token decoupled from the EntityName data-type token in
|
|
62
|
+
// Entity.fragment.lua so the class can be renamed independently.
|
|
63
|
+
EntyClass: cls,
|
|
64
|
+
|
|
47
65
|
'#Entity-Hook': ({ name, indent }: any) =>
|
|
48
66
|
Content({ indent }, `utility.feature_hook(ctx, "${name}")`),
|
|
49
67
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, each, Content, canonToType, canonKey, entityIdField, opRequestShape } from '@voxgig/sdkgen'
|
|
2
|
+
import { cmp, each, Content, canonToType, canonKey, entityIdField, opRequestShape, safeVarName } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -61,6 +61,9 @@ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
|
|
|
61
61
|
const fields = entity.fields || []
|
|
62
62
|
// Model-driven id key: null when this entity has no id-like field.
|
|
63
63
|
const idF = entityIdField(entity)
|
|
64
|
+
// Sanitise the local variable name — an entity whose lowercased name is a
|
|
65
|
+
// Lua keyword (e.g. `end`) would otherwise emit uncompilable code.
|
|
66
|
+
const eVar = safeVarName(entity.name, 'lua')
|
|
64
67
|
|
|
65
68
|
Content(`
|
|
66
69
|
### ${entity.Name}
|
|
@@ -73,7 +76,7 @@ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
|
|
|
73
76
|
`)
|
|
74
77
|
}
|
|
75
78
|
|
|
76
|
-
Content(`Create an instance: \`local ${
|
|
79
|
+
Content(`Create an instance: \`local ${eVar} = client:${entity.Name}(nil)\`
|
|
77
80
|
|
|
78
81
|
`)
|
|
79
82
|
|
|
@@ -128,7 +131,7 @@ const ReadmeEntity = cmp(function ReadmeEntity(props: any) {
|
|
|
128
131
|
Content(`#### Example: Load
|
|
129
132
|
|
|
130
133
|
\`\`\`lua
|
|
131
|
-
local ${
|
|
134
|
+
local ${eVar}, err = client:${entity.Name}():load(${loadArg})
|
|
132
135
|
\`\`\`
|
|
133
136
|
|
|
134
137
|
`)
|
|
@@ -138,7 +141,7 @@ local ${entity.name}, err = client:${entity.Name}():load(${loadArg})
|
|
|
138
141
|
Content(`#### Example: List
|
|
139
142
|
|
|
140
143
|
\`\`\`lua
|
|
141
|
-
local ${
|
|
144
|
+
local ${eVar}s, err = client:${entity.Name}():list()
|
|
142
145
|
\`\`\`
|
|
143
146
|
|
|
144
147
|
`)
|
|
@@ -155,7 +158,7 @@ local ${entity.name}s, err = client:${entity.Name}():list()
|
|
|
155
158
|
Content(`#### Example: Create
|
|
156
159
|
|
|
157
160
|
\`\`\`lua
|
|
158
|
-
local ${
|
|
161
|
+
local ${eVar}, err = client:${entity.Name}():create({
|
|
159
162
|
`)
|
|
160
163
|
createItems.map((it: any) => {
|
|
161
164
|
Content(` ${luaKey(it.name)} = ${luaLit(it.type, 'example_' + it.name)}, -- ${canonToType(it.type, target.name)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, Content, isAuthActive, envName, canonKey, entityIdField,
|
|
2
|
+
import { cmp, Content, isAuthActive, envName, canonKey, entityIdField, pickExampleEntity, opRequestShape } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -26,13 +26,13 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
|
|
|
26
26
|
const { target, ctx$: { model } } = props
|
|
27
27
|
|
|
28
28
|
const entity = getModelPath(model, `main.${KIT}.entity`)
|
|
29
|
-
|
|
29
|
+
// Pick an entity with a real op (prefer a read op) — never fabricate a
|
|
30
|
+
// `load` on an op-less entity like Cloudsmith's `Abort`. primaryOp is null
|
|
31
|
+
// only when NO entity exposes any op (a direct()-only SDK).
|
|
32
|
+
const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
|
|
30
33
|
const eName = exampleEntity ? nom(exampleEntity, 'Name') : 'Entity'
|
|
31
34
|
// Model-driven id key: null when the entity has no id-like field.
|
|
32
35
|
const idF = exampleEntity ? entityIdField(exampleEntity) : null
|
|
33
|
-
// Drive the test-mode example off the entity's PRIMARY op — never a hardcoded
|
|
34
|
-
// `load` a create-only entity lacks.
|
|
35
|
-
const primaryOp = exampleEntity ? (entityPrimaryOp(exampleEntity) || 'load') : 'load'
|
|
36
36
|
const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
|
|
37
37
|
let testCallArg = ''
|
|
38
38
|
if (exampleEntity && isMatchOp) {
|
|
@@ -45,6 +45,14 @@ const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
|
|
|
45
45
|
testCallArg = `{ ${chosen.map((it: any) => `${luaKey(it.name)} = ${luaLit(it.type)}`).join(', ')} }`
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// The op-driven test-mode line, shown only when the SDK has an entity op.
|
|
49
|
+
// A direct()-only SDK (no ops anywhere) shows a direct() call instead.
|
|
50
|
+
const testModeExample = primaryOp
|
|
51
|
+
? `local result, err = client:${eName}():${primaryOp}(${testCallArg})
|
|
52
|
+
-- result is the returned data; err is set on failure`
|
|
53
|
+
: `local result, err = client:direct({ path = "/api/resource", method = "GET" })
|
|
54
|
+
-- result is the returned data; err is set on failure`
|
|
55
|
+
|
|
48
56
|
const apikeyEnvLine = isAuthActive(model)
|
|
49
57
|
? `\n${envName(model)}_APIKEY=<your-key>`
|
|
50
58
|
: ''
|
|
@@ -89,8 +97,7 @@ Create a mock client for unit testing — no server required:
|
|
|
89
97
|
\`\`\`lua
|
|
90
98
|
local client = sdk.test()
|
|
91
99
|
|
|
92
|
-
|
|
93
|
-
-- result is the returned data; err is set on failure
|
|
100
|
+
${testModeExample}
|
|
94
101
|
\`\`\`
|
|
95
102
|
|
|
96
103
|
### Use a custom fetch function
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, each, Content, isAuthActive, envName, canonKey, opRequestShape, entityIdField, entityDataIdField, entityOps } from '@voxgig/sdkgen'
|
|
2
|
+
import { cmp, each, Content, isAuthActive, envName, canonKey, opRequestShape, entityIdField, entityDataIdField, entityOps, safeVarName } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -43,6 +43,9 @@ local client = ${ctor}
|
|
|
43
43
|
if (exampleEntity) {
|
|
44
44
|
const eName = nom(exampleEntity, 'Name')
|
|
45
45
|
const article = /^[aeiou]/i.test(eName) ? "an" : "a"
|
|
46
|
+
// Sanitise the local variable name — an entity whose lowercased name is a
|
|
47
|
+
// Lua keyword (e.g. `end`) would otherwise emit uncompilable code.
|
|
48
|
+
const eVar = safeVarName(eName.toLowerCase(), 'lua')
|
|
46
49
|
const opnames = entityOps(exampleEntity)
|
|
47
50
|
// Model-driven id key: `idF` is the MATCH key (null when none). `dataIdF`
|
|
48
51
|
// is the id on the RETURNED record's data type — an entity can key its match
|
|
@@ -91,10 +94,10 @@ Entity operations return \`(value, err)\`. For \`list\`, \`value\` is the
|
|
|
91
94
|
array of records itself — iterate it directly (there is no wrapper).
|
|
92
95
|
|
|
93
96
|
\`\`\`lua
|
|
94
|
-
local ${
|
|
97
|
+
local ${eVar}s, err = client:${eName}():list()
|
|
95
98
|
if err then error(err) end
|
|
96
99
|
|
|
97
|
-
for _, item in ipairs(${
|
|
100
|
+
for _, item in ipairs(${eVar}s) do
|
|
98
101
|
${printLine}
|
|
99
102
|
end
|
|
100
103
|
\`\`\`
|
|
@@ -105,7 +108,7 @@ end
|
|
|
105
108
|
if (nestedEntity) {
|
|
106
109
|
const neName = nom(nestedEntity, 'Name')
|
|
107
110
|
const neArticle = /^[aeiou]/i.test(neName) ? "an" : "a"
|
|
108
|
-
const neVar = neName.toLowerCase()
|
|
111
|
+
const neVar = safeVarName(neName.toLowerCase(), 'lua')
|
|
109
112
|
|
|
110
113
|
// Model-driven match: every REQUIRED load-match key — the same shape
|
|
111
114
|
// the runtime resolves path params from, so the example always works.
|
|
@@ -151,9 +154,9 @@ print(${neVar})
|
|
|
151
154
|
Content(`### 3. Load ${article} ${eName.toLowerCase()}
|
|
152
155
|
|
|
153
156
|
\`\`\`lua
|
|
154
|
-
local ${
|
|
157
|
+
local ${eVar}, err = client:${eName}():load(${loadArg})
|
|
155
158
|
if err then error(err) end
|
|
156
|
-
print(${
|
|
159
|
+
print(${eVar})
|
|
157
160
|
\`\`\`
|
|
158
161
|
|
|
159
162
|
`)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, each, Content, canonToType, canonKey, File, isAuthActive, entityIdField, opRequestShape } from '@voxgig/sdkgen'
|
|
2
|
+
import { cmp, each, Content, canonToType, canonKey, File, isAuthActive, entityIdField, opRequestShape, safeVarName } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -174,6 +174,9 @@ same parameters as \`direct()\`.
|
|
|
174
174
|
// Model-driven id key: null when this entity has no id-like field, in
|
|
175
175
|
// which case load/remove match on no argument and update omits the id.
|
|
176
176
|
const idF = entityIdField(ent)
|
|
177
|
+
// Sanitise the local variable name — an entity whose lowercased name is
|
|
178
|
+
// a Lua keyword (e.g. `end`) would otherwise emit uncompilable code.
|
|
179
|
+
const eVar = safeVarName(ent.name, 'lua')
|
|
177
180
|
|
|
178
181
|
Content(`
|
|
179
182
|
---
|
|
@@ -189,7 +192,7 @@ same parameters as \`direct()\`.
|
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
Content(`\`\`\`lua
|
|
192
|
-
local ${
|
|
195
|
+
local ${eVar} = client:${ent.Name}(nil)
|
|
193
196
|
\`\`\`
|
|
194
197
|
|
|
195
198
|
`)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, Content, isAuthActive, envName, canonKey, entityIdField, opRequestShape } from '@voxgig/sdkgen'
|
|
2
|
+
import { cmp, Content, isAuthActive, envName, canonKey, entityIdField, opRequestShape, safeVarName } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -46,6 +46,9 @@ local client = ${ctor}
|
|
|
46
46
|
|
|
47
47
|
if (exampleEntity) {
|
|
48
48
|
const eName = nom(exampleEntity, 'Name')
|
|
49
|
+
// Sanitise the local variable name — an entity whose lowercased name is a
|
|
50
|
+
// Lua keyword (e.g. `end`) would otherwise emit uncompilable code.
|
|
51
|
+
const eVar = safeVarName(eName.toLowerCase(), 'lua')
|
|
49
52
|
const opnames = Object.keys(exampleEntity.op || {})
|
|
50
53
|
// Model-driven id key: null when the entity has no id-like field, in which
|
|
51
54
|
// case the load example takes no match argument.
|
|
@@ -55,8 +58,8 @@ local client = ${ctor}
|
|
|
55
58
|
|
|
56
59
|
if (opnames.includes('list')) {
|
|
57
60
|
Content(`-- List all ${eName.toLowerCase()}s
|
|
58
|
-
local ${
|
|
59
|
-
print(${
|
|
61
|
+
local ${eVar}s, err = client:${eName}():list()
|
|
62
|
+
print(${eVar}s)
|
|
60
63
|
`)
|
|
61
64
|
hasCall = true
|
|
62
65
|
}
|
|
@@ -76,8 +79,8 @@ print(${eName.toLowerCase()}s)
|
|
|
76
79
|
: ''
|
|
77
80
|
Content(`
|
|
78
81
|
-- Load a specific ${eName.toLowerCase()}
|
|
79
|
-
local ${
|
|
80
|
-
print(${
|
|
82
|
+
local ${eVar}, err = client:${eName}():load(${loadArg})
|
|
83
|
+
print(${eVar})
|
|
81
84
|
`)
|
|
82
85
|
hasCall = true
|
|
83
86
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { cmp, Content, canonKey, entityIdField,
|
|
2
|
+
import { cmp, Content, canonKey, entityIdField, pickExampleEntity, opRequestShape } from '@voxgig/sdkgen'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
KIT,
|
|
@@ -27,18 +27,17 @@ const ReadmeTopTest = cmp(function ReadmeTopTest(props: any) {
|
|
|
27
27
|
|
|
28
28
|
const entity = getModelPath(model, `main.${KIT}.entity`)
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
// Pick an entity with a real op (prefer a read op) — never fabricate a
|
|
31
|
+
// `load` on an op-less entity like Cloudsmith's `Abort`.
|
|
32
|
+
const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
|
|
31
33
|
|
|
32
34
|
Content(`\`\`\`lua
|
|
33
35
|
local client = sdk.test()
|
|
34
36
|
`)
|
|
35
37
|
|
|
36
|
-
if (exampleEntity) {
|
|
38
|
+
if (exampleEntity && primaryOp) {
|
|
37
39
|
const eName = nom(exampleEntity, 'Name')
|
|
38
|
-
// Drive the test-mode example off the entity's PRIMARY op (never a hardcoded
|
|
39
|
-
// `load` a create-only entity lacks).
|
|
40
40
|
const idF = entityIdField(exampleEntity)
|
|
41
|
-
const primaryOp = entityPrimaryOp(exampleEntity) || 'load'
|
|
42
41
|
const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
|
|
43
42
|
let arg = ''
|
|
44
43
|
if (isMatchOp) {
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
local vs = require("utility.struct.struct")
|
|
4
4
|
local helpers = require("core.helpers")
|
|
5
5
|
|
|
6
|
-
local
|
|
7
|
-
|
|
6
|
+
local EntyClass = {}
|
|
7
|
+
EntyClass.__index = EntyClass
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
function
|
|
10
|
+
function EntyClass.new(client, entopts)
|
|
11
11
|
entopts = entopts or {}
|
|
12
12
|
if entopts["active"] == nil then
|
|
13
13
|
entopts["active"] = true
|
|
@@ -17,7 +17,7 @@ function EntityNameEntity.new(client, entopts)
|
|
|
17
17
|
entopts["active"] = true
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
local self = setmetatable({},
|
|
20
|
+
local self = setmetatable({}, EntyClass)
|
|
21
21
|
self._name = "entityname"
|
|
22
22
|
self._client = client
|
|
23
23
|
self._utility = client:get_utility()
|
|
@@ -36,21 +36,21 @@ function EntityNameEntity.new(client, entopts)
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
function
|
|
39
|
+
function EntyClass:get_name()
|
|
40
40
|
return self._name
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
function
|
|
44
|
+
function EntyClass:make()
|
|
45
45
|
local opts = {}
|
|
46
46
|
for k, v in pairs(self._entopts) do
|
|
47
47
|
opts[k] = v
|
|
48
48
|
end
|
|
49
|
-
return
|
|
49
|
+
return EntyClass.new(self._client, opts)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
function
|
|
53
|
+
function EntyClass:data_set(args)
|
|
54
54
|
if args ~= nil then
|
|
55
55
|
self._data = helpers.to_map(vs.clone(args)) or {}
|
|
56
56
|
self._utility.feature_hook(self._entctx, "SetData")
|
|
@@ -58,13 +58,13 @@ function EntityNameEntity:data_set(args)
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
function
|
|
61
|
+
function EntyClass:data_get()
|
|
62
62
|
self._utility.feature_hook(self._entctx, "GetData")
|
|
63
63
|
return vs.clone(self._data)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
function
|
|
67
|
+
function EntyClass:match_set(args)
|
|
68
68
|
if args ~= nil then
|
|
69
69
|
self._match = helpers.to_map(vs.clone(args)) or {}
|
|
70
70
|
self._utility.feature_hook(self._entctx, "SetMatch")
|
|
@@ -72,7 +72,7 @@ function EntityNameEntity:match_set(args)
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
function
|
|
75
|
+
function EntyClass:match_get()
|
|
76
76
|
self._utility.feature_hook(self._entctx, "GetMatch")
|
|
77
77
|
return vs.clone(self._match)
|
|
78
78
|
end
|
|
@@ -89,7 +89,7 @@ end
|
|
|
89
89
|
-- #RemoveOp
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
function
|
|
92
|
+
function EntyClass:_run_op(ctx, post_done)
|
|
93
93
|
local utility = self._utility
|
|
94
94
|
|
|
95
95
|
-- #PrePoint-Hook
|
|
@@ -144,4 +144,4 @@ function EntityNameEntity:_run_op(ctx, post_done)
|
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
return
|
|
147
|
+
return EntyClass
|