@penkov/swagger-code-gen 1.8.10 → 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.
@@ -1,25 +1,97 @@
1
- import { Collection } from 'scats';
2
- import { SchemaFactory } from './schemas.js';
3
- import { Method } from './method.js';
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
- return schemasNames.toMap(name => [name, SchemaFactory.resolveSchemaType(jsonSchemas[name])]);
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 jsonSchemas = json.components.schemas;
11
- const schemasNames = Collection.from(Object.keys(jsonSchemas));
12
- return schemasNames
13
- .toMap(name => [name, SchemaFactory.build(name, jsonSchemas[name], schemasTypes, options)]);
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 = SchemaFactory.build('body', bodySchemaDef, schemasTypes, options);
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
  })
@@ -76,18 +135,38 @@ export class Method {
76
135
  this.response = mimeTypes.get('application/json')
77
136
  .orElseValue(mimeTypes.values.headOption)
78
137
  .filter(p => option(p.schema).isDefined)
79
- .map(p => Property.fromDefinition('', p.schema, schemasTypes, options).copy({
80
- nullable: false,
81
- required: true
82
- }))
83
- .map(r => ({
84
- asProperty: r,
85
- responseType: r.jsType,
86
- description: respDef.description
87
- }))
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
+ })
88
167
  .getOrElseValue(({
89
168
  asProperty: Property.fromDefinition('UNKNOWN', { type: 'any' }, schemasTypes, options),
90
- responseType: 'any'
169
+ responseType: 'any',
91
170
  }));
92
171
  this.wrapParamsInObject = this.parameters.size > 2 || (this.body.nonEmpty) && this.parameters.nonEmpty;
93
172
  }
@@ -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(/\./g, '_');
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))).mkString(' | ');
55
+ .orElseValue(option(oneOfItem.type)))
56
+ .map(tpe => this.toJsType(tpe))
57
+ .mkString(' | ');
56
58
  }))
57
- .getOrElseValue(definition.type);
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
- switch (tpe) {
103
- case 'boolean':
104
- return 'boolean';
105
- case 'number':
106
- return 'number';
107
- case 'integer':
108
- return 'number';
109
- case 'file':
110
- return 'File';
111
- case 'any':
112
- return 'any';
113
- case 'string':
114
- if (format.contains('binary')) {
115
- return 'Blob | Buffer';
116
- }
117
- else {
118
- return 'string';
119
- }
120
- case 'array':
121
- return `ReadonlyArray<${Property.toJsType(itemTpe)}>`;
122
- default:
123
- return NameUtils.normaliseClassname(tpe);
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' || option(def.properties).exists(p => Object.keys(p).length > 0)) {
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' || option(def.properties).exists(p => Object.keys(p).length > 0)) {
18
- return SchemaObject.fromDefinition(name, def, schemasTypes, options);
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,26 +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
- static fromDefinition(name, def, schemasTypes, options) {
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
- const properties = option(def.properties)
86
- .map(props => Collection.from(Object.keys(props)))
131
+ .map(arr => typeof arr === 'boolean' ? Nil : Collection.from(arr))
87
132
  .getOrElseValue(Nil)
88
- .map(propName => {
89
- const property = Property.fromDefinition(propName, def.properties[propName], schemasTypes, options);
90
- return property.copy({
91
- required: explicitlyRequired.exists(c => c.contains(propName)) ? true : property.required
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
+ });
92
148
  });
93
149
  });
94
- 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);
95
151
  }
96
152
  get normalName() {
97
153
  return NameUtils.normaliseClassname(this.name);
@@ -2,7 +2,7 @@
2
2
  export class <%= schema.normalName %>Dto {
3
3
 
4
4
  /**
5
- <%_ schema.properties.foreach(p => { -%>
5
+ <%_ schema.propsIncludingInherited().foreach(p => { -%>
6
6
  * @param <%= p.name %> <%- p.description.getOrElseValue('') %>
7
7
  <%_ }); -%>
8
8
  */
@@ -1,5 +1,5 @@
1
1
  <%_ if (schema.schemaType === 'object') { -%>
2
- export interface <%= schema.normalName %> {
2
+ export interface <%= schema.normalName %><%= schema.parentsString%> {
3
3
  <%_ schema.properties.foreach(p => { -%>
4
4
  <%_ p.description.foreach(d => { -%>
5
5
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@penkov/swagger-code-gen",
3
- "version": "1.8.10",
3
+ "version": "1.9.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "generate-client": "./dist/cli.mjs"