@voxgig/apidef 2.4.0 → 3.0.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/dist/apidef.d.ts +5 -1
- package/dist/apidef.js +197 -112
- package/dist/apidef.js.map +1 -1
- package/dist/builder/entity/entity.d.ts +3 -0
- package/dist/builder/entity/{apiEntity.js → entity.js} +12 -9
- package/dist/builder/entity/entity.js.map +1 -0
- package/dist/builder/entity/info.d.ts +3 -0
- package/dist/builder/entity/info.js +22 -0
- package/dist/builder/entity/info.js.map +1 -0
- package/dist/builder/entity.js +7 -21
- package/dist/builder/entity.js.map +1 -1
- package/dist/builder/flow/flowHeuristic01.js +21 -11
- package/dist/builder/flow/flowHeuristic01.js.map +1 -1
- package/dist/builder/flow.d.ts +2 -1
- package/dist/builder/flow.js +29 -4
- package/dist/builder/flow.js.map +1 -1
- package/dist/def.d.ts +62 -0
- package/dist/def.js +4 -0
- package/dist/def.js.map +1 -0
- package/dist/desc.d.ts +89 -0
- package/dist/desc.js +4 -0
- package/dist/desc.js.map +1 -0
- package/dist/guide/guide.d.ts +2 -1
- package/dist/guide/guide.js +161 -30
- package/dist/guide/guide.js.map +1 -1
- package/dist/guide/heuristic01.d.ts +2 -1
- package/dist/guide/heuristic01.js +1120 -234
- package/dist/guide/heuristic01.js.map +1 -1
- package/dist/model.d.ts +55 -0
- package/dist/model.js +4 -0
- package/dist/model.js.map +1 -0
- package/dist/parse.d.ts +1 -2
- package/dist/parse.js +8 -47
- package/dist/parse.js.map +1 -1
- package/dist/transform/args.d.ts +3 -0
- package/dist/transform/args.js +58 -0
- package/dist/transform/args.js.map +1 -0
- package/dist/transform/clean.js +27 -3
- package/dist/transform/clean.js.map +1 -1
- package/dist/transform/entity.d.ts +11 -3
- package/dist/transform/entity.js +57 -41
- package/dist/transform/entity.js.map +1 -1
- package/dist/transform/field.d.ts +3 -3
- package/dist/transform/field.js +90 -65
- package/dist/transform/field.js.map +1 -1
- package/dist/transform/operation.d.ts +1 -1
- package/dist/transform/operation.js +94 -296
- package/dist/transform/operation.js.map +1 -1
- package/dist/transform/select.d.ts +3 -0
- package/dist/transform/select.js +44 -0
- package/dist/transform/select.js.map +1 -0
- package/dist/transform/top.d.ts +9 -0
- package/dist/transform/top.js +11 -2
- package/dist/transform/top.js.map +1 -1
- package/dist/transform.js +4 -0
- package/dist/transform.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +112 -19
- package/dist/types.js +4 -2
- package/dist/types.js.map +1 -1
- package/dist/utility.d.ts +30 -2
- package/dist/utility.js +381 -6
- package/dist/utility.js.map +1 -1
- package/model/apidef.jsonic +75 -1
- package/model/guide.jsonic +14 -44
- package/package.json +19 -14
- package/src/apidef.ts +264 -121
- package/src/builder/entity/{apiEntity.ts → entity.ts} +18 -11
- package/src/builder/entity/info.ts +53 -0
- package/src/builder/entity.ts +9 -35
- package/src/builder/flow/flowHeuristic01.ts +46 -12
- package/src/builder/flow.ts +39 -5
- package/src/def.ts +91 -0
- package/src/desc.ts +143 -0
- package/src/guide/guide.ts +207 -134
- package/src/guide/heuristic01.ts +1651 -272
- package/src/model.ts +98 -0
- package/src/parse.ts +5 -61
- package/src/schematron.ts.off +317 -0
- package/src/transform/args.ts +102 -0
- package/src/transform/clean.ts +43 -8
- package/src/transform/entity.ts +100 -51
- package/src/transform/field.ts +150 -71
- package/src/transform/operation.ts +118 -414
- package/src/transform/select.ts +90 -0
- package/src/transform/top.ts +76 -3
- package/src/transform.ts +4 -0
- package/src/types.ts +185 -5
- package/src/utility.ts +481 -9
- package/dist/builder/entity/apiEntity.d.ts +0 -3
- package/dist/builder/entity/apiEntity.js.map +0 -1
- package/dist/builder/entity/def.d.ts +0 -3
- package/dist/builder/entity/def.js +0 -19
- package/dist/builder/entity/def.js.map +0 -1
- package/src/builder/entity/def.ts +0 -44
- package/src/guide.ts.off +0 -136
package/src/transform/entity.ts
CHANGED
|
@@ -1,74 +1,57 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import { each
|
|
3
|
+
import { each } from 'jostraca'
|
|
4
4
|
|
|
5
5
|
import type { TransformResult, Transform } from '../transform'
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { formatJSONIC } from '../utility'
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { KIT } from '../types'
|
|
10
10
|
|
|
11
|
+
import type { KitModel } from '../types'
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
import type {
|
|
14
|
+
GuideEntity,
|
|
15
|
+
} from './top'
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
GuidePath,
|
|
19
|
+
PathDesc,
|
|
20
|
+
} from '../desc'
|
|
21
|
+
|
|
22
|
+
import type {
|
|
23
|
+
ModelEntity,
|
|
24
|
+
} from '../model'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
const entityTransform = async function(
|
|
13
30
|
ctx: any,
|
|
14
31
|
): Promise<TransformResult> {
|
|
15
|
-
const { apimodel,
|
|
32
|
+
const { apimodel, guide } = ctx
|
|
33
|
+
const kit: KitModel = apimodel.main[KIT]
|
|
16
34
|
|
|
17
35
|
let msg = ''
|
|
18
36
|
|
|
19
|
-
each(guide.entity, (guideEntity:
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
each(guide.entity, (guideEntity: GuideEntity, entname: string) => {
|
|
38
|
+
ctx.log.debug({ point: 'guide-entity', note: entname })
|
|
39
|
+
|
|
40
|
+
const paths$ = resolvePathList(guideEntity, ctx.def)
|
|
41
|
+
const relations = buildRelations(guideEntity, paths$)
|
|
22
42
|
|
|
23
|
-
const
|
|
43
|
+
const modelent: ModelEntity = {
|
|
44
|
+
name: entname,
|
|
24
45
|
op: {},
|
|
25
|
-
|
|
26
|
-
cmd: {},
|
|
46
|
+
fields: [],
|
|
27
47
|
id: {
|
|
28
48
|
name: 'id',
|
|
29
49
|
field: 'id',
|
|
30
50
|
},
|
|
31
|
-
|
|
51
|
+
relations,
|
|
32
52
|
}
|
|
33
53
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
let ancestors: string[] = []
|
|
37
|
-
let ancestorsDone = false
|
|
38
|
-
|
|
39
|
-
each(guideEntity.path, (guidePath: any, pathStr: string) => {
|
|
40
|
-
const path = guidePath.key$
|
|
41
|
-
const pathdef = def.paths[path]
|
|
42
|
-
|
|
43
|
-
if (null == pathdef) {
|
|
44
|
-
throw new Error('path not found in OpenAPI: ' + path +
|
|
45
|
-
' (entity: ' + guideEntity.name + ')')
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// TODO: is this needed?
|
|
49
|
-
guidePath.parts$ = path.split('/')
|
|
50
|
-
guidePath.params$ = guidePath.parts$
|
|
51
|
-
.filter((p: string) => p.startsWith('{'))
|
|
52
|
-
.map((p: string) => p.substring(1, p.length - 1))
|
|
53
|
-
|
|
54
|
-
if (!ancestorsDone) {
|
|
55
|
-
// Find all path sections matching /foo/{..param..} and build ancestors array
|
|
56
|
-
const paramRegex = /\/([a-zA-Z0-9_-]+)\/\{[a-zA-Z0-9_-]+\}/g
|
|
57
|
-
let m
|
|
58
|
-
while ((m = paramRegex.exec(pathStr)) !== null) {
|
|
59
|
-
// Skip if this is the last section (the entity itself)
|
|
60
|
-
const remainingPath = pathStr.substring(m.index + m[0].length)
|
|
61
|
-
if (remainingPath.length > 0) {
|
|
62
|
-
const ancestorName = depluralize(snakify(m[1]))
|
|
63
|
-
ancestors.push(ancestorName)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
ancestorsDone = true
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
entityModel.ancestors = ancestors
|
|
54
|
+
kit.entity[entname] = modelent
|
|
72
55
|
|
|
73
56
|
msg += guideEntity.name + ' '
|
|
74
57
|
})
|
|
@@ -77,6 +60,72 @@ const entityTransform: Transform = async function(
|
|
|
77
60
|
}
|
|
78
61
|
|
|
79
62
|
|
|
63
|
+
|
|
64
|
+
function resolvePathList(guideEntity: GuideEntity, def: { paths: Record<string, any> }) {
|
|
65
|
+
const paths$: PathDesc[] = []
|
|
66
|
+
|
|
67
|
+
each(guideEntity.path, (guidePath: GuidePath, orig: string) => {
|
|
68
|
+
const parts = orig.split('/').filter(p => '' != p)
|
|
69
|
+
const rename = guidePath.rename ?? {}
|
|
70
|
+
|
|
71
|
+
each(rename.param, (param: any) => {
|
|
72
|
+
const pI = parts.indexOf('{' + param.key$ + '}')
|
|
73
|
+
parts[pI] = '{' + param.val$ + '}'
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const pathdesc: PathDesc = {
|
|
77
|
+
orig,
|
|
78
|
+
parts,
|
|
79
|
+
rename,
|
|
80
|
+
method: '', // operation collectOps will copy and assign per op
|
|
81
|
+
op: guidePath.op,
|
|
82
|
+
def: def.paths[orig],
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
paths$.push(pathdesc)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
guideEntity.paths$ = paths$
|
|
89
|
+
|
|
90
|
+
return paths$
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
function buildRelations(guideEntity: any, paths$: PathDesc[]) {
|
|
96
|
+
let ancestors: any[] = paths$
|
|
97
|
+
.map(pli => pli.parts
|
|
98
|
+
.map((p, i) =>
|
|
99
|
+
(pli.parts[i + 1]?.[0] === '{' && pli.parts[i + 1] !== '{id}') ? p : null)
|
|
100
|
+
.filter(p => null != p))
|
|
101
|
+
.filter(n => 0 < n.length)
|
|
102
|
+
.sort((a, b) => a.length - b.length)
|
|
103
|
+
|
|
104
|
+
// remove suffixes
|
|
105
|
+
ancestors = ancestors
|
|
106
|
+
.reduce((a, n, j) =>
|
|
107
|
+
((0 < (ancestors.slice(j + 1).filter(p => suffix(p, n))).length
|
|
108
|
+
? null : a.push(n)), a), [])
|
|
109
|
+
|
|
110
|
+
const relations = {
|
|
111
|
+
ancestors
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
guideEntity.relations$ = relations
|
|
115
|
+
|
|
116
|
+
return relations
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
// True if array c is a suffix of array p,
|
|
121
|
+
function suffix(p: string[], c: string[]): boolean {
|
|
122
|
+
return c.reduce((b, _, i) => (b && c[c.length - 1 - i] === p[p.length - 1 - i]), true)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
80
127
|
export {
|
|
81
|
-
|
|
128
|
+
resolvePathList,
|
|
129
|
+
buildRelations,
|
|
130
|
+
entityTransform,
|
|
82
131
|
}
|
package/src/transform/field.ts
CHANGED
|
@@ -2,120 +2,199 @@
|
|
|
2
2
|
|
|
3
3
|
import { each, getx } from 'jostraca'
|
|
4
4
|
|
|
5
|
+
import { getelem } from '@voxgig/struct'
|
|
6
|
+
|
|
5
7
|
import type { TransformResult, Transform } from '../transform'
|
|
6
8
|
|
|
7
9
|
import { fixName } from '../transform'
|
|
8
10
|
|
|
11
|
+
import { formatJSONIC, validator, canonize } from '../utility'
|
|
12
|
+
|
|
13
|
+
import { KIT } from '../types'
|
|
14
|
+
|
|
15
|
+
import type { KitModel } from '../types'
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
PathDef,
|
|
19
|
+
ParameterDef,
|
|
20
|
+
MethodDef,
|
|
21
|
+
SchemaDef,
|
|
22
|
+
} from '../def'
|
|
23
|
+
|
|
24
|
+
import type {
|
|
25
|
+
GuideEntity,
|
|
26
|
+
} from './top'
|
|
9
27
|
|
|
28
|
+
import type {
|
|
29
|
+
GuideOp,
|
|
30
|
+
PathDesc,
|
|
31
|
+
} from '../desc'
|
|
10
32
|
|
|
11
|
-
|
|
33
|
+
import type {
|
|
34
|
+
OpName,
|
|
35
|
+
ModelOpMap,
|
|
36
|
+
ModelOp,
|
|
37
|
+
ModelEntity,
|
|
38
|
+
ModelAlt,
|
|
39
|
+
ModelArg,
|
|
40
|
+
ModelField,
|
|
41
|
+
} from '../model'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
const fieldTransform = async function(
|
|
12
46
|
ctx: any,
|
|
13
47
|
): Promise<TransformResult> {
|
|
14
|
-
const { apimodel,
|
|
48
|
+
const { apimodel, def } = ctx
|
|
49
|
+
const kit: KitModel = apimodel.main[KIT]
|
|
50
|
+
|
|
51
|
+
let msg = 'field '
|
|
52
|
+
|
|
53
|
+
const opFieldPrecedence: OpName[] = ['load', 'create', 'update', 'patch', 'list']
|
|
15
54
|
|
|
16
|
-
|
|
55
|
+
each(kit.entity, (ment: ModelEntity, entname: string) => {
|
|
56
|
+
const fielddefs: SchemaDef[] = []
|
|
17
57
|
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const entityModel = apimodel.main.api.entity[entityName]
|
|
58
|
+
const fields = ment.fields
|
|
59
|
+
const seen: any = {}
|
|
21
60
|
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
61
|
+
for (let opname of opFieldPrecedence) {
|
|
62
|
+
const mop = ment.op[opname]
|
|
63
|
+
if (mop) {
|
|
64
|
+
const malts = mop.alts
|
|
26
65
|
|
|
27
|
-
|
|
28
|
-
|
|
66
|
+
for (let malt of malts) {
|
|
67
|
+
const opfields = resolveOpFields(ment, mop, malt, def)
|
|
29
68
|
|
|
30
|
-
|
|
31
|
-
|
|
69
|
+
for (let opfield of opfields) {
|
|
70
|
+
if (!seen[opfield.name]) {
|
|
71
|
+
fields.push(opfield)
|
|
72
|
+
seen[opfield.name] = opfield
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
mergeField(ment, mop, malt, def, seen[opfield.name], opfield)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
32
78
|
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
33
81
|
|
|
34
|
-
|
|
82
|
+
fields.sort((a: ModelField, b: ModelField) => {
|
|
83
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
|
35
84
|
})
|
|
36
85
|
|
|
37
|
-
msg +=
|
|
86
|
+
msg += ment.name + ' '
|
|
38
87
|
})
|
|
39
88
|
|
|
40
89
|
return { ok: true, msg }
|
|
41
90
|
}
|
|
42
91
|
|
|
43
92
|
|
|
44
|
-
function fieldbuild(
|
|
45
|
-
entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any
|
|
46
|
-
) {
|
|
47
|
-
let fieldCount = 0
|
|
48
|
-
let fieldSets = getx(pathdef.get, 'responses 200 content "application/json" schema')
|
|
49
93
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
94
|
+
function resolveOpFields(
|
|
95
|
+
ment: ModelEntity,
|
|
96
|
+
mop: ModelOp,
|
|
97
|
+
malt: ModelAlt,
|
|
98
|
+
def: any
|
|
99
|
+
): ModelField[] {
|
|
100
|
+
const mfields: ModelField[] = []
|
|
101
|
+
const fielddefs = findFieldDefs(ment, mop, malt, def)
|
|
102
|
+
|
|
103
|
+
for (let fielddef of fielddefs) {
|
|
104
|
+
const fieldname = (fielddef as any).key$ as string
|
|
105
|
+
const mfield: ModelField = {
|
|
106
|
+
name: canonize(fieldname),
|
|
107
|
+
type: validator(fielddef.type),
|
|
108
|
+
req: !!fielddef.required,
|
|
109
|
+
op: {},
|
|
56
110
|
}
|
|
111
|
+
mfields.push(mfield)
|
|
57
112
|
}
|
|
58
113
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const field =
|
|
62
|
-
(entityModel.field[property.key$] = entityModel.field[property.key$] || {})
|
|
114
|
+
return mfields
|
|
115
|
+
}
|
|
63
116
|
|
|
64
|
-
field.name = property.key$
|
|
65
|
-
fixName(field, field.name)
|
|
66
117
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
118
|
+
function findFieldDefs(
|
|
119
|
+
ment: ModelEntity,
|
|
120
|
+
mop: ModelOp,
|
|
121
|
+
malt: ModelAlt,
|
|
122
|
+
def: any
|
|
123
|
+
): SchemaDef[] {
|
|
124
|
+
const fielddefs: SchemaDef[] = []
|
|
125
|
+
const pathdef = def.paths[malt.orig]
|
|
70
126
|
|
|
71
|
-
|
|
127
|
+
const method = malt.method.toLowerCase()
|
|
128
|
+
const opdef: any = pathdef[method]
|
|
72
129
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
130
|
+
if (opdef) {
|
|
131
|
+
const responses = opdef.responses
|
|
132
|
+
const requestBody = opdef.requestBody
|
|
76
133
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
|
|
134
|
+
let fieldSets
|
|
135
|
+
|
|
136
|
+
if (responses) {
|
|
137
|
+
fieldSets = getx(responses, '200 content "application/json" schema') ??
|
|
138
|
+
getx(responses, '200 schema')
|
|
139
|
+
if ('get' === method && 'list' == mop.name) {
|
|
140
|
+
fieldSets = getx(responses, '201 content "application/json" schema items') ??
|
|
141
|
+
getx(responses, '201 schema items')
|
|
142
|
+
}
|
|
143
|
+
else if ('put' === method && null == fieldSets) {
|
|
144
|
+
fieldSets = getx(responses, '201 content "application/json" schema') ??
|
|
145
|
+
getx(responses, '201 schema')
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (requestBody) {
|
|
150
|
+
fieldSets = [
|
|
151
|
+
fieldSets,
|
|
152
|
+
getx(requestBody, 'content "application/json" schema') ??
|
|
153
|
+
getx(requestBody, 'schema')
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if (fieldSets) {
|
|
159
|
+
if (Array.isArray(fieldSets.allOf)) {
|
|
160
|
+
fieldSets = fieldSets.allOf
|
|
161
|
+
}
|
|
162
|
+
else if (fieldSets.properties) {
|
|
163
|
+
fieldSets = [fieldSets]
|
|
84
164
|
}
|
|
85
165
|
}
|
|
166
|
+
|
|
167
|
+
each(fieldSets, (fieldSet: any) => {
|
|
168
|
+
each(fieldSet?.properties, (property: any) => {
|
|
169
|
+
fielddefs.push(property)
|
|
170
|
+
})
|
|
171
|
+
})
|
|
86
172
|
}
|
|
87
173
|
|
|
88
|
-
return
|
|
174
|
+
return fielddefs
|
|
89
175
|
}
|
|
90
176
|
|
|
91
177
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
178
|
+
function mergeField(
|
|
179
|
+
ment: ModelEntity,
|
|
180
|
+
mop: ModelOp,
|
|
181
|
+
malt: ModelAlt,
|
|
182
|
+
def: any,
|
|
183
|
+
exisingField: ModelField,
|
|
184
|
+
newField: ModelField
|
|
185
|
+
) {
|
|
96
186
|
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
(property.type.filter((t: string) => 'null' != t).sort()[0]) ||
|
|
103
|
-
property.type[0] ||
|
|
104
|
-
'string'
|
|
105
|
-
field.typelist = property.type
|
|
106
|
-
}
|
|
107
|
-
else if ('undefined' === ptt && null != property.enum) {
|
|
108
|
-
field.type = 'string'
|
|
109
|
-
field.enum = property.enum
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`APIDEF: Unsupported property type: ${property.type} (${entity.name}.${field.name})`)
|
|
187
|
+
if (newField.req !== exisingField.req) {
|
|
188
|
+
exisingField.op[mop.name] = {
|
|
189
|
+
req: newField.req,
|
|
190
|
+
type: newField.type,
|
|
191
|
+
}
|
|
114
192
|
}
|
|
193
|
+
|
|
194
|
+
return exisingField
|
|
115
195
|
}
|
|
116
196
|
|
|
117
197
|
|
|
118
198
|
export {
|
|
119
|
-
fieldTransform
|
|
199
|
+
fieldTransform,
|
|
120
200
|
}
|
|
121
|
-
|