@voxgig/apidef 2.0.2 → 2.2.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/dist/apidef.js +14 -1
- package/dist/apidef.js.map +1 -1
- package/dist/guide/heuristic01.js +189 -50
- package/dist/guide/heuristic01.js.map +1 -1
- package/dist/guide.js +36 -8
- package/dist/guide.js.map +1 -1
- package/dist/parse.d.ts +2 -1
- package/dist/parse.js +81 -3
- package/dist/parse.js.map +1 -1
- package/dist/transform/clean.d.ts +3 -0
- package/dist/transform/clean.js +16 -0
- package/dist/transform/clean.js.map +1 -0
- package/dist/transform/field.js +24 -1
- package/dist/transform/field.js.map +1 -1
- package/dist/transform/operation.js +88 -23
- package/dist/transform/operation.js.map +1 -1
- package/dist/transform.d.ts +1 -8
- package/dist/transform.js +121 -94
- package/dist/transform.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +6 -1
- package/dist/utility.js +42 -14
- package/dist/utility.js.map +1 -1
- package/model/apidef.jsonic +1 -0
- package/package.json +9 -10
- package/src/apidef.ts +24 -3
- package/src/guide/heuristic01.ts +263 -59
- package/src/guide.ts +48 -9
- package/src/parse.ts +106 -4
- package/src/transform/clean.ts +28 -0
- package/src/transform/field.ts +27 -1
- package/src/transform/operation.ts +118 -31
- package/src/transform.ts +28 -21
- package/src/utility.ts +48 -14
package/src/guide.ts
CHANGED
|
@@ -3,38 +3,65 @@ import Path from 'node:path'
|
|
|
3
3
|
|
|
4
4
|
import { File, Content, each } from 'jostraca'
|
|
5
5
|
|
|
6
|
+
import { merge } from '@voxgig/struct'
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
import { heuristic01 } from './guide/heuristic01'
|
|
8
10
|
|
|
11
|
+
import {
|
|
12
|
+
getdlog,
|
|
13
|
+
} from './utility'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// Log non-fatal wierdness.
|
|
17
|
+
const dlog = getdlog('apidef', __filename)
|
|
9
18
|
|
|
10
19
|
async function resolveGuide(ctx: any) {
|
|
11
|
-
let
|
|
20
|
+
let baseguide: Record<string, any> = {}
|
|
21
|
+
let override: Record<string, any> = ctx.model.main.api.guide
|
|
12
22
|
|
|
13
23
|
if ('heuristic01' === ctx.opts.strategy) {
|
|
14
|
-
|
|
24
|
+
baseguide = await heuristic01(ctx)
|
|
15
25
|
}
|
|
16
26
|
else {
|
|
17
27
|
throw new Error('Unknown guide strategy: ' + ctx.opts.strategy)
|
|
18
28
|
}
|
|
19
29
|
|
|
30
|
+
// Override generated base guide with custom hints
|
|
31
|
+
let guide = merge([{}, baseguide, override])
|
|
32
|
+
|
|
33
|
+
// TODO: this is a hack!!!
|
|
34
|
+
// Instead, update @voxgig/model, so that builders can request a reload of the entire
|
|
35
|
+
// model. This allows builders to modify the model for later buidlers
|
|
36
|
+
// during a single generation pass.
|
|
37
|
+
|
|
38
|
+
|
|
20
39
|
guide = cleanGuide(guide)
|
|
21
40
|
|
|
22
|
-
|
|
41
|
+
|
|
42
|
+
// TODO: FIX: sdk.jsonic should have final version of guide
|
|
43
|
+
if (ctx.model.main?.api) {
|
|
44
|
+
ctx.model.main.api.guide = guide
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
dlog('missing', 'ctx.model.main.api')
|
|
48
|
+
}
|
|
23
49
|
|
|
24
50
|
const guideFile =
|
|
25
51
|
Path.join(ctx.opts.folder,
|
|
26
|
-
(null == ctx.opts.outprefix ? '' : ctx.opts.outprefix) + 'guide.jsonic')
|
|
52
|
+
(null == ctx.opts.outprefix ? '' : ctx.opts.outprefix) + 'base-guide.jsonic')
|
|
27
53
|
|
|
28
54
|
const guideBlocks = [
|
|
29
55
|
'# Guide',
|
|
30
56
|
'',
|
|
31
57
|
'main: api: guide: { ',
|
|
32
|
-
|
|
58
|
+
|
|
33
59
|
]
|
|
34
60
|
|
|
35
61
|
|
|
36
|
-
guideBlocks.push(...each(
|
|
37
|
-
guideBlocks.push(
|
|
62
|
+
guideBlocks.push(...each(baseguide.entity, (entity, entityname) => {
|
|
63
|
+
guideBlocks.push(`
|
|
64
|
+
entity: ${entityname}: {`)
|
|
38
65
|
|
|
39
66
|
each(entity.path, (path, pathname) => {
|
|
40
67
|
guideBlocks.push(` path: '${pathname}': op: {`)
|
|
@@ -58,8 +85,8 @@ async function resolveGuide(ctx: any) {
|
|
|
58
85
|
const guideSrc = guideBlocks.join('\n')
|
|
59
86
|
|
|
60
87
|
return () => {
|
|
61
|
-
|
|
62
|
-
|
|
88
|
+
// Save base guide for reference
|
|
89
|
+
File({ name: '../def/' + Path.basename(guideFile) }, () => Content(guideSrc))
|
|
63
90
|
}
|
|
64
91
|
}
|
|
65
92
|
|
|
@@ -70,7 +97,19 @@ function cleanGuide(guide: Record<string, any>): Record<string, any> {
|
|
|
70
97
|
entity: {}
|
|
71
98
|
}
|
|
72
99
|
|
|
100
|
+
const exclude_entity = guide.exclude?.entity?.split(',') || []
|
|
101
|
+
const include_entity = guide.include?.entity?.split(',') || []
|
|
102
|
+
|
|
73
103
|
each(guide.entity, (entity: any, name: string) => {
|
|
104
|
+
if (exclude_entity.includes(name)) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
if (exclude_entity.includes('*')) {
|
|
108
|
+
if (!include_entity.includes(name)) {
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
74
113
|
let ent: any = clean.entity[name] = clean.entity[name] = { name, path: {} }
|
|
75
114
|
|
|
76
115
|
each(entity.path, (path: any, pathname: string) => {
|
package/src/parse.ts
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { bundleFromString, createConfig } from '@redocly/openapi-core'
|
|
4
4
|
|
|
5
|
+
import { each, snakify } from 'jostraca'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
import { depluralize } from './utility'
|
|
5
9
|
|
|
6
10
|
|
|
7
11
|
// Parse an API definition source into a JSON sructure.
|
|
@@ -16,12 +20,51 @@ async function parse(kind: string, source: any, meta?: any) {
|
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
async function parseOpenAPI(source: any, meta?: any) {
|
|
19
|
-
const
|
|
20
|
-
|
|
23
|
+
const base = meta?.config || {}
|
|
24
|
+
const config: any = await createConfig(base)
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
// First pass: parse without dereferencing to preserve $refs
|
|
27
|
+
const bundleWithRefs = await bundleFromString({
|
|
23
28
|
source,
|
|
24
29
|
config,
|
|
30
|
+
dereference: false,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// Walk the tree and add x-ref properties
|
|
34
|
+
const seen = new WeakSet()
|
|
35
|
+
let refCount = 0
|
|
36
|
+
|
|
37
|
+
function addXRefs(obj: any, path: string = '') {
|
|
38
|
+
if (!obj || typeof obj !== 'object' || seen.has(obj)) return
|
|
39
|
+
seen.add(obj)
|
|
40
|
+
|
|
41
|
+
if (Array.isArray(obj)) {
|
|
42
|
+
obj.forEach((item, index) => addXRefs(item, `${path}[${index}]`))
|
|
43
|
+
} else {
|
|
44
|
+
// Check for $ref property
|
|
45
|
+
if (obj.$ref && typeof obj.$ref === 'string') {
|
|
46
|
+
obj['x-ref'] = obj.$ref
|
|
47
|
+
refCount++
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Recursively process all properties
|
|
51
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
52
|
+
if (value && typeof value === 'object') {
|
|
53
|
+
addXRefs(value, path ? `${path}.${key}` : key)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
addXRefs(bundleWithRefs.bundle.parsed)
|
|
60
|
+
|
|
61
|
+
// Serialize back to string with x-refs preserved
|
|
62
|
+
const sourceWithXRefs = JSON.stringify(bundleWithRefs.bundle.parsed)
|
|
63
|
+
|
|
64
|
+
// Second pass: parse with dereferencing
|
|
65
|
+
const bundle = await bundleFromString({
|
|
66
|
+
source: sourceWithXRefs,
|
|
67
|
+
config,
|
|
25
68
|
dereference: true,
|
|
26
69
|
})
|
|
27
70
|
|
|
@@ -31,6 +74,65 @@ async function parseOpenAPI(source: any, meta?: any) {
|
|
|
31
74
|
}
|
|
32
75
|
|
|
33
76
|
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
// Make consistent changes to support semantic entities.
|
|
83
|
+
function rewrite(def: any) {
|
|
84
|
+
const paths = def.paths
|
|
85
|
+
each(paths, (path) => {
|
|
86
|
+
each(path.parameters, (param: any) => {
|
|
87
|
+
|
|
88
|
+
let new_param = param.name
|
|
89
|
+
let new_path = path.key$
|
|
90
|
+
|
|
91
|
+
// Rename param if nane is "id", and not the final param.
|
|
92
|
+
// Rewrite /foo/{id}/bar as /foo/{foo_id}/bar.
|
|
93
|
+
// Avoids ambiguity with bar id.
|
|
94
|
+
if (param.name.match(/^id$/i)) {
|
|
95
|
+
let m = path.key$.match(/\/([^\/]+)\/{id\}\/[^\/]/)
|
|
96
|
+
|
|
97
|
+
if (m) {
|
|
98
|
+
const parent = depluralize(snakify(m[1]))
|
|
99
|
+
new_param = `${parent}_id`
|
|
100
|
+
new_path = path.key$.replace('{id}', '{' + new_param + '}')
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
new_param = depluralize(snakify(param.name))
|
|
105
|
+
new_path = path.key$.replace('{' + param.name + '}', '{' + new_param + '}')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let pathdef = paths[path.key$]
|
|
109
|
+
delete paths[path.key$]
|
|
110
|
+
|
|
111
|
+
paths[new_path] = pathdef
|
|
112
|
+
path.key$ = new_path
|
|
113
|
+
|
|
114
|
+
param.name = new_param
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
sortkeys(def, 'paths')
|
|
120
|
+
sortkeys(def, 'components')
|
|
121
|
+
|
|
122
|
+
return def
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
function sortkeys(obj: any, prop: string) {
|
|
127
|
+
const sorted: any = {}
|
|
128
|
+
const sorted_keys = Object.keys(obj[prop]).sort()
|
|
129
|
+
for (let sk of sorted_keys) {
|
|
130
|
+
sorted[sk] = obj[prop][sk]
|
|
131
|
+
}
|
|
132
|
+
obj[prop] = sorted
|
|
133
|
+
}
|
|
134
|
+
|
|
34
135
|
export {
|
|
35
|
-
parse
|
|
136
|
+
parse,
|
|
137
|
+
rewrite,
|
|
36
138
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
import { each, getx } from 'jostraca'
|
|
3
|
+
|
|
4
|
+
import type { TransformResult } from '../transform'
|
|
5
|
+
|
|
6
|
+
import { walk } from '@voxgig/struct'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const cleanTransform = async function(
|
|
11
|
+
ctx: any,
|
|
12
|
+
): Promise<TransformResult> {
|
|
13
|
+
const { apimodel } = ctx
|
|
14
|
+
|
|
15
|
+
walk(apimodel, (k: any, v: any) => {
|
|
16
|
+
if ('string' === typeof k && k.includes('$')) {
|
|
17
|
+
return undefined
|
|
18
|
+
}
|
|
19
|
+
return v
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return { ok: true, msg: 'clean' }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
cleanTransform
|
|
28
|
+
}
|
package/src/transform/field.ts
CHANGED
|
@@ -69,7 +69,8 @@ function fieldbuild(
|
|
|
69
69
|
field.name = property.key$
|
|
70
70
|
fixName(field, field.name)
|
|
71
71
|
|
|
72
|
-
field.type = property.type
|
|
72
|
+
// field.type = property.type
|
|
73
|
+
resolveFieldType(entityModel, field, property)
|
|
73
74
|
fixName(field, field.type, 'type')
|
|
74
75
|
|
|
75
76
|
field.short = property.description
|
|
@@ -93,6 +94,31 @@ function fieldbuild(
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
|
|
97
|
+
// Resovles a heuristic "primary" type which subsumes the more detailed type.
|
|
98
|
+
// The primary type is only: string, number, boolean, null, object, array
|
|
99
|
+
function resolveFieldType(entity: any, field: any, property: any) {
|
|
100
|
+
const ptt = typeof property.type
|
|
101
|
+
|
|
102
|
+
if ('string' === ptt) {
|
|
103
|
+
field.type = property.type
|
|
104
|
+
}
|
|
105
|
+
else if (Array.isArray(property.type)) {
|
|
106
|
+
field.type =
|
|
107
|
+
(property.type.filter((t: string) => 'null' != t).sort()[0]) ||
|
|
108
|
+
property.type[0] ||
|
|
109
|
+
'string'
|
|
110
|
+
field.typelist = property.type
|
|
111
|
+
}
|
|
112
|
+
else if ('undefined' === ptt && null != property.enum) {
|
|
113
|
+
field.type = 'string'
|
|
114
|
+
field.enum = property.enum
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`APIDEF: Unsupported property type: ${property.type} (${entity.name}.${field.name})`)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
96
122
|
|
|
97
123
|
export {
|
|
98
124
|
fieldTransform
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import { each, getx } from 'jostraca'
|
|
3
|
+
import { each, getx, snakify } from 'jostraca'
|
|
4
|
+
|
|
5
|
+
import { transform, setprop, getprop } from '@voxgig/struct'
|
|
4
6
|
|
|
5
7
|
import type { TransformResult } from '../transform'
|
|
6
8
|
|
|
7
9
|
import { fixName, OPKIND } from '../transform'
|
|
8
10
|
|
|
11
|
+
import { depluralize } from '../utility'
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
const operationTransform = async function(
|
|
@@ -31,7 +34,6 @@ const operationTransform = async function(
|
|
|
31
34
|
entity: any,
|
|
32
35
|
model: any
|
|
33
36
|
) => {
|
|
34
|
-
|
|
35
37
|
const paramSpec: any = paramMap[paramDef.name] = {
|
|
36
38
|
required: paramDef.required
|
|
37
39
|
}
|
|
@@ -143,12 +145,13 @@ const operationTransform = async function(
|
|
|
143
145
|
content: any,
|
|
144
146
|
schema: any,
|
|
145
147
|
propkeys: any
|
|
146
|
-
) => {
|
|
148
|
+
): [any, string] => {
|
|
147
149
|
let why = 'default'
|
|
148
150
|
let transform: any = '`body`'
|
|
151
|
+
const properties = schema?.properties
|
|
149
152
|
|
|
150
|
-
if (null == content || null == schema || null == propkeys) {
|
|
151
|
-
return transform
|
|
153
|
+
if (null == content || null == schema || null == propkeys || null == properties) {
|
|
154
|
+
return [transform, why]
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
const opname = op.key$
|
|
@@ -162,9 +165,12 @@ const operationTransform = async function(
|
|
|
162
165
|
else {
|
|
163
166
|
// Use sub property that is an array
|
|
164
167
|
for (let pk of propkeys) {
|
|
165
|
-
if ('array' ===
|
|
168
|
+
if ('array' === properties[pk]?.type) {
|
|
166
169
|
why = 'list-single-array:' + pk
|
|
167
170
|
transform = '`body.' + pk + '`'
|
|
171
|
+
|
|
172
|
+
// TODO: if each item has prop === name of entity, use that, get with $EACH
|
|
173
|
+
|
|
168
174
|
break
|
|
169
175
|
}
|
|
170
176
|
}
|
|
@@ -173,14 +179,14 @@ const operationTransform = async function(
|
|
|
173
179
|
}
|
|
174
180
|
else {
|
|
175
181
|
if ('object' === schema.type) {
|
|
176
|
-
if (null ==
|
|
182
|
+
if (null == properties.id) {
|
|
177
183
|
if (1 === propkeys.length) {
|
|
178
184
|
why = 'map-single-prop:' + propkeys[0]
|
|
179
185
|
transform = '`body.' + propkeys[0] + '`'
|
|
180
186
|
}
|
|
181
187
|
else {
|
|
182
188
|
for (let pk of propkeys) {
|
|
183
|
-
if (
|
|
189
|
+
if (properties[pk]?.properties?.id) {
|
|
184
190
|
why = 'map-sub-prop:' + pk
|
|
185
191
|
transform = '`body.' + pk + '`'
|
|
186
192
|
break
|
|
@@ -205,12 +211,13 @@ const operationTransform = async function(
|
|
|
205
211
|
content: any,
|
|
206
212
|
schema: any,
|
|
207
213
|
propkeys: any
|
|
208
|
-
) => {
|
|
209
|
-
let transform: any = '`
|
|
214
|
+
): [any, string] => {
|
|
215
|
+
let transform: any = '`reqdata`'
|
|
210
216
|
let why = 'default'
|
|
217
|
+
const properties = schema?.properties
|
|
211
218
|
|
|
212
|
-
if (null == content || null == schema || null == propkeys) {
|
|
213
|
-
return transform
|
|
219
|
+
if (null == content || null == schema || null == propkeys || null == properties) {
|
|
220
|
+
return [transform, why]
|
|
214
221
|
}
|
|
215
222
|
|
|
216
223
|
const opname = op.key$
|
|
@@ -219,14 +226,14 @@ const operationTransform = async function(
|
|
|
219
226
|
if ('array' !== schema.type) {
|
|
220
227
|
if (1 === propkeys.length) {
|
|
221
228
|
why = 'list-single-prop:' + propkeys[0]
|
|
222
|
-
transform = { [propkeys[0]]: '`
|
|
229
|
+
transform = { [propkeys[0]]: '`reqdata`' }
|
|
223
230
|
}
|
|
224
231
|
else {
|
|
225
232
|
// Use sub property that is an array
|
|
226
233
|
for (let pk of propkeys) {
|
|
227
|
-
if ('array' ===
|
|
234
|
+
if ('array' === properties[pk]?.type) {
|
|
228
235
|
why = 'list-single-array:' + pk
|
|
229
|
-
transform = { [pk]: '`
|
|
236
|
+
transform = { [pk]: '`reqdata`' }
|
|
230
237
|
break
|
|
231
238
|
}
|
|
232
239
|
}
|
|
@@ -235,16 +242,16 @@ const operationTransform = async function(
|
|
|
235
242
|
}
|
|
236
243
|
else {
|
|
237
244
|
if ('object' === schema.type) {
|
|
238
|
-
if (null ==
|
|
245
|
+
if (null == properties.id) {
|
|
239
246
|
if (1 === propkeys.length) {
|
|
240
247
|
why = 'map-single-prop:' + propkeys[0]
|
|
241
|
-
transform = { [propkeys[0]]: '`
|
|
248
|
+
transform = { [propkeys[0]]: '`reqdata`' }
|
|
242
249
|
}
|
|
243
250
|
else {
|
|
244
251
|
for (let pk of propkeys) {
|
|
245
|
-
if (
|
|
252
|
+
if (properties[pk]?.properties?.id) {
|
|
246
253
|
why = 'map-sub-prop:' + pk
|
|
247
|
-
transform = { [pk]: '`
|
|
254
|
+
transform = { [pk]: '`reqdata`' }
|
|
248
255
|
break
|
|
249
256
|
}
|
|
250
257
|
}
|
|
@@ -269,8 +276,10 @@ const operationTransform = async function(
|
|
|
269
276
|
const [reqform, reqform_COMMENT] =
|
|
270
277
|
resolveTransform(entityModel, op, kind, 'reqform', pathdef)
|
|
271
278
|
|
|
272
|
-
const opModel =
|
|
279
|
+
const opModel = {
|
|
273
280
|
path: path.key$,
|
|
281
|
+
pathalt: ([] as any[]),
|
|
282
|
+
|
|
274
283
|
method,
|
|
275
284
|
kind,
|
|
276
285
|
param: {},
|
|
@@ -289,24 +298,64 @@ const operationTransform = async function(
|
|
|
289
298
|
|
|
290
299
|
fixName(opModel, op.key$)
|
|
291
300
|
|
|
301
|
+
let params: any[] = []
|
|
302
|
+
|
|
292
303
|
// Params are in the path
|
|
293
304
|
if (0 < path.params$.length) {
|
|
294
|
-
let
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
305
|
+
let sharedparams = getx(pathdef, 'parameters?in=path') || []
|
|
306
|
+
params = sharedparams.concat(
|
|
307
|
+
getx(pathdef[method], 'parameters?in=path') || []
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
// if (Array.isArray(params)) {
|
|
311
|
+
params.reduce((a: any, p: any) =>
|
|
312
|
+
(paramBuilder(a, p, opModel, entityModel,
|
|
313
|
+
pathdef, op, path, entity, model), a), opModel.param)
|
|
314
|
+
//}
|
|
300
315
|
}
|
|
301
316
|
|
|
302
317
|
// Queries are after the ?
|
|
303
|
-
let
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
318
|
+
let sharedqueries = getx(pathdef, 'parameters?in!=path') || []
|
|
319
|
+
let queries = sharedqueries.concat(getx(pathdef[method], 'parameters?in!=path') || [])
|
|
320
|
+
queries.reduce((a: any, p: any) =>
|
|
321
|
+
(queryBuilder(a, p, opModel, entityModel,
|
|
322
|
+
pathdef, op, path, entity, model), a), opModel.query)
|
|
323
|
+
|
|
324
|
+
let pathalt: any[] = []
|
|
325
|
+
const pathselector = makePathSelector(path.key$) // , params)
|
|
326
|
+
let before = false
|
|
327
|
+
|
|
328
|
+
if (null != entityModel.op[opname]) {
|
|
329
|
+
pathalt = entityModel.op[opname].pathalt
|
|
330
|
+
|
|
331
|
+
// Ordering for pathalts: most to least paramrs, then alphanumberic
|
|
332
|
+
for (let i = 0; i < pathalt.length; i++) {
|
|
333
|
+
let current = pathalt[i]
|
|
334
|
+
before =
|
|
335
|
+
pathselector.pn$ > current.pn$ ||
|
|
336
|
+
(pathselector.pn$ === current.pn$ &&
|
|
337
|
+
pathselector.path <= current.path)
|
|
338
|
+
|
|
339
|
+
if (before) {
|
|
340
|
+
pathalt = [
|
|
341
|
+
...pathalt.slice(0, i),
|
|
342
|
+
pathselector,
|
|
343
|
+
...pathalt.slice(i),
|
|
344
|
+
]
|
|
345
|
+
break
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (!before) {
|
|
351
|
+
pathalt.push(pathselector)
|
|
308
352
|
}
|
|
309
353
|
|
|
354
|
+
opModel.path = pathalt[pathalt.length - 1].path
|
|
355
|
+
opModel.pathalt = pathalt
|
|
356
|
+
|
|
357
|
+
entityModel.op[opname] = opModel
|
|
358
|
+
|
|
310
359
|
return opModel
|
|
311
360
|
},
|
|
312
361
|
|
|
@@ -333,6 +382,22 @@ const operationTransform = async function(
|
|
|
333
382
|
|
|
334
383
|
}
|
|
335
384
|
|
|
385
|
+
/*
|
|
386
|
+
console.dir(
|
|
387
|
+
transform({ guide }, {
|
|
388
|
+
entity: {
|
|
389
|
+
'`$PACK`': ['guide.entity', {
|
|
390
|
+
'`$KEY`': 'name',
|
|
391
|
+
op: {
|
|
392
|
+
// load: ['`$IF`', ['`$SELECT`',{path:{'`$ANY`':{op:{load:'`$EXISTS`'}}}}], {
|
|
393
|
+
load: ['`$IF`', 'path.*.op.load', {
|
|
394
|
+
path: () => 'foo'
|
|
395
|
+
}]
|
|
396
|
+
}
|
|
397
|
+
}]
|
|
398
|
+
}
|
|
399
|
+
}), { depth: null })
|
|
400
|
+
*/
|
|
336
401
|
|
|
337
402
|
each(guide.entity, (guideEntity: any) => {
|
|
338
403
|
let opcount = 0
|
|
@@ -348,6 +413,8 @@ const operationTransform = async function(
|
|
|
348
413
|
opcount++
|
|
349
414
|
}
|
|
350
415
|
})
|
|
416
|
+
|
|
417
|
+
|
|
351
418
|
})
|
|
352
419
|
|
|
353
420
|
msg += guideEntity.name + '=' + opcount + ' '
|
|
@@ -357,6 +424,26 @@ const operationTransform = async function(
|
|
|
357
424
|
}
|
|
358
425
|
|
|
359
426
|
|
|
427
|
+
function makePathSelector(path: string) { // , params: any[]) {
|
|
428
|
+
let out: any = { path }
|
|
429
|
+
|
|
430
|
+
let pn$ = 0
|
|
431
|
+
for (const m of path.matchAll(/\/[^\/]+\/{([^}]+)\}/g)) {
|
|
432
|
+
const paramName = depluralize(snakify(m[1]))
|
|
433
|
+
out[paramName] = true
|
|
434
|
+
pn$++
|
|
435
|
+
}
|
|
436
|
+
out.pn$ = pn$
|
|
437
|
+
|
|
438
|
+
// for (let p of params) {
|
|
439
|
+
// setprop(out, p.name, getprop(out, p.name, false))
|
|
440
|
+
// console.log('SP', p.name, out[p.name])
|
|
441
|
+
// }
|
|
442
|
+
|
|
443
|
+
return out
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
|
|
360
447
|
export {
|
|
361
448
|
operationTransform
|
|
362
449
|
}
|
package/src/transform.ts
CHANGED
|
@@ -79,6 +79,7 @@ const GuideShape = Gubu({
|
|
|
79
79
|
type Guide = ReturnType<typeof GuideShape>
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
/*
|
|
82
83
|
async function resolveTransforms(ctx: TransformCtx): Promise<TransformSpec> {
|
|
83
84
|
const { log, model: { main: { api: { guide } } } } = ctx
|
|
84
85
|
|
|
@@ -89,27 +90,27 @@ async function resolveTransforms(ctx: TransformCtx): Promise<TransformSpec> {
|
|
|
89
90
|
// TODO: parameterize
|
|
90
91
|
const defkind = 'openapi'
|
|
91
92
|
const transformNames = guide.control.transform[defkind].order
|
|
92
|
-
.split(/\s*,\s
|
|
93
|
+
.split(/\s*,\s* /)
|
|
93
94
|
.map((t: string) => t.trim())
|
|
94
|
-
|
|
95
|
+
.filter((t: string) => '' != t)
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
log.info({
|
|
98
|
+
point: 'transform', note: 'order: ' + transformNames.join(';'),
|
|
99
|
+
order: transformNames
|
|
100
|
+
})
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
catch (err: any) {
|
|
109
|
-
throw err
|
|
102
|
+
try {
|
|
103
|
+
for (const tn of transformNames) {
|
|
104
|
+
log.debug({ what: 'transform', transform: tn, note: tn })
|
|
105
|
+
const transform = await resolveTransform(tn, ctx)
|
|
106
|
+
tspec.transform.push(transform)
|
|
110
107
|
}
|
|
108
|
+
}
|
|
109
|
+
catch (err: any) {
|
|
110
|
+
throw err
|
|
111
|
+
}
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
return tspec
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
|
|
@@ -195,13 +196,19 @@ async function processTransforms(
|
|
|
195
196
|
|
|
196
197
|
return pres
|
|
197
198
|
}
|
|
199
|
+
*/
|
|
198
200
|
|
|
199
201
|
|
|
200
202
|
|
|
201
203
|
function fixName(base: any, name: string, prop = 'name') {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
if (null != base && 'object' === typeof base && 'string' === typeof name) {
|
|
205
|
+
base[prop.toLowerCase()] = name.toLowerCase()
|
|
206
|
+
base[camelify(prop)] = camelify(name)
|
|
207
|
+
base[prop.toUpperCase()] = name.toUpperCase()
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
// record to a "wierds" log
|
|
211
|
+
}
|
|
205
212
|
}
|
|
206
213
|
|
|
207
214
|
|
|
@@ -221,6 +228,6 @@ export {
|
|
|
221
228
|
fixName,
|
|
222
229
|
OPKIND,
|
|
223
230
|
GuideShape,
|
|
224
|
-
resolveTransforms,
|
|
225
|
-
processTransforms,
|
|
231
|
+
// resolveTransforms,
|
|
232
|
+
// processTransforms,
|
|
226
233
|
}
|