@penkov/swagger-code-gen 1.8.9 → 1.9.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/components-parse.js +82 -10
- package/dist/index.js +4 -3
- package/dist/method.js +98 -17
- package/dist/name.utils.js +3 -1
- package/dist/parameter.js +3 -0
- package/dist/property.js +51 -29
- package/dist/schemas.js +72 -13
- package/dist/templates/scats-schema.ejs +1 -1
- package/dist/templates/schema.ejs +1 -1
- package/package.json +1 -1
package/dist/components-parse.js
CHANGED
|
@@ -1,25 +1,97 @@
|
|
|
1
|
-
import { Collection } from 'scats';
|
|
2
|
-
import { SchemaFactory } from './schemas.js';
|
|
3
|
-
import {
|
|
1
|
+
import { Collection, mutable, option } from 'scats';
|
|
2
|
+
import { SchemaFactory, SchemaObject } from './schemas.js';
|
|
3
|
+
import { SCHEMA_PREFIX } from './property.js';
|
|
4
|
+
import { Method, supportedBodyMimeTypes } from './method.js';
|
|
4
5
|
export function resolveSchemasTypes(json) {
|
|
5
6
|
const jsonSchemas = json.components.schemas;
|
|
6
7
|
const schemasNames = Collection.from(Object.keys(jsonSchemas));
|
|
7
|
-
|
|
8
|
+
const sharedBodies = Collection.from(Object.keys(json.components.requestBodies))
|
|
9
|
+
.toMap(name => {
|
|
10
|
+
const sharedBodyDef = json.components.requestBodies[name];
|
|
11
|
+
const mimeTypes = Collection.from(Object.keys(sharedBodyDef['content']));
|
|
12
|
+
const supportedMimeTypes = mimeTypes.filter(_ => supportedBodyMimeTypes.containsKey(_));
|
|
13
|
+
if (sharedBodyDef['content'][supportedMimeTypes.head]['schema']['$ref']) {
|
|
14
|
+
return [name + '$RequestBody', 'object'];
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return [name + '$RequestBody', 'property'];
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return schemasNames.toMap(name => [name, SchemaFactory.resolveSchemaType(jsonSchemas[name])])
|
|
21
|
+
.appendedAll(sharedBodies);
|
|
8
22
|
}
|
|
9
23
|
export function resolveSchemas(json, schemasTypes, options) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.
|
|
24
|
+
const schemas = Collection.from(Object.keys(json.components.schemas))
|
|
25
|
+
.toMap(schemaName => [schemaName, json.components.schemas[schemaName]])
|
|
26
|
+
.appendedAll(Collection.from(Object.keys(json.components.requestBodies))
|
|
27
|
+
.filter(rb => option(json.components.requestBodies[rb]['content']).isDefined)
|
|
28
|
+
.toMap(rb => {
|
|
29
|
+
const sharedBodyDef = json.components.requestBodies[rb];
|
|
30
|
+
const mimeTypes = Collection.from(Object.keys(sharedBodyDef['content']));
|
|
31
|
+
const supportedMimeTypes = mimeTypes.filter(_ => supportedBodyMimeTypes.containsKey(_));
|
|
32
|
+
if (sharedBodyDef['content'][supportedMimeTypes.head]['schema']['$ref']) {
|
|
33
|
+
return [rb + '$RequestBody', sharedBodyDef['content'][supportedMimeTypes.head]['schema']];
|
|
34
|
+
}
|
|
35
|
+
else if (sharedBodyDef['content'][supportedMimeTypes.head]['schema']['anyOf']) {
|
|
36
|
+
return [rb + '$RequestBody', {
|
|
37
|
+
type: Collection.from(sharedBodyDef['content'][supportedMimeTypes.head]['schema']['anyOf'])
|
|
38
|
+
.map(x => x['$ref'].toString().substring(SCHEMA_PREFIX.length)).mkString(' | ')
|
|
39
|
+
}];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
return [rb + '$RequestBody', {
|
|
43
|
+
type: 'any'
|
|
44
|
+
}];
|
|
45
|
+
}
|
|
46
|
+
}));
|
|
47
|
+
const pool = new mutable.HashMap();
|
|
48
|
+
pool.addAll(schemas.keySet
|
|
49
|
+
.filter(s => schemasTypes.get(s).contains('enum') || schemasTypes.get(s).contains('property'))
|
|
50
|
+
.toMap(name => [name, SchemaFactory.build(name, schemas.get(name).getOrElseThrow(() => new Error(`No schema for ${name}`)), schemasTypes, options)]));
|
|
51
|
+
const emptyArrayToObj = (x) => SchemaFactory.isEmptyObjectOrArray(x) ? {} : x;
|
|
52
|
+
pool.addAll(schemas.keySet
|
|
53
|
+
.filter(s => schemasTypes.get(s).contains('object') &&
|
|
54
|
+
SchemaObject.allSuperClassDefined(schemas.get(s)
|
|
55
|
+
.getOrElseThrow(() => new Error(`No schema for ${s}`)), schemasTypes, pool.keySet))
|
|
56
|
+
.toMap(name => {
|
|
57
|
+
const schema = schemas.get(name).getOrElseThrow(() => new Error(`No schema for ${name}`));
|
|
58
|
+
return [name, SchemaObject.fromDefinition(name, emptyArrayToObj(schema), schemasTypes, options, pool.toImmutable)];
|
|
59
|
+
}));
|
|
60
|
+
let currentSize = pool.size;
|
|
61
|
+
let unprocessed = schemas.keySet
|
|
62
|
+
.filter(s => !pool.containsKey(s) && schemasTypes.get(s).contains('object'));
|
|
63
|
+
while (unprocessed.nonEmpty) {
|
|
64
|
+
pool.addAll(unprocessed.filter(s => SchemaObject.allSuperClassDefined(emptyArrayToObj(schemas.get(s).get), schemasTypes, pool.keySet))
|
|
65
|
+
.toMap(name => [name, SchemaObject.fromDefinition(name, emptyArrayToObj(schemas.get(name).get), schemasTypes, options, pool.toImmutable)]));
|
|
66
|
+
unprocessed = schemas.keySet
|
|
67
|
+
.filter(s => !pool.containsKey(s) && schemasTypes.get(s).contains('object'));
|
|
68
|
+
const newSize = pool.size;
|
|
69
|
+
if (newSize <= currentSize) {
|
|
70
|
+
throw new Error(`No superclass definitions were found for ${unprocessed.mkString(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
currentSize = newSize;
|
|
73
|
+
}
|
|
74
|
+
return pool.toImmutable;
|
|
14
75
|
}
|
|
15
|
-
export function resolvePaths(json, schemasTypes, options) {
|
|
76
|
+
export function resolvePaths(json, schemasTypes, options, pool) {
|
|
16
77
|
const jsonSchemas = json.paths;
|
|
17
78
|
return Collection.from(Object.keys(jsonSchemas)).flatMap(path => {
|
|
18
79
|
const methods = jsonSchemas[path];
|
|
19
|
-
return Collection.from(Object.keys(methods)).map(methodName => new Method(path, methodName, methods[methodName], schemasTypes, options));
|
|
80
|
+
return Collection.from(Object.keys(methods)).map(methodName => new Method(path, methodName, methods[methodName], schemasTypes, options, pool));
|
|
20
81
|
}).filter(m => {
|
|
21
82
|
const included = options.includeTags.isEmpty || options.includeTags.intersect(m.tags).nonEmpty;
|
|
22
83
|
const excluded = options.excludeTags.nonEmpty && options.excludeTags.intersect(m.tags).nonEmpty;
|
|
23
84
|
return included && !excluded;
|
|
24
85
|
});
|
|
25
86
|
}
|
|
87
|
+
export function generateInPlace(paths, schemasTypes, options, pool) {
|
|
88
|
+
return paths.filter(m => option(m.response.inPlace).isDefined)
|
|
89
|
+
.map(m => {
|
|
90
|
+
return SchemaObject.fromDefinition(m.response.responseType, m.response.inPlace, schemasTypes, options, pool);
|
|
91
|
+
}).appendedAll(paths.flatMap(m => m.body)
|
|
92
|
+
.filter(b => option(b.inPlace).isDefined)
|
|
93
|
+
.map(m => {
|
|
94
|
+
console.log(`Generating inplace body: ${m.inPlaceClassname}`);
|
|
95
|
+
return SchemaObject.fromDefinition(m.inPlaceClassname, m.inPlace, schemasTypes, options, pool);
|
|
96
|
+
}));
|
|
97
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import log4js from 'log4js';
|
|
2
2
|
import fetch from 'node-fetch';
|
|
3
3
|
import { Renderer } from './renderer.js';
|
|
4
|
-
import { resolvePaths, resolveSchemas, resolveSchemasTypes } from './components-parse.js';
|
|
4
|
+
import { generateInPlace, resolvePaths, resolveSchemas, resolveSchemasTypes } from './components-parse.js';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { dirname } from 'path';
|
|
7
7
|
import https from 'https';
|
|
@@ -27,9 +27,10 @@ export async function main(url, enableScats, targetNode, outputFile, ignoreSSLEr
|
|
|
27
27
|
.then(async (json) => {
|
|
28
28
|
const schemasTypes = resolveSchemasTypes(json);
|
|
29
29
|
const schemas = resolveSchemas(json, schemasTypes, options);
|
|
30
|
-
const paths = resolvePaths(json, schemasTypes, options);
|
|
30
|
+
const paths = resolvePaths(json, schemasTypes, options, schemas);
|
|
31
|
+
const inplace = generateInPlace(paths, schemasTypes, options, schemas);
|
|
31
32
|
logger.debug(`Downloaded swagger: ${schemas.size} schemas, ${paths.size} paths`);
|
|
32
|
-
await renderer.renderToFile(schemas.values, paths, enableScats, targetNode, outputFile);
|
|
33
|
+
await renderer.renderToFile(schemas.appendedAll(inplace.toMap(s => [s.name, s])).values, paths, enableScats, targetNode, outputFile);
|
|
33
34
|
logger.debug(`Wrote client to ${outputFile}`);
|
|
34
35
|
});
|
|
35
36
|
}
|
package/dist/method.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Collection, HashMap, HashSet, identity, Nil, option } from 'scats';
|
|
2
|
-
import { Property } from './property.js';
|
|
2
|
+
import { Property, SCHEMA_PREFIX } from './property.js';
|
|
3
3
|
import { Parameter } from './parameter.js';
|
|
4
4
|
import { SchemaFactory } from './schemas.js';
|
|
5
5
|
import { NameUtils } from './name.utils.js';
|
|
6
|
+
export const SHARED_BODIES_PREFIX = '#/components/requestBodies/';
|
|
6
7
|
const sortByIn = HashMap.of(['path', 0], ['query', 1], ['header', 2], ['cookie', 3], ['body', 4]);
|
|
7
|
-
const supportedBodyMimeTypes = HashMap.of(['application/json', 'Json'], ['application/x-www-form-urlencoded', 'Form'], ['multipart/form-data', 'File'], ['application/octet-stream', 'Binary']);
|
|
8
|
+
export const supportedBodyMimeTypes = HashMap.of(['application/json', 'Json'], ['application/x-www-form-urlencoded', 'Form'], ['multipart/form-data', 'File'], ['application/octet-stream', 'Binary']);
|
|
8
9
|
export class Method {
|
|
9
|
-
constructor(path, method, def, schemasTypes, options) {
|
|
10
|
+
constructor(path, method, def, schemasTypes, options, pool) {
|
|
10
11
|
this.path = path;
|
|
11
12
|
this.method = method;
|
|
12
13
|
this.tags = HashSet.from(option(def.tags).getOrElseValue([]));
|
|
@@ -38,14 +39,70 @@ export class Method {
|
|
|
38
39
|
}
|
|
39
40
|
});
|
|
40
41
|
this.body = option(def.requestBody)
|
|
41
|
-
.flatMap(body => option(body.content)
|
|
42
|
+
.flatMap(body => option(body.content)
|
|
43
|
+
.orElse(() => {
|
|
44
|
+
return option(body).filter(x => {
|
|
45
|
+
const sharedRef = option(x['$ref']).exists(ref => ref.toString().startsWith(SHARED_BODIES_PREFIX));
|
|
46
|
+
return sharedRef;
|
|
47
|
+
})
|
|
48
|
+
.map(x => {
|
|
49
|
+
const referenced = pool.get(x['$ref'].substring(SHARED_BODIES_PREFIX.length) + '$RequestBody');
|
|
50
|
+
if (referenced.exists(o => o instanceof Property)) {
|
|
51
|
+
return {
|
|
52
|
+
'application/json': {
|
|
53
|
+
'schema': {
|
|
54
|
+
type: referenced.get.type
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
return {
|
|
61
|
+
'application/json': {
|
|
62
|
+
'schema': {
|
|
63
|
+
$ref: x['$ref'] + '$RequestBody'
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}))
|
|
42
70
|
.map(body => {
|
|
43
71
|
const bodyRequired = option(def.requestBody.required).contains(true);
|
|
44
72
|
const mimeTypes = Collection.from(Object.keys(body));
|
|
45
73
|
const supportedMimeTypes = mimeTypes.filter(_ => supportedBodyMimeTypes.containsKey(_));
|
|
46
74
|
return supportedMimeTypes.map(mt => {
|
|
47
75
|
const bodySchemaDef = body[mt].schema;
|
|
48
|
-
let res
|
|
76
|
+
let res;
|
|
77
|
+
let inPlaceClassname = null;
|
|
78
|
+
if (SchemaFactory.isEmptyObjectOrArray(bodySchemaDef)) {
|
|
79
|
+
res = Property.fromDefinition('body', {
|
|
80
|
+
...bodySchemaDef,
|
|
81
|
+
required: bodyRequired,
|
|
82
|
+
type: 'object'
|
|
83
|
+
}, schemasTypes, options);
|
|
84
|
+
}
|
|
85
|
+
else if (bodySchemaDef['$ref']) {
|
|
86
|
+
const ref = bodySchemaDef['$ref'].toString();
|
|
87
|
+
res = Property.fromDefinition('body', {
|
|
88
|
+
...bodySchemaDef,
|
|
89
|
+
$ref: ref.startsWith(SHARED_BODIES_PREFIX) ? SCHEMA_PREFIX + ref.substring(SHARED_BODIES_PREFIX.length, ref.length) : ref,
|
|
90
|
+
required: bodyRequired
|
|
91
|
+
}, schemasTypes, options);
|
|
92
|
+
}
|
|
93
|
+
else if (bodySchemaDef['type']) {
|
|
94
|
+
res = Property.fromDefinition('body', {
|
|
95
|
+
type: bodySchemaDef['type'],
|
|
96
|
+
required: bodyRequired
|
|
97
|
+
}, schemasTypes, options);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
inPlaceClassname = NameUtils.normaliseClassname(def.operationId + 'Body$' + method);
|
|
101
|
+
res = Property.fromDefinition('body', {
|
|
102
|
+
...bodySchemaDef,
|
|
103
|
+
$ref: SCHEMA_PREFIX + inPlaceClassname
|
|
104
|
+
}, schemasTypes.appended(inPlaceClassname, 'object'), options);
|
|
105
|
+
}
|
|
49
106
|
if (res.schemaType === 'property') {
|
|
50
107
|
const bProperty = res;
|
|
51
108
|
res = bProperty.copy({
|
|
@@ -56,7 +113,9 @@ export class Method {
|
|
|
56
113
|
return {
|
|
57
114
|
body: res,
|
|
58
115
|
mimeType: mt,
|
|
59
|
-
suffix: supportedMimeTypes.size > 1 ? supportedBodyMimeTypes.get(mt).getOrElseValue(mt) : ''
|
|
116
|
+
suffix: supportedMimeTypes.size > 1 ? supportedBodyMimeTypes.get(mt).getOrElseValue(mt) : '',
|
|
117
|
+
inPlace: inPlaceClassname ? bodySchemaDef : undefined,
|
|
118
|
+
inPlaceClassname: inPlaceClassname,
|
|
60
119
|
};
|
|
61
120
|
});
|
|
62
121
|
})
|
|
@@ -69,23 +128,45 @@ export class Method {
|
|
|
69
128
|
.minByOption(identity);
|
|
70
129
|
const respDef = successCode.map(_ => def.responses[_])
|
|
71
130
|
.orElse(() => option(def.responses['default']))
|
|
72
|
-
.
|
|
131
|
+
.orElse(() => statusCodes.headOption.flatMap(code => option(def.responses[code])))
|
|
132
|
+
.getOrElseValue({});
|
|
73
133
|
const mimeTypes = option(respDef.content)
|
|
74
134
|
.map(content => Collection.from(Object.keys(content)).toMap(mimeType => [mimeType, content[mimeType]])).getOrElseValue(HashMap.empty);
|
|
75
135
|
this.response = mimeTypes.get('application/json')
|
|
76
136
|
.orElseValue(mimeTypes.values.headOption)
|
|
77
|
-
.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
137
|
+
.filter(p => option(p.schema).isDefined)
|
|
138
|
+
.map(p => {
|
|
139
|
+
if (p.schema.type === 'object' && p.schema['properties'] && Object.keys(p.schema['properties']).length > 0) {
|
|
140
|
+
const inPlaceObject = NameUtils.normaliseClassname(def.operationId + 'Response$' + method);
|
|
141
|
+
const r = Property.fromDefinition('', {
|
|
142
|
+
...p.schema,
|
|
143
|
+
$ref: SCHEMA_PREFIX + inPlaceObject
|
|
144
|
+
}, schemasTypes.appended(inPlaceObject, 'object'), options).copy({
|
|
145
|
+
nullable: false,
|
|
146
|
+
required: true
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
asProperty: r,
|
|
150
|
+
responseType: inPlaceObject,
|
|
151
|
+
description: respDef.description,
|
|
152
|
+
inPlace: p.schema
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const r = Property.fromDefinition('', p.schema, schemasTypes, options).copy({
|
|
157
|
+
nullable: false,
|
|
158
|
+
required: true,
|
|
159
|
+
});
|
|
160
|
+
return {
|
|
161
|
+
asProperty: r,
|
|
162
|
+
responseType: r.jsType,
|
|
163
|
+
description: respDef.description,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
})
|
|
86
167
|
.getOrElseValue(({
|
|
87
168
|
asProperty: Property.fromDefinition('UNKNOWN', { type: 'any' }, schemasTypes, options),
|
|
88
|
-
responseType: 'any'
|
|
169
|
+
responseType: 'any',
|
|
89
170
|
}));
|
|
90
171
|
this.wrapParamsInObject = this.parameters.size > 2 || (this.body.nonEmpty) && this.parameters.nonEmpty;
|
|
91
172
|
}
|
package/dist/name.utils.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export class NameUtils {
|
|
2
2
|
static normaliseClassname(n) {
|
|
3
|
+
if (!n)
|
|
4
|
+
return '';
|
|
3
5
|
let res = '';
|
|
4
6
|
let needUpperCase = true;
|
|
5
7
|
for (let i = 0; i < n.length; i++) {
|
|
@@ -22,7 +24,7 @@ export class NameUtils {
|
|
|
22
24
|
return `$${n}`;
|
|
23
25
|
}
|
|
24
26
|
else {
|
|
25
|
-
return n.replace(
|
|
27
|
+
return n.replace(/[./]/g, '_');
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
}
|
package/dist/parameter.js
CHANGED
|
@@ -32,6 +32,9 @@ export class Parameter {
|
|
|
32
32
|
let jsType;
|
|
33
33
|
if (schema instanceof SchemaObject) {
|
|
34
34
|
jsType = schema.type;
|
|
35
|
+
if (schema.type === 'integer') {
|
|
36
|
+
jsType = 'number';
|
|
37
|
+
}
|
|
35
38
|
}
|
|
36
39
|
else if (schema instanceof SchemaEnum) {
|
|
37
40
|
if (schemas.containsKey(schema.name)) {
|
package/dist/property.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Collection, none, option } from 'scats';
|
|
1
|
+
import { Collection, Nil, none, option, some } from 'scats';
|
|
2
|
+
import { SchemaFactory } from './schemas.js';
|
|
2
3
|
import { NameUtils } from './name.utils.js';
|
|
3
|
-
const SCHEMA_PREFIX = '#/components/schemas/';
|
|
4
|
+
export const SCHEMA_PREFIX = '#/components/schemas/';
|
|
4
5
|
export class Property {
|
|
5
6
|
constructor(name, type, format, description, defaultValue, nullable, required, items, referencesObject, itemReferencesObject, enumValues) {
|
|
6
7
|
this.name = name;
|
|
@@ -30,8 +31,7 @@ export class Property {
|
|
|
30
31
|
const itemReferencesObject = option(definition.items)
|
|
31
32
|
.flatMap(i => option(i.$ref))
|
|
32
33
|
.exists(ref => schemaTypes.get(ref.substring(SCHEMA_PREFIX.length)).contains('object'));
|
|
33
|
-
const type = option(definition.$ref)
|
|
34
|
-
.map(ref => ref.substring(SCHEMA_PREFIX.length))
|
|
34
|
+
const type = option(definition.$ref).map(ref => ref.substring(SCHEMA_PREFIX.length))
|
|
35
35
|
.orElse(() => option(definition.allOf)
|
|
36
36
|
.map(x => Collection.from(x))
|
|
37
37
|
.filter(x => x.nonEmpty)
|
|
@@ -52,9 +52,20 @@ export class Property {
|
|
|
52
52
|
.filter(t => t.type !== 'null')
|
|
53
53
|
.flatMapOption(oneOfItem => option(oneOfItem.$ref)
|
|
54
54
|
.map(ref => ref.substring(SCHEMA_PREFIX.length))
|
|
55
|
-
.orElseValue(option(oneOfItem.type)))
|
|
55
|
+
.orElseValue(option(oneOfItem.type)))
|
|
56
|
+
.map(tpe => this.toJsType(tpe))
|
|
57
|
+
.mkString(' | ');
|
|
56
58
|
}))
|
|
57
|
-
.
|
|
59
|
+
.orElse(() => {
|
|
60
|
+
if (SchemaFactory.isEmptyObjectOrArray(definition)) {
|
|
61
|
+
return some('object');
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return none;
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.orElse(() => option(definition.type))
|
|
68
|
+
.getOrElseValue('any');
|
|
58
69
|
const nullable = option(definition.nullable).contains(true) ||
|
|
59
70
|
(referencesObject && options.referencedObjectsNullableByDefault && !option(definition.nullable).contains(false)) ||
|
|
60
71
|
option(definition.anyOf)
|
|
@@ -99,29 +110,40 @@ export class Property {
|
|
|
99
110
|
return this.type === 'array';
|
|
100
111
|
}
|
|
101
112
|
static toJsType(tpe, itemTpe = 'any', format = none) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return '
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
113
|
+
return option(tpe)
|
|
114
|
+
.map(x => Collection.from(x.split('|')))
|
|
115
|
+
.getOrElseValue(Nil)
|
|
116
|
+
.map(x => x.trim())
|
|
117
|
+
.map(t => {
|
|
118
|
+
switch (t) {
|
|
119
|
+
case 'boolean':
|
|
120
|
+
return 'boolean';
|
|
121
|
+
case 'number':
|
|
122
|
+
return 'number';
|
|
123
|
+
case 'integer':
|
|
124
|
+
return 'number';
|
|
125
|
+
case 'Object':
|
|
126
|
+
case 'object':
|
|
127
|
+
return 'object';
|
|
128
|
+
case 'file':
|
|
129
|
+
return 'File';
|
|
130
|
+
case 'any':
|
|
131
|
+
return 'any';
|
|
132
|
+
case 'String':
|
|
133
|
+
case 'string':
|
|
134
|
+
if (format.contains('binary')) {
|
|
135
|
+
return 'Blob | Buffer';
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return 'string';
|
|
139
|
+
}
|
|
140
|
+
case 'array':
|
|
141
|
+
return `ReadonlyArray<${Property.toJsType(itemTpe)}>`;
|
|
142
|
+
default:
|
|
143
|
+
return NameUtils.normaliseClassname(tpe);
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
.distinct.mkString(' | ');
|
|
125
147
|
}
|
|
126
148
|
get normalType() {
|
|
127
149
|
return NameUtils.normaliseClassname(this.type);
|
package/dist/schemas.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import { Collection, Nil, option } from 'scats';
|
|
2
|
-
import { Property } from './property.js';
|
|
1
|
+
import { Collection, HashMap, HashSet, Nil, option } from 'scats';
|
|
2
|
+
import { Property, SCHEMA_PREFIX } from './property.js';
|
|
3
3
|
import { NameUtils } from './name.utils.js';
|
|
4
4
|
export class SchemaFactory {
|
|
5
|
+
static isEmptyObjectOrArray(x) {
|
|
6
|
+
if (Array.isArray(x) && x.length === 0)
|
|
7
|
+
return true;
|
|
8
|
+
if (typeof x === 'object' && Object.keys(x).length === 0)
|
|
9
|
+
return true;
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
5
12
|
static resolveSchemaType(def) {
|
|
6
|
-
if (def.type === 'object' ||
|
|
13
|
+
if (def.type === 'object' ||
|
|
14
|
+
option(def.properties).exists(p => Object.keys(p).length > 0) ||
|
|
15
|
+
option(def.allOf).exists(x => x.length > 0) ||
|
|
16
|
+
SchemaFactory.isEmptyObjectOrArray(def)) {
|
|
7
17
|
return 'object';
|
|
8
18
|
}
|
|
9
19
|
else if (def.enum) {
|
|
@@ -14,8 +24,10 @@ export class SchemaFactory {
|
|
|
14
24
|
}
|
|
15
25
|
}
|
|
16
26
|
static build(name, def, schemasTypes, options) {
|
|
17
|
-
if (def.type === 'object' ||
|
|
18
|
-
|
|
27
|
+
if (def.type === 'object' ||
|
|
28
|
+
option(def.properties).exists(p => Object.keys(p).length > 0) ||
|
|
29
|
+
schemasTypes.get(name).contains('object')) {
|
|
30
|
+
return SchemaObject.fromDefinition(name, def, schemasTypes, options, HashMap.empty);
|
|
19
31
|
}
|
|
20
32
|
else if (def.enum) {
|
|
21
33
|
return SchemaEnum.fromDefinition(name, def);
|
|
@@ -72,23 +84,70 @@ export class SchemaEnum {
|
|
|
72
84
|
}
|
|
73
85
|
}
|
|
74
86
|
export class SchemaObject {
|
|
75
|
-
constructor(name, title, type, properties) {
|
|
87
|
+
constructor(name, title, type, properties, parents, explicitlyRequiredProperties) {
|
|
76
88
|
this.name = name;
|
|
77
89
|
this.title = title;
|
|
78
90
|
this.type = type;
|
|
79
91
|
this.properties = properties;
|
|
92
|
+
this.parents = parents;
|
|
93
|
+
this.explicitlyRequiredProperties = explicitlyRequiredProperties;
|
|
80
94
|
this.schemaType = 'object';
|
|
81
95
|
}
|
|
82
|
-
|
|
96
|
+
get parentsString() {
|
|
97
|
+
return this.parents.nonEmpty ? ' extends ' + this.parents.keySet.map(n => NameUtils.normaliseClassname(n)).mkString(', ') : '';
|
|
98
|
+
}
|
|
99
|
+
propsIncludingInherited() {
|
|
100
|
+
const pendingParents = this.parents.values.toArray;
|
|
101
|
+
const props = this.properties.toBuffer;
|
|
102
|
+
const propNames = props.map(p => p.name).toSet.toMutable;
|
|
103
|
+
while (pendingParents.length > 0) {
|
|
104
|
+
const parent = pendingParents.shift();
|
|
105
|
+
props.appendAll(parent.properties.filter(parentProp => !propNames.contains(parentProp.name)));
|
|
106
|
+
parent.parents.values.foreach(pp => pendingParents.push(pp));
|
|
107
|
+
}
|
|
108
|
+
return props.toCollection;
|
|
109
|
+
}
|
|
110
|
+
static allSuperClassDefined(def, schemasTypes, pool) {
|
|
111
|
+
const parents = option(def.allOf)
|
|
112
|
+
.orElse(() => option(def['$ref']).map(x => [{ $ref: x }]))
|
|
113
|
+
.map(x => Collection.from(x))
|
|
114
|
+
.filter(x => x.nonEmpty)
|
|
115
|
+
.getOrElseValue(Nil)
|
|
116
|
+
.flatMapOption(x => option(x['$ref']))
|
|
117
|
+
.map(x => x.substring(SCHEMA_PREFIX.length))
|
|
118
|
+
.filter(p => schemasTypes.get(p).contains('object'))
|
|
119
|
+
.toSet;
|
|
120
|
+
return parents.removedAll(pool).isEmpty;
|
|
121
|
+
}
|
|
122
|
+
static fromDefinition(name, def, schemasTypes, options, pool) {
|
|
123
|
+
const allOff = option(def.allOf)
|
|
124
|
+
.orElse(() => option(def['$ref']).map(x => [{ $ref: x }]))
|
|
125
|
+
.map(x => Collection.from(x)).filter(x => x.nonEmpty);
|
|
126
|
+
const parents = allOff.getOrElseValue(Nil)
|
|
127
|
+
.flatMapOption(x => option(x['$ref']))
|
|
128
|
+
.map(x => x.substring(SCHEMA_PREFIX.length))
|
|
129
|
+
.filter(p => schemasTypes.get(p).contains('object'));
|
|
83
130
|
const explicitlyRequired = option(def.required)
|
|
84
|
-
.map(arr => typeof arr === 'boolean' ? Nil : Collection.from(arr))
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
131
|
+
.map(arr => typeof arr === 'boolean' ? Nil : Collection.from(arr))
|
|
132
|
+
.getOrElseValue(Nil)
|
|
133
|
+
.appendedAll(parents.flatMap(p => pool.get(p)
|
|
134
|
+
.map(o => o.explicitlyRequiredProperties)
|
|
135
|
+
.getOrElseValue(HashSet.empty)
|
|
136
|
+
.toCollection))
|
|
137
|
+
.toSet;
|
|
138
|
+
const properties = allOff.getOrElseValue(Collection.of(def))
|
|
139
|
+
.flatMap(subSchema => {
|
|
140
|
+
return option(subSchema['properties'])
|
|
141
|
+
.map(props => Collection.from(Object.keys(props)))
|
|
142
|
+
.getOrElseValue(Nil)
|
|
143
|
+
.map(propName => {
|
|
144
|
+
const property = Property.fromDefinition(propName, subSchema['properties'][propName], schemasTypes, options);
|
|
145
|
+
return property.copy({
|
|
146
|
+
required: explicitlyRequired.contains(propName) ? true : property.required
|
|
147
|
+
});
|
|
89
148
|
});
|
|
90
149
|
});
|
|
91
|
-
return new SchemaObject(name, def.title, def.type, properties);
|
|
150
|
+
return new SchemaObject(name, def.title, def.type, properties, parents.toMap(p => [p, pool.get(p).get]), explicitlyRequired);
|
|
92
151
|
}
|
|
93
152
|
get normalName() {
|
|
94
153
|
return NameUtils.normaliseClassname(this.name);
|