@voxgig/apidef 0.2.0 → 0.3.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/run-apidef.js +178 -0
- package/bin/run-apidef.js~ +330 -0
- package/dist/apidef.d.ts +10 -1
- package/dist/apidef.js +157 -175
- 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 +13 -5
- package/src/apidef.ts +191 -240
- 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,133 @@
|
|
|
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'
|
|
10
|
+
import Pino from 'pino'
|
|
11
|
+
import PinoPretty from 'pino-pretty'
|
|
12
|
+
|
|
13
|
+
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
resolveTransforms,
|
|
18
|
+
processTransforms,
|
|
19
|
+
fixName,
|
|
20
|
+
} from './transform'
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
type ApiDefOptions = {
|
|
18
24
|
fs?: any
|
|
25
|
+
pino?: ReturnType<typeof Pino>
|
|
26
|
+
debug?: boolean | string
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
|
|
30
|
+
type ApiDefSpec = {
|
|
31
|
+
def: string
|
|
32
|
+
model: string,
|
|
33
|
+
kind: string,
|
|
34
|
+
meta: Record<string, any>,
|
|
35
|
+
}
|
|
22
36
|
|
|
23
37
|
|
|
24
38
|
function ApiDef(opts: ApiDefOptions = {}) {
|
|
25
39
|
const fs = opts.fs || Fs
|
|
40
|
+
let pino = opts.pino
|
|
41
|
+
|
|
42
|
+
if (null == pino) {
|
|
43
|
+
let pretty = PinoPretty({ sync: true })
|
|
44
|
+
const level = null == opts.debug ? 'info' :
|
|
45
|
+
true === opts.debug ? 'debug' :
|
|
46
|
+
'string' == typeof opts.debug ? opts.debug :
|
|
47
|
+
'info'
|
|
48
|
+
|
|
49
|
+
pino = Pino({
|
|
50
|
+
name: 'apidef',
|
|
51
|
+
level,
|
|
52
|
+
},
|
|
53
|
+
pretty
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
const log = pino.child({ cmp: 'apidef' })
|
|
26
59
|
|
|
27
60
|
|
|
28
61
|
async function watch(spec: any) {
|
|
62
|
+
log.info({ point: 'watch-start' })
|
|
63
|
+
log.debug({ point: 'watch-spec', spec })
|
|
64
|
+
|
|
29
65
|
await generate(spec)
|
|
30
66
|
|
|
31
67
|
const fsw = new FSWatcher()
|
|
32
68
|
|
|
33
69
|
fsw.on('change', (...args: any[]) => {
|
|
34
|
-
|
|
70
|
+
log.trace({ watch: 'change', file: args[0] })
|
|
35
71
|
generate(spec)
|
|
36
72
|
})
|
|
37
73
|
|
|
38
|
-
|
|
74
|
+
log.trace({ watch: 'add', what: 'def', file: spec.def })
|
|
39
75
|
fsw.add(spec.def)
|
|
40
76
|
|
|
41
|
-
|
|
77
|
+
log.trace({ watch: 'add', what: 'guide', file: spec.guilde })
|
|
42
78
|
fsw.add(spec.guide)
|
|
43
79
|
}
|
|
44
80
|
|
|
45
81
|
|
|
46
|
-
async function generate(spec:
|
|
47
|
-
|
|
82
|
+
async function generate(spec: ApiDefSpec) {
|
|
83
|
+
const start = Date.now()
|
|
84
|
+
|
|
85
|
+
log.info({ point: 'generate-start', start })
|
|
86
|
+
log.debug({ point: 'generate-spec', spec })
|
|
87
|
+
|
|
88
|
+
// TODO: Validate spec
|
|
89
|
+
const ctx = {
|
|
90
|
+
log,
|
|
91
|
+
spec,
|
|
92
|
+
guide: {},
|
|
93
|
+
opts,
|
|
94
|
+
util: { fixName },
|
|
95
|
+
defpath: Path.dirname(spec.def)
|
|
96
|
+
}
|
|
48
97
|
|
|
49
98
|
const guide = await resolveGuide(spec, opts)
|
|
50
|
-
|
|
99
|
+
log.debug({ point: 'guide', guide })
|
|
51
100
|
|
|
52
|
-
|
|
101
|
+
ctx.guide = guide
|
|
102
|
+
const transformSpec = await resolveTransforms(ctx)
|
|
103
|
+
log.debug({ point: 'transform', spec: transformSpec })
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
let source
|
|
107
|
+
try {
|
|
108
|
+
source = fs.readFileSync(spec.def, 'utf8')
|
|
109
|
+
}
|
|
110
|
+
catch (err: any) {
|
|
111
|
+
log.error({ read: 'fail', what: 'def', file: spec.def, err })
|
|
112
|
+
throw err
|
|
113
|
+
}
|
|
53
114
|
|
|
54
|
-
const modelBasePath = Path.dirname(spec.model)
|
|
55
115
|
|
|
56
116
|
const config = await createConfig({})
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
117
|
+
let bundle
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
bundle = await bundleFromString({
|
|
121
|
+
source,
|
|
122
|
+
config,
|
|
123
|
+
dereference: true,
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
catch (err: any) {
|
|
127
|
+
log.error({ parse: 'fail', what: 'openapi', file: spec.def, err })
|
|
128
|
+
throw err
|
|
129
|
+
}
|
|
130
|
+
|
|
62
131
|
|
|
63
132
|
const model = {
|
|
64
133
|
main: {
|
|
@@ -71,11 +140,18 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
71
140
|
|
|
72
141
|
try {
|
|
73
142
|
const def = bundle.bundle.parsed
|
|
74
|
-
|
|
75
|
-
|
|
143
|
+
const processResult = await processTransforms(ctx, transformSpec, model, def)
|
|
144
|
+
|
|
145
|
+
if (!processResult.ok) {
|
|
146
|
+
log.error({ process: 'fail', what: 'transform', result: processResult })
|
|
147
|
+
throw new Error('Transform failed: ' + processResult.msg)
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
log.debug({ process: 'result', what: 'transform', result: processResult })
|
|
151
|
+
}
|
|
76
152
|
}
|
|
77
153
|
catch (err: any) {
|
|
78
|
-
|
|
154
|
+
log.error({ process: 'fail', what: 'transform', err })
|
|
79
155
|
throw err
|
|
80
156
|
}
|
|
81
157
|
|
|
@@ -83,18 +159,19 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
83
159
|
let modelSrc = JSON.stringify(modelapi, null, 2)
|
|
84
160
|
modelSrc = modelSrc.substring(1, modelSrc.length - 1)
|
|
85
161
|
|
|
86
|
-
|
|
87
|
-
spec.model,
|
|
88
|
-
modelSrc
|
|
89
|
-
)
|
|
162
|
+
writeChanged('api-model', spec.model, modelSrc)
|
|
90
163
|
|
|
91
164
|
|
|
165
|
+
const modelBasePath = Path.dirname(spec.model)
|
|
92
166
|
const defFilePath = Path.join(modelBasePath, 'def.jsonic')
|
|
93
167
|
|
|
94
168
|
const modelDef = { main: { def: model.main.def } }
|
|
95
169
|
let modelDefSrc = JSON.stringify(modelDef, null, 2)
|
|
96
170
|
modelDefSrc = modelDefSrc.substring(1, modelDefSrc.length - 1)
|
|
97
171
|
|
|
172
|
+
writeChanged('def-model', defFilePath, modelDefSrc)
|
|
173
|
+
|
|
174
|
+
/*
|
|
98
175
|
let existingSrc: string = ''
|
|
99
176
|
if (fs.existsSync(defFilePath)) {
|
|
100
177
|
existingSrc = fs.readFileSync(defFilePath, 'utf8')
|
|
@@ -110,7 +187,7 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
110
187
|
modelDefSrc
|
|
111
188
|
)
|
|
112
189
|
}
|
|
113
|
-
|
|
190
|
+
*/
|
|
114
191
|
|
|
115
192
|
return {
|
|
116
193
|
ok: true,
|
|
@@ -119,6 +196,43 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
119
196
|
}
|
|
120
197
|
|
|
121
198
|
|
|
199
|
+
|
|
200
|
+
function writeChanged(what: string, path: string, content: string) {
|
|
201
|
+
let exists = false
|
|
202
|
+
let changed = false
|
|
203
|
+
let action = ''
|
|
204
|
+
try {
|
|
205
|
+
let existingContent: string = ''
|
|
206
|
+
exists = fs.existsSync(path)
|
|
207
|
+
|
|
208
|
+
if (exists) {
|
|
209
|
+
action = 'read'
|
|
210
|
+
existingContent = fs.readFileSync(path, 'utf8')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
changed = existingContent !== content
|
|
214
|
+
|
|
215
|
+
log.info({
|
|
216
|
+
write: 'file', what, skip: !changed, exists, changed,
|
|
217
|
+
contentLength: content.length, file: path
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
if (changed) {
|
|
221
|
+
action = 'write'
|
|
222
|
+
fs.writeFileSync(path, content)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (err: any) {
|
|
226
|
+
log.error({
|
|
227
|
+
fail: action, what, file: path, exists, changed,
|
|
228
|
+
contentLength: content.length, err
|
|
229
|
+
})
|
|
230
|
+
throw err
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
122
236
|
async function resolveGuide(spec: any, _opts: any) {
|
|
123
237
|
if (null == spec.guide) {
|
|
124
238
|
spec.guide = spec.def + '-guide.jsonic'
|
|
@@ -127,34 +241,58 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
127
241
|
const path = Path.normalize(spec.guide)
|
|
128
242
|
let src: string
|
|
129
243
|
|
|
130
|
-
|
|
244
|
+
let action = ''
|
|
245
|
+
let exists = false
|
|
246
|
+
try {
|
|
131
247
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
248
|
+
action = 'exists'
|
|
249
|
+
let exists = fs.existsSync(path)
|
|
250
|
+
|
|
251
|
+
log.debug({ read: 'file', what: 'guide', file: path, exists })
|
|
252
|
+
|
|
253
|
+
if (exists) {
|
|
254
|
+
action = 'read'
|
|
255
|
+
src = fs.readFileSync(path, 'utf8')
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
src = `
|
|
137
259
|
# API Specification Transform Guide
|
|
138
260
|
|
|
139
|
-
@"
|
|
261
|
+
@"@voxgig/apidef/model/guide.jsonic"
|
|
140
262
|
|
|
141
263
|
guide: entity: {
|
|
142
264
|
|
|
143
265
|
}
|
|
144
266
|
|
|
267
|
+
guide: control: transform: openapi: order: \`
|
|
268
|
+
top,
|
|
269
|
+
entity,
|
|
270
|
+
operation,
|
|
271
|
+
field,
|
|
272
|
+
manual,
|
|
273
|
+
\`
|
|
274
|
+
|
|
145
275
|
`
|
|
146
|
-
|
|
276
|
+
action = 'write'
|
|
277
|
+
fs.writeFileSync(path, src)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (err: any) {
|
|
281
|
+
log.error({ fail: action, what: 'guide', file: path, exists, err })
|
|
282
|
+
throw err
|
|
147
283
|
}
|
|
148
284
|
|
|
149
|
-
|
|
285
|
+
|
|
286
|
+
const aopts = { path }
|
|
150
287
|
const root = Aontu(src, aopts)
|
|
151
288
|
const hasErr = root.err && 0 < root.err.length
|
|
152
289
|
|
|
153
|
-
// TODO: collect all errors
|
|
154
290
|
if (hasErr) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
291
|
+
const err: any = new Error('Guide parse error:\n' +
|
|
292
|
+
(root.err.map((pe: any) => pe.msg)).join('\n'))
|
|
293
|
+
log.error({ fail: 'parse', what: 'guide', file: path, err })
|
|
294
|
+
err.errs = () => root.err
|
|
295
|
+
throw err
|
|
158
296
|
}
|
|
159
297
|
|
|
160
298
|
let genctx = new Context({ root })
|
|
@@ -162,27 +300,29 @@ guide: entity: {
|
|
|
162
300
|
|
|
163
301
|
// TODO: collect all errors
|
|
164
302
|
if (genctx.err && 0 < genctx.err.length) {
|
|
165
|
-
|
|
166
|
-
|
|
303
|
+
const err: any = new Error('Guide build error:\n' +
|
|
304
|
+
(genctx.err.map((pe: any) => pe.msg)).join('\n'))
|
|
305
|
+
log.error({ fail: 'build', what: 'guide', file: path, err })
|
|
306
|
+
err.errs = () => genctx.err
|
|
307
|
+
throw err
|
|
167
308
|
}
|
|
168
309
|
|
|
169
|
-
// console.log('GUIDE')
|
|
170
|
-
// console.dir(guide, { depth: null })
|
|
171
|
-
|
|
172
310
|
const pathParts = Path.parse(path)
|
|
173
311
|
spec.guideModelPath = Path.join(pathParts.dir, pathParts.name + '.json')
|
|
174
312
|
|
|
175
313
|
const updatedSrc = JSON.stringify(guide, null, 2)
|
|
176
314
|
|
|
315
|
+
writeChanged('guide-model', spec.guideModelPath, updatedSrc)
|
|
316
|
+
|
|
177
317
|
// console.log('APIDEF resolveGuide write', spec.guideModelPath, src !== updatedSrc)
|
|
178
|
-
let existingSrc = ''
|
|
179
|
-
if (fs.existsSync(spec.guideModelPath)) {
|
|
180
|
-
|
|
181
|
-
}
|
|
318
|
+
// let existingSrc = ''
|
|
319
|
+
// if (fs.existsSync(spec.guideModelPath)) {
|
|
320
|
+
// existingSrc = fs.readFileSync(spec.guideModelPath, 'utf8')
|
|
321
|
+
// }
|
|
182
322
|
|
|
183
|
-
if (existingSrc !== updatedSrc) {
|
|
184
|
-
|
|
185
|
-
}
|
|
323
|
+
// if (existingSrc !== updatedSrc) {
|
|
324
|
+
// fs.writeFileSync(spec.guideModelPath, updatedSrc)
|
|
325
|
+
// }
|
|
186
326
|
|
|
187
327
|
return guide
|
|
188
328
|
}
|
|
@@ -197,195 +337,6 @@ guide: entity: {
|
|
|
197
337
|
|
|
198
338
|
|
|
199
339
|
|
|
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
340
|
export type {
|
|
390
341
|
ApiDefOptions,
|
|
391
342
|
}
|
|
@@ -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
|
+
}
|