@voxgig/apidef 0.2.0 → 1.0.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-apidef +178 -0
- package/dist/apidef.d.ts +11 -2
- package/dist/apidef.js +146 -182
- package/dist/apidef.js.map +1 -1
- package/dist/transform/entity.d.ts +6 -0
- package/dist/transform/entity.js +35 -0
- package/dist/transform/entity.js.map +1 -0
- package/dist/transform/field.d.ts +6 -0
- package/dist/transform/field.js +144 -0
- package/dist/transform/field.js.map +1 -0
- package/dist/transform/fieldTransform.d.ts +5 -0
- package/dist/transform/fieldTransform.js +55 -0
- package/dist/transform/fieldTransform.js.map +1 -0
- package/dist/transform/manual.d.ts +6 -0
- package/dist/transform/manual.js +11 -0
- package/dist/transform/manual.js.map +1 -0
- package/dist/transform/operation.d.ts +6 -0
- package/dist/transform/operation.js +81 -0
- package/dist/transform/operation.js.map +1 -0
- package/dist/transform/top.d.ts +6 -0
- package/dist/transform/top.js +11 -0
- package/dist/transform/top.js.map +1 -0
- package/dist/transform.d.ts +26 -0
- package/dist/transform.js +111 -0
- package/dist/transform.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/model/guide.jsonic +32 -0
- package/package.json +14 -5
- package/src/apidef.ts +191 -256
- package/src/transform/entity.ts +54 -0
- package/src/transform/field.ts +185 -0
- package/src/transform/manual.ts +22 -0
- package/src/transform/operation.ts +125 -0
- package/src/transform/top.ts +21 -0
- package/src/transform.ts +193 -0
- package/model/guide.jsonic~ +0 -14
package/src/apidef.ts
CHANGED
|
@@ -1,64 +1,127 @@
|
|
|
1
|
-
/* Copyright (c) 2024
|
|
1
|
+
/* Copyright (c) 2024 Voxgig, MIT License */
|
|
2
2
|
|
|
3
3
|
import * as Fs from 'node:fs'
|
|
4
|
-
|
|
5
4
|
import Path from 'node:path'
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
import { bundleFromString, createConfig } from '@redocly/openapi-core'
|
|
9
|
-
|
|
10
8
|
import { FSWatcher } from 'chokidar'
|
|
11
|
-
|
|
12
9
|
import { Aontu, Context } from 'aontu'
|
|
13
10
|
|
|
14
|
-
import {
|
|
11
|
+
import { prettyPino, Pino } from '@voxgig/util'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
resolveTransforms,
|
|
16
|
+
processTransforms,
|
|
17
|
+
fixName,
|
|
18
|
+
} from './transform'
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
type ApiDefOptions = {
|
|
18
22
|
fs?: any
|
|
23
|
+
pino?: ReturnType<typeof Pino>
|
|
24
|
+
debug?: boolean | string
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
|
|
28
|
+
type ApiDefSpec = {
|
|
29
|
+
def: string
|
|
30
|
+
model: string,
|
|
31
|
+
kind: string,
|
|
32
|
+
meta: Record<string, any>,
|
|
33
|
+
}
|
|
22
34
|
|
|
23
35
|
|
|
24
36
|
function ApiDef(opts: ApiDefOptions = {}) {
|
|
25
37
|
const fs = opts.fs || Fs
|
|
38
|
+
const pino = prettyPino('apidef', opts)
|
|
39
|
+
|
|
40
|
+
const log = pino.child({ cmp: 'apidef' })
|
|
26
41
|
|
|
27
42
|
|
|
28
43
|
async function watch(spec: any) {
|
|
44
|
+
log.info({ point: 'watch-start' })
|
|
45
|
+
log.debug({ point: 'watch-spec', spec })
|
|
46
|
+
|
|
29
47
|
await generate(spec)
|
|
30
48
|
|
|
31
49
|
const fsw = new FSWatcher()
|
|
32
50
|
|
|
33
51
|
fsw.on('change', (...args: any[]) => {
|
|
34
|
-
|
|
52
|
+
log.trace({ watch: 'change', file: args[0] })
|
|
35
53
|
generate(spec)
|
|
36
54
|
})
|
|
37
55
|
|
|
38
|
-
|
|
56
|
+
log.trace({ watch: 'add', what: 'def', file: spec.def })
|
|
39
57
|
fsw.add(spec.def)
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
log.trace({ watch: 'add', what: 'guide', file: spec.guilde })
|
|
42
60
|
fsw.add(spec.guide)
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
|
|
46
|
-
async function generate(spec:
|
|
47
|
-
|
|
64
|
+
async function generate(spec: ApiDefSpec) {
|
|
65
|
+
const start = Date.now()
|
|
66
|
+
|
|
67
|
+
// TODO: validate spec
|
|
68
|
+
|
|
69
|
+
const defpath = Path.normalize(spec.def)
|
|
70
|
+
|
|
71
|
+
log.info({ point: 'generate-start', note: 'defpath', defpath, start })
|
|
72
|
+
log.debug({ point: 'generate-spec', spec })
|
|
73
|
+
|
|
74
|
+
// TODO: Validate spec
|
|
75
|
+
const ctx = {
|
|
76
|
+
log,
|
|
77
|
+
spec,
|
|
78
|
+
guide: {},
|
|
79
|
+
opts,
|
|
80
|
+
util: { fixName },
|
|
81
|
+
defpath: Path.dirname(defpath)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
48
85
|
|
|
49
86
|
const guide = await resolveGuide(spec, opts)
|
|
50
|
-
const transform = resolveTranform(spec, guide, opts)
|
|
51
87
|
|
|
52
|
-
|
|
88
|
+
if (null == guide) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
log.debug({ point: 'guide', guide })
|
|
94
|
+
|
|
95
|
+
ctx.guide = guide
|
|
96
|
+
const transformSpec = await resolveTransforms(ctx)
|
|
97
|
+
log.debug({ point: 'transform', spec: transformSpec })
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
let source
|
|
101
|
+
try {
|
|
102
|
+
source = fs.readFileSync(spec.def, 'utf8')
|
|
103
|
+
}
|
|
104
|
+
catch (err: any) {
|
|
105
|
+
log.error({ read: 'fail', what: 'def', file: defpath, err })
|
|
106
|
+
throw err
|
|
107
|
+
}
|
|
53
108
|
|
|
54
|
-
const modelBasePath = Path.dirname(spec.model)
|
|
55
109
|
|
|
56
110
|
const config = await createConfig({})
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
111
|
+
let bundle
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
bundle = await bundleFromString({
|
|
115
|
+
source,
|
|
116
|
+
config,
|
|
117
|
+
dereference: true,
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
catch (err: any) {
|
|
121
|
+
log.error({ parse: 'fail', what: 'openapi', file: defpath, err })
|
|
122
|
+
throw err
|
|
123
|
+
}
|
|
124
|
+
|
|
62
125
|
|
|
63
126
|
const model = {
|
|
64
127
|
main: {
|
|
@@ -71,11 +134,18 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
71
134
|
|
|
72
135
|
try {
|
|
73
136
|
const def = bundle.bundle.parsed
|
|
74
|
-
|
|
75
|
-
|
|
137
|
+
const processResult = await processTransforms(ctx, transformSpec, model, def)
|
|
138
|
+
|
|
139
|
+
if (!processResult.ok) {
|
|
140
|
+
log.error({ process: 'fail', what: 'transform', result: processResult })
|
|
141
|
+
throw new Error('Transform failed: ' + processResult.msg)
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
log.debug({ process: 'result', what: 'transform', result: processResult })
|
|
145
|
+
}
|
|
76
146
|
}
|
|
77
147
|
catch (err: any) {
|
|
78
|
-
|
|
148
|
+
log.error({ process: 'fail', what: 'transform', err })
|
|
79
149
|
throw err
|
|
80
150
|
}
|
|
81
151
|
|
|
@@ -83,34 +153,19 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
83
153
|
let modelSrc = JSON.stringify(modelapi, null, 2)
|
|
84
154
|
modelSrc = modelSrc.substring(1, modelSrc.length - 1)
|
|
85
155
|
|
|
86
|
-
|
|
87
|
-
spec.model,
|
|
88
|
-
modelSrc
|
|
89
|
-
)
|
|
156
|
+
writeChanged('api-model', spec.model, modelSrc)
|
|
90
157
|
|
|
91
158
|
|
|
159
|
+
const modelBasePath = Path.dirname(spec.model)
|
|
92
160
|
const defFilePath = Path.join(modelBasePath, 'def.jsonic')
|
|
93
161
|
|
|
94
162
|
const modelDef = { main: { def: model.main.def } }
|
|
95
163
|
let modelDefSrc = JSON.stringify(modelDef, null, 2)
|
|
96
164
|
modelDefSrc = modelDefSrc.substring(1, modelDefSrc.length - 1)
|
|
97
165
|
|
|
98
|
-
|
|
99
|
-
if (fs.existsSync(defFilePath)) {
|
|
100
|
-
existingSrc = fs.readFileSync(defFilePath, 'utf8')
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let writeModelDef = existingSrc !== modelDefSrc
|
|
104
|
-
// console.log('APIDEF', writeModelDef)
|
|
105
|
-
|
|
106
|
-
// Only write the model def if it has changed
|
|
107
|
-
if (writeModelDef) {
|
|
108
|
-
fs.writeFileSync(
|
|
109
|
-
defFilePath,
|
|
110
|
-
modelDefSrc
|
|
111
|
-
)
|
|
112
|
-
}
|
|
166
|
+
writeChanged('def-model', defFilePath, modelDefSrc)
|
|
113
167
|
|
|
168
|
+
log.info({ point: 'generate-end', note: 'success', break: true })
|
|
114
169
|
|
|
115
170
|
return {
|
|
116
171
|
ok: true,
|
|
@@ -119,6 +174,47 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
119
174
|
}
|
|
120
175
|
|
|
121
176
|
|
|
177
|
+
|
|
178
|
+
function writeChanged(what: string, path: string, content: string) {
|
|
179
|
+
let exists = false
|
|
180
|
+
let changed = false
|
|
181
|
+
let action = ''
|
|
182
|
+
try {
|
|
183
|
+
let existingContent: string = ''
|
|
184
|
+
path = Path.normalize(path)
|
|
185
|
+
|
|
186
|
+
exists = fs.existsSync(path)
|
|
187
|
+
|
|
188
|
+
if (exists) {
|
|
189
|
+
action = 'read'
|
|
190
|
+
existingContent = fs.readFileSync(path, 'utf8')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
changed = existingContent !== content
|
|
194
|
+
|
|
195
|
+
log.info({
|
|
196
|
+
point: 'write-' + what,
|
|
197
|
+
note: 'changed,file',
|
|
198
|
+
write: 'file', what, skip: !changed, exists, changed,
|
|
199
|
+
contentLength: content.length, file: path
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
if (changed) {
|
|
203
|
+
action = 'write'
|
|
204
|
+
fs.writeFileSync(path, content)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (err: any) {
|
|
208
|
+
log.error({
|
|
209
|
+
fail: action, what, file: path, exists, changed,
|
|
210
|
+
contentLength: content.length, err
|
|
211
|
+
})
|
|
212
|
+
throw err
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
122
218
|
async function resolveGuide(spec: any, _opts: any) {
|
|
123
219
|
if (null == spec.guide) {
|
|
124
220
|
spec.guide = spec.def + '-guide.jsonic'
|
|
@@ -127,34 +223,70 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
127
223
|
const path = Path.normalize(spec.guide)
|
|
128
224
|
let src: string
|
|
129
225
|
|
|
130
|
-
|
|
226
|
+
let action = ''
|
|
227
|
+
let exists = false
|
|
228
|
+
try {
|
|
131
229
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
230
|
+
action = 'exists'
|
|
231
|
+
let exists = fs.existsSync(path)
|
|
232
|
+
|
|
233
|
+
log.debug({ read: 'file', what: 'guide', file: path, exists })
|
|
234
|
+
|
|
235
|
+
if (exists) {
|
|
236
|
+
action = 'read'
|
|
237
|
+
src = fs.readFileSync(path, 'utf8')
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
src = `
|
|
137
241
|
# API Specification Transform Guide
|
|
138
242
|
|
|
139
|
-
@"
|
|
243
|
+
@"@voxgig/apidef/model/guide.jsonic"
|
|
140
244
|
|
|
141
245
|
guide: entity: {
|
|
142
246
|
|
|
143
247
|
}
|
|
144
248
|
|
|
249
|
+
guide: control: transform: openapi: order: \`
|
|
250
|
+
top,
|
|
251
|
+
entity,
|
|
252
|
+
operation,
|
|
253
|
+
field,
|
|
254
|
+
manual,
|
|
255
|
+
\`
|
|
256
|
+
|
|
145
257
|
`
|
|
146
|
-
|
|
258
|
+
action = 'write'
|
|
259
|
+
fs.writeFileSync(path, src)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (err: any) {
|
|
263
|
+
log.error({ fail: action, what: 'guide', file: path, exists, err })
|
|
264
|
+
throw err
|
|
147
265
|
}
|
|
148
266
|
|
|
149
|
-
const aopts = {}
|
|
267
|
+
const aopts = { path }
|
|
150
268
|
const root = Aontu(src, aopts)
|
|
151
269
|
const hasErr = root.err && 0 < root.err.length
|
|
152
270
|
|
|
153
|
-
// TODO: collect all errors
|
|
154
271
|
if (hasErr) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
272
|
+
for (let serr of root.err) {
|
|
273
|
+
let err: any = new Error('Guide model: ' + serr.msg)
|
|
274
|
+
err.cause$ = [serr]
|
|
275
|
+
|
|
276
|
+
if ('syntax' === serr.why) {
|
|
277
|
+
err.uxmsg$ = true
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
log.error({ fail: 'parse', point: 'guide-parse', file: path, err })
|
|
281
|
+
|
|
282
|
+
if (err.uxmsg$) {
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
err.rooterrs$ = root.err
|
|
287
|
+
throw err
|
|
288
|
+
}
|
|
289
|
+
}
|
|
158
290
|
}
|
|
159
291
|
|
|
160
292
|
let genctx = new Context({ root })
|
|
@@ -162,27 +294,19 @@ guide: entity: {
|
|
|
162
294
|
|
|
163
295
|
// TODO: collect all errors
|
|
164
296
|
if (genctx.err && 0 < genctx.err.length) {
|
|
165
|
-
|
|
166
|
-
|
|
297
|
+
const err: any = new Error('Guide build error:\n' +
|
|
298
|
+
(genctx.err.map((pe: any) => pe.msg)).join('\n'))
|
|
299
|
+
log.error({ fail: 'build', what: 'guide', file: path, err })
|
|
300
|
+
err.errs = () => genctx.err
|
|
301
|
+
throw err
|
|
167
302
|
}
|
|
168
303
|
|
|
169
|
-
// console.log('GUIDE')
|
|
170
|
-
// console.dir(guide, { depth: null })
|
|
171
|
-
|
|
172
304
|
const pathParts = Path.parse(path)
|
|
173
305
|
spec.guideModelPath = Path.join(pathParts.dir, pathParts.name + '.json')
|
|
174
306
|
|
|
175
307
|
const updatedSrc = JSON.stringify(guide, null, 2)
|
|
176
308
|
|
|
177
|
-
|
|
178
|
-
let existingSrc = ''
|
|
179
|
-
if (fs.existsSync(spec.guideModelPath)) {
|
|
180
|
-
existingSrc = fs.readFileSync(spec.guideModelPath, 'utf8')
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (existingSrc !== updatedSrc) {
|
|
184
|
-
fs.writeFileSync(spec.guideModelPath, updatedSrc)
|
|
185
|
-
}
|
|
309
|
+
writeChanged('guide-model', spec.guideModelPath, updatedSrc)
|
|
186
310
|
|
|
187
311
|
return guide
|
|
188
312
|
}
|
|
@@ -197,195 +321,6 @@ guide: entity: {
|
|
|
197
321
|
|
|
198
322
|
|
|
199
323
|
|
|
200
|
-
|
|
201
|
-
function resolveTranform(spec: any, guide: any, opts: any) {
|
|
202
|
-
return makeOpenAPITransform(spec, guide, opts)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
function extractFields(properties: any) {
|
|
207
|
-
const fieldMap = each(properties)
|
|
208
|
-
.reduce((a: any, p: any) => (a[p.key$] =
|
|
209
|
-
{ name: p.key$, kind: camelify(p.type) }, a), {})
|
|
210
|
-
return fieldMap
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
function fixName(base: any, name: string, prop = 'name') {
|
|
215
|
-
base[prop.toLowerCase()] = name.toLowerCase()
|
|
216
|
-
base[camelify(prop)] = camelify(name)
|
|
217
|
-
base[prop.toUpperCase()] = name.toUpperCase()
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
function makeOpenAPITransform(spec: any, guideModel: any, opts: any) {
|
|
222
|
-
|
|
223
|
-
const paramBuilder = (paramMap: any, paramDef: any,
|
|
224
|
-
entityModel: any, pathdef: any,
|
|
225
|
-
op: any, path: any, entity: any, model: any) => {
|
|
226
|
-
|
|
227
|
-
paramMap[paramDef.name] = {
|
|
228
|
-
required: paramDef.required
|
|
229
|
-
}
|
|
230
|
-
fixName(paramMap[paramDef.name], paramDef.name)
|
|
231
|
-
|
|
232
|
-
const type = paramDef.schema ? paramDef.schema.type : paramDef.type
|
|
233
|
-
fixName(paramMap[paramDef.name], type, 'type')
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const queryBuilder = (queryMap: any, queryDef: any,
|
|
238
|
-
entityModel: any, pathdef: any,
|
|
239
|
-
op: any, path: any, entity: any, model: any) => {
|
|
240
|
-
queryMap[queryDef.name] = {
|
|
241
|
-
required: queryDef.required
|
|
242
|
-
}
|
|
243
|
-
fixName(queryMap[queryDef.name], queryDef.name)
|
|
244
|
-
|
|
245
|
-
const type = queryDef.schema ? queryDef.schema.type : queryDef.type
|
|
246
|
-
fixName(queryMap[queryDef.name], type, 'type')
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const opBuilder: any = {
|
|
251
|
-
any: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
252
|
-
const em = entityModel.op[op.key$] = {
|
|
253
|
-
path: path.key$,
|
|
254
|
-
method: op.val$,
|
|
255
|
-
param: {},
|
|
256
|
-
query: {},
|
|
257
|
-
}
|
|
258
|
-
fixName(em, op.key$)
|
|
259
|
-
|
|
260
|
-
// Params are in the path
|
|
261
|
-
if (0 < path.params.length) {
|
|
262
|
-
let params = getx(pathdef[op.val$], 'parameters?in=path') || []
|
|
263
|
-
if (Array.isArray(params)) {
|
|
264
|
-
params.reduce((a: any, p: any) =>
|
|
265
|
-
(paramBuilder(a, p, entityModel, pathdef, op, path, entity, model), a), em.param)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Queries are after the ?
|
|
270
|
-
let queries = getx(pathdef[op.val$], 'parameters?in!=path') || []
|
|
271
|
-
if (Array.isArray(queries)) {
|
|
272
|
-
queries.reduce((a: any, p: any) =>
|
|
273
|
-
(queryBuilder(a, p, entityModel, pathdef, op, path, entity, model), a), em.query)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return em
|
|
277
|
-
},
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
list: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
281
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
282
|
-
},
|
|
283
|
-
|
|
284
|
-
load: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
285
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
286
|
-
},
|
|
287
|
-
|
|
288
|
-
create: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
289
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
290
|
-
},
|
|
291
|
-
|
|
292
|
-
save: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
293
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
remove: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
297
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
298
|
-
},
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
function fieldbuild(
|
|
304
|
-
entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any
|
|
305
|
-
) {
|
|
306
|
-
// console.log(pathdef)
|
|
307
|
-
|
|
308
|
-
let fieldSets = getx(pathdef.get, 'responses 200 content "application/json" schema')
|
|
309
|
-
|
|
310
|
-
if (fieldSets) {
|
|
311
|
-
if (Array.isArray(fieldSets.allOf)) {
|
|
312
|
-
fieldSets = fieldSets.allOf
|
|
313
|
-
}
|
|
314
|
-
else if (fieldSets.properties) {
|
|
315
|
-
fieldSets = [fieldSets]
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
each(fieldSets, (fieldSet: any) => {
|
|
320
|
-
each(fieldSet.properties, (property: any) => {
|
|
321
|
-
// console.log(property)
|
|
322
|
-
|
|
323
|
-
const field =
|
|
324
|
-
(entityModel.field[property.key$] = entityModel.field[property.key$] || {})
|
|
325
|
-
|
|
326
|
-
field.name = property.key$
|
|
327
|
-
fixName(field, field.name)
|
|
328
|
-
|
|
329
|
-
field.type = property.type
|
|
330
|
-
fixName(field, field.type, 'type')
|
|
331
|
-
|
|
332
|
-
field.short = property.description
|
|
333
|
-
})
|
|
334
|
-
})
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
return function OpenAPITransform(def: any, model: any) {
|
|
339
|
-
fixName(model.main.api, spec.meta.name)
|
|
340
|
-
|
|
341
|
-
// console.log('OpenAPITransform', guideModel)
|
|
342
|
-
|
|
343
|
-
model.main.def.desc = def.info.description
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
each(guideModel.guide.entity, (entity: any) => {
|
|
347
|
-
// console.log('ENTITY', entity)
|
|
348
|
-
|
|
349
|
-
const entityModel: any = model.main.api.entity[entity.key$] = {
|
|
350
|
-
op: {},
|
|
351
|
-
field: {},
|
|
352
|
-
cmd: {},
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
fixName(entityModel, entity.key$)
|
|
356
|
-
|
|
357
|
-
each(entity.path, (path: any) => {
|
|
358
|
-
const pathdef = def.paths[path.key$]
|
|
359
|
-
|
|
360
|
-
if (null == pathdef) {
|
|
361
|
-
throw new Error('APIDEF: path not found in OpenAPI: ' + path.key$ +
|
|
362
|
-
' (entity: ' + entity.name + ')')
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
path.parts = path.key$.split('/')
|
|
366
|
-
path.params = path.parts
|
|
367
|
-
.filter((p: string) => p.startsWith('{'))
|
|
368
|
-
.map((p: string) => p.substring(1, p.length - 1))
|
|
369
|
-
|
|
370
|
-
// console.log('ENTITY-PATH', entity, path)
|
|
371
|
-
|
|
372
|
-
each(path.op, (op: any) => {
|
|
373
|
-
const opbuild = opBuilder[op.key$]
|
|
374
|
-
|
|
375
|
-
if (opbuild) {
|
|
376
|
-
opbuild(entityModel, pathdef, op, path, entity, model)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if ('load' === op.key$) {
|
|
380
|
-
fieldbuild(entityModel, pathdef, op, path, entity, model)
|
|
381
|
-
}
|
|
382
|
-
})
|
|
383
|
-
})
|
|
384
|
-
})
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
389
324
|
export type {
|
|
390
325
|
ApiDefOptions,
|
|
391
326
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { each } from 'jostraca'
|
|
4
|
+
|
|
5
|
+
import type { TransformCtx, TransformSpec } from '../transform'
|
|
6
|
+
|
|
7
|
+
import { fixName } from '../transform'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async function entityTransform(ctx: TransformCtx, tspec: TransformSpec, model: any, def: any) {
|
|
12
|
+
const { guide: { guide } } = ctx
|
|
13
|
+
let msg = ''
|
|
14
|
+
|
|
15
|
+
each(guide.entity, (guideEntity: any) => {
|
|
16
|
+
|
|
17
|
+
const entityModel: any = model.main.api.entity[guideEntity.key$] = {
|
|
18
|
+
op: {},
|
|
19
|
+
field: {},
|
|
20
|
+
cmd: {},
|
|
21
|
+
id: {
|
|
22
|
+
name: 'id',
|
|
23
|
+
field: 'id',
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fixName(entityModel, guideEntity.key$)
|
|
28
|
+
|
|
29
|
+
each(guideEntity.path, (guidePath: any) => {
|
|
30
|
+
const pathdef = def.paths[guidePath.key$]
|
|
31
|
+
|
|
32
|
+
if (null == pathdef) {
|
|
33
|
+
throw new Error('APIDEF: path not found in OpenAPI: ' + guidePath.key$ +
|
|
34
|
+
' (entity: ' + guideEntity.name + ')')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
guidePath.parts$ = guidePath.key$.split('/')
|
|
38
|
+
guidePath.params$ = guidePath.parts$
|
|
39
|
+
.filter((p: string) => p.startsWith('{'))
|
|
40
|
+
.map((p: string) => p.substring(1, p.length - 1))
|
|
41
|
+
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
msg += guideEntity.name + ' '
|
|
45
|
+
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return { ok: true, msg }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
entityTransform
|
|
54
|
+
}
|