@voxgig/apidef 0.1.4 → 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 +166 -162
- 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 -6
- package/src/apidef.ts +213 -233
- 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,57 +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[]) => {
|
|
70
|
+
log.trace({ watch: 'change', file: args[0] })
|
|
34
71
|
generate(spec)
|
|
35
72
|
})
|
|
36
73
|
|
|
74
|
+
log.trace({ watch: 'add', what: 'def', file: spec.def })
|
|
37
75
|
fsw.add(spec.def)
|
|
76
|
+
|
|
77
|
+
log.trace({ watch: 'add', what: 'guide', file: spec.guilde })
|
|
78
|
+
fsw.add(spec.guide)
|
|
38
79
|
}
|
|
39
80
|
|
|
40
81
|
|
|
41
|
-
async function generate(spec:
|
|
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
|
+
}
|
|
97
|
+
|
|
42
98
|
const guide = await resolveGuide(spec, opts)
|
|
43
|
-
|
|
99
|
+
log.debug({ point: 'guide', guide })
|
|
44
100
|
|
|
45
|
-
|
|
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
|
+
}
|
|
46
114
|
|
|
47
|
-
const modelBasePath = Path.dirname(spec.model)
|
|
48
115
|
|
|
49
116
|
const config = await createConfig({})
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
+
|
|
55
131
|
|
|
56
132
|
const model = {
|
|
57
133
|
main: {
|
|
@@ -64,11 +140,18 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
64
140
|
|
|
65
141
|
try {
|
|
66
142
|
const def = bundle.bundle.parsed
|
|
67
|
-
|
|
68
|
-
|
|
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
|
+
}
|
|
69
152
|
}
|
|
70
153
|
catch (err: any) {
|
|
71
|
-
|
|
154
|
+
log.error({ process: 'fail', what: 'transform', err })
|
|
72
155
|
throw err
|
|
73
156
|
}
|
|
74
157
|
|
|
@@ -76,23 +159,35 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
76
159
|
let modelSrc = JSON.stringify(modelapi, null, 2)
|
|
77
160
|
modelSrc = modelSrc.substring(1, modelSrc.length - 1)
|
|
78
161
|
|
|
79
|
-
|
|
80
|
-
spec.model,
|
|
81
|
-
modelSrc
|
|
82
|
-
)
|
|
162
|
+
writeChanged('api-model', spec.model, modelSrc)
|
|
83
163
|
|
|
84
164
|
|
|
165
|
+
const modelBasePath = Path.dirname(spec.model)
|
|
85
166
|
const defFilePath = Path.join(modelBasePath, 'def.jsonic')
|
|
86
167
|
|
|
87
168
|
const modelDef = { main: { def: model.main.def } }
|
|
88
169
|
let modelDefSrc = JSON.stringify(modelDef, null, 2)
|
|
89
170
|
modelDefSrc = modelDefSrc.substring(1, modelDefSrc.length - 1)
|
|
90
171
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
172
|
+
writeChanged('def-model', defFilePath, modelDefSrc)
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
let existingSrc: string = ''
|
|
176
|
+
if (fs.existsSync(defFilePath)) {
|
|
177
|
+
existingSrc = fs.readFileSync(defFilePath, 'utf8')
|
|
178
|
+
}
|
|
95
179
|
|
|
180
|
+
let writeModelDef = existingSrc !== modelDefSrc
|
|
181
|
+
// console.log('APIDEF', writeModelDef)
|
|
182
|
+
|
|
183
|
+
// Only write the model def if it has changed
|
|
184
|
+
if (writeModelDef) {
|
|
185
|
+
fs.writeFileSync(
|
|
186
|
+
defFilePath,
|
|
187
|
+
modelDefSrc
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
*/
|
|
96
191
|
|
|
97
192
|
return {
|
|
98
193
|
ok: true,
|
|
@@ -101,6 +196,43 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
101
196
|
}
|
|
102
197
|
|
|
103
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
|
+
|
|
104
236
|
async function resolveGuide(spec: any, _opts: any) {
|
|
105
237
|
if (null == spec.guide) {
|
|
106
238
|
spec.guide = spec.def + '-guide.jsonic'
|
|
@@ -109,34 +241,58 @@ function ApiDef(opts: ApiDefOptions = {}) {
|
|
|
109
241
|
const path = Path.normalize(spec.guide)
|
|
110
242
|
let src: string
|
|
111
243
|
|
|
112
|
-
|
|
244
|
+
let action = ''
|
|
245
|
+
let exists = false
|
|
246
|
+
try {
|
|
113
247
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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 = `
|
|
119
259
|
# API Specification Transform Guide
|
|
120
260
|
|
|
121
|
-
@"
|
|
261
|
+
@"@voxgig/apidef/model/guide.jsonic"
|
|
122
262
|
|
|
123
263
|
guide: entity: {
|
|
124
264
|
|
|
125
265
|
}
|
|
126
266
|
|
|
267
|
+
guide: control: transform: openapi: order: \`
|
|
268
|
+
top,
|
|
269
|
+
entity,
|
|
270
|
+
operation,
|
|
271
|
+
field,
|
|
272
|
+
manual,
|
|
273
|
+
\`
|
|
274
|
+
|
|
127
275
|
`
|
|
128
|
-
|
|
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
|
|
129
283
|
}
|
|
130
284
|
|
|
131
|
-
|
|
285
|
+
|
|
286
|
+
const aopts = { path }
|
|
132
287
|
const root = Aontu(src, aopts)
|
|
133
288
|
const hasErr = root.err && 0 < root.err.length
|
|
134
289
|
|
|
135
|
-
// TODO: collect all errors
|
|
136
290
|
if (hasErr) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
140
296
|
}
|
|
141
297
|
|
|
142
298
|
let genctx = new Context({ root })
|
|
@@ -144,16 +300,29 @@ guide: entity: {
|
|
|
144
300
|
|
|
145
301
|
// TODO: collect all errors
|
|
146
302
|
if (genctx.err && 0 < genctx.err.length) {
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
149
308
|
}
|
|
150
309
|
|
|
151
|
-
// console.log('GUIDE')
|
|
152
|
-
// console.dir(guide, { depth: null })
|
|
153
|
-
|
|
154
310
|
const pathParts = Path.parse(path)
|
|
155
311
|
spec.guideModelPath = Path.join(pathParts.dir, pathParts.name + '.json')
|
|
156
|
-
|
|
312
|
+
|
|
313
|
+
const updatedSrc = JSON.stringify(guide, null, 2)
|
|
314
|
+
|
|
315
|
+
writeChanged('guide-model', spec.guideModelPath, updatedSrc)
|
|
316
|
+
|
|
317
|
+
// console.log('APIDEF resolveGuide write', spec.guideModelPath, src !== updatedSrc)
|
|
318
|
+
// let existingSrc = ''
|
|
319
|
+
// if (fs.existsSync(spec.guideModelPath)) {
|
|
320
|
+
// existingSrc = fs.readFileSync(spec.guideModelPath, 'utf8')
|
|
321
|
+
// }
|
|
322
|
+
|
|
323
|
+
// if (existingSrc !== updatedSrc) {
|
|
324
|
+
// fs.writeFileSync(spec.guideModelPath, updatedSrc)
|
|
325
|
+
// }
|
|
157
326
|
|
|
158
327
|
return guide
|
|
159
328
|
}
|
|
@@ -168,195 +337,6 @@ guide: entity: {
|
|
|
168
337
|
|
|
169
338
|
|
|
170
339
|
|
|
171
|
-
|
|
172
|
-
function resolveTranform(spec: any, guide: any, opts: any) {
|
|
173
|
-
return makeOpenAPITransform(spec, guide, opts)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
function extractFields(properties: any) {
|
|
178
|
-
const fieldMap = each(properties)
|
|
179
|
-
.reduce((a: any, p: any) => (a[p.key$] =
|
|
180
|
-
{ name: p.key$, kind: camelify(p.type) }, a), {})
|
|
181
|
-
return fieldMap
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
function fixName(base: any, name: string, prop = 'name') {
|
|
186
|
-
base[prop.toLowerCase()] = name.toLowerCase()
|
|
187
|
-
base[camelify(prop)] = camelify(name)
|
|
188
|
-
base[prop.toUpperCase()] = name.toUpperCase()
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
function makeOpenAPITransform(spec: any, guideModel: any, opts: any) {
|
|
193
|
-
|
|
194
|
-
const paramBuilder = (paramMap: any, paramDef: any,
|
|
195
|
-
entityModel: any, pathdef: any,
|
|
196
|
-
op: any, path: any, entity: any, model: any) => {
|
|
197
|
-
|
|
198
|
-
paramMap[paramDef.name] = {
|
|
199
|
-
required: paramDef.required
|
|
200
|
-
}
|
|
201
|
-
fixName(paramMap[paramDef.name], paramDef.name)
|
|
202
|
-
|
|
203
|
-
const type = paramDef.schema ? paramDef.schema.type : paramDef.type
|
|
204
|
-
fixName(paramMap[paramDef.name], type, 'type')
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const queryBuilder = (queryMap: any, queryDef: any,
|
|
209
|
-
entityModel: any, pathdef: any,
|
|
210
|
-
op: any, path: any, entity: any, model: any) => {
|
|
211
|
-
queryMap[queryDef.name] = {
|
|
212
|
-
required: queryDef.required
|
|
213
|
-
}
|
|
214
|
-
fixName(queryMap[queryDef.name], queryDef.name)
|
|
215
|
-
|
|
216
|
-
const type = queryDef.schema ? queryDef.schema.type : queryDef.type
|
|
217
|
-
fixName(queryMap[queryDef.name], type, 'type')
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const opBuilder: any = {
|
|
222
|
-
any: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
223
|
-
const em = entityModel.op[op.key$] = {
|
|
224
|
-
path: path.key$,
|
|
225
|
-
method: op.val$,
|
|
226
|
-
param: {},
|
|
227
|
-
query: {},
|
|
228
|
-
}
|
|
229
|
-
fixName(em, op.key$)
|
|
230
|
-
|
|
231
|
-
// Params are in the path
|
|
232
|
-
if (0 < path.params.length) {
|
|
233
|
-
let params = getx(pathdef[op.val$], 'parameters?in=path') || []
|
|
234
|
-
if (Array.isArray(params)) {
|
|
235
|
-
params.reduce((a: any, p: any) =>
|
|
236
|
-
(paramBuilder(a, p, entityModel, pathdef, op, path, entity, model), a), em.param)
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Queries are after the ?
|
|
241
|
-
let queries = getx(pathdef[op.val$], 'parameters?in!=path') || []
|
|
242
|
-
if (Array.isArray(queries)) {
|
|
243
|
-
queries.reduce((a: any, p: any) =>
|
|
244
|
-
(queryBuilder(a, p, entityModel, pathdef, op, path, entity, model), a), em.query)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return em
|
|
248
|
-
},
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
list: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
252
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
253
|
-
},
|
|
254
|
-
|
|
255
|
-
load: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
256
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
257
|
-
},
|
|
258
|
-
|
|
259
|
-
create: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
260
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
save: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
264
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
remove: (entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any) => {
|
|
268
|
-
return opBuilder.any(entityModel, pathdef, op, path, entity, model)
|
|
269
|
-
},
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
function fieldbuild(
|
|
275
|
-
entityModel: any, pathdef: any, op: any, path: any, entity: any, model: any
|
|
276
|
-
) {
|
|
277
|
-
// console.log(pathdef)
|
|
278
|
-
|
|
279
|
-
let fieldSets = getx(pathdef.get, 'responses 200 content "application/json" schema')
|
|
280
|
-
|
|
281
|
-
if (fieldSets) {
|
|
282
|
-
if (Array.isArray(fieldSets.allOf)) {
|
|
283
|
-
fieldSets = fieldSets.allOf
|
|
284
|
-
}
|
|
285
|
-
else if (fieldSets.properties) {
|
|
286
|
-
fieldSets = [fieldSets]
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
each(fieldSets, (fieldSet: any) => {
|
|
291
|
-
each(fieldSet.properties, (property: any) => {
|
|
292
|
-
// console.log(property)
|
|
293
|
-
|
|
294
|
-
const field =
|
|
295
|
-
(entityModel.field[property.key$] = entityModel.field[property.key$] || {})
|
|
296
|
-
|
|
297
|
-
field.name = property.key$
|
|
298
|
-
fixName(field, field.name)
|
|
299
|
-
|
|
300
|
-
field.type = property.type
|
|
301
|
-
fixName(field, field.type, 'type')
|
|
302
|
-
|
|
303
|
-
field.short = property.description
|
|
304
|
-
})
|
|
305
|
-
})
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
return function OpenAPITransform(def: any, model: any) {
|
|
310
|
-
fixName(model.main.api, spec.meta.name)
|
|
311
|
-
|
|
312
|
-
// console.log('OpenAPITransform', guideModel)
|
|
313
|
-
|
|
314
|
-
model.main.def.desc = def.info.description
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
each(guideModel.guide.entity, (entity: any) => {
|
|
318
|
-
// console.log('ENTITY', entity)
|
|
319
|
-
|
|
320
|
-
const entityModel: any = model.main.api.entity[entity.key$] = {
|
|
321
|
-
op: {},
|
|
322
|
-
field: {},
|
|
323
|
-
cmd: {},
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
fixName(entityModel, entity.key$)
|
|
327
|
-
|
|
328
|
-
each(entity.path, (path: any) => {
|
|
329
|
-
const pathdef = def.paths[path.key$]
|
|
330
|
-
|
|
331
|
-
if (null == pathdef) {
|
|
332
|
-
throw new Error('APIDEF: path not found in OpenAPI: ' + path.key$ +
|
|
333
|
-
' (entity: ' + entity.name + ')')
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
path.parts = path.key$.split('/')
|
|
337
|
-
path.params = path.parts
|
|
338
|
-
.filter((p: string) => p.startsWith('{'))
|
|
339
|
-
.map((p: string) => p.substring(1, p.length - 1))
|
|
340
|
-
|
|
341
|
-
// console.log('ENTITY-PATH', entity, path)
|
|
342
|
-
|
|
343
|
-
each(path.op, (op: any) => {
|
|
344
|
-
const opbuild = opBuilder[op.key$]
|
|
345
|
-
|
|
346
|
-
if (opbuild) {
|
|
347
|
-
opbuild(entityModel, pathdef, op, path, entity, model)
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if ('load' === op.key$) {
|
|
351
|
-
fieldbuild(entityModel, pathdef, op, path, entity, model)
|
|
352
|
-
}
|
|
353
|
-
})
|
|
354
|
-
})
|
|
355
|
-
})
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
340
|
export type {
|
|
361
341
|
ApiDefOptions,
|
|
362
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
|
+
}
|