@penkov/swagger-code-gen 1.6.8 → 1.6.9

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/method.js CHANGED
@@ -1,8 +1,9 @@
1
- import { Collection, HashMap, HashSet, identity, option } from 'scats';
1
+ import { Collection, HashMap, HashSet, identity, Nil, option } from 'scats';
2
2
  import { Property } from './property.js';
3
3
  import { Parameter } from './parameter.js';
4
4
  import { SchemaFactory } from './schemas.js';
5
5
  const sortByIn = HashMap.of(['path', 0], ['query', 1], ['header', 2], ['cookie', 3], ['body', 4]);
6
+ const supportedBodyMimeTypes = HashMap.of(['application/json', 'Json'], ['application/x-www-form-urlencoded', 'Form'], ['multipart/form-data', 'File'], ['application/octet-stream', 'Binary']);
6
7
  export class Method {
7
8
  constructor(path, method, def, schemasTypes, options) {
8
9
  this.path = path;
@@ -35,28 +36,30 @@ export class Method {
35
36
  return p;
36
37
  }
37
38
  });
38
- this.body = option(def.requestBody).flatMap(body => option(body.content))
39
- .flatMap(body => {
39
+ this.body = option(def.requestBody)
40
+ .flatMap(body => option(body.content))
41
+ .map(body => {
40
42
  const bodyRequired = option(def.requestBody.required).contains(true);
41
43
  const mimeTypes = Collection.from(Object.keys(body));
42
- return mimeTypes
43
- .find(_ => _ === 'application/json')
44
- .orElseValue(mimeTypes.headOption)
45
- .map(mt => {
44
+ const supportedMimeTypes = mimeTypes.filter(_ => supportedBodyMimeTypes.containsKey(_));
45
+ return supportedMimeTypes.map(mt => {
46
46
  const bodySchemaDef = body[mt].schema;
47
- const res = SchemaFactory.build('body', bodySchemaDef, schemasTypes, options);
47
+ let res = SchemaFactory.build('body', bodySchemaDef, schemasTypes, options);
48
48
  if (res.schemaType === 'property') {
49
49
  const bProperty = res;
50
- return bProperty.copy({
50
+ res = bProperty.copy({
51
51
  nullable: bProperty.referencesObject ? option(bodySchemaDef['nullable']).contains(true) : bProperty.nullable,
52
52
  required: bodyRequired
53
53
  });
54
54
  }
55
- else {
56
- return res;
57
- }
55
+ return {
56
+ body: res,
57
+ mimeType: mt,
58
+ suffix: supportedMimeTypes.size > 1 ? supportedBodyMimeTypes.get(mt).getOrElseValue(mt) : ''
59
+ };
58
60
  });
59
- });
61
+ })
62
+ .getOrElseValue(Nil);
60
63
  this.bodyDescription = option(def.requestBody).flatMap(body => option(body.description));
61
64
  const statusCodes = Collection.from(Object.keys(def.responses))
62
65
  .map(x => parseInt(x));
@@ -83,7 +86,7 @@ export class Method {
83
86
  asProperty: Property.fromDefinition('UNKNOWN', { type: 'any' }, schemasTypes, options),
84
87
  responseType: 'any'
85
88
  }));
86
- this.wrapParamsInObject = this.parameters.size > 2 || (this.body.isDefined) && this.parameters.nonEmpty;
89
+ this.wrapParamsInObject = this.parameters.size > 2 || (this.body.nonEmpty) && this.parameters.nonEmpty;
87
90
  }
88
91
  get endpointName() {
89
92
  return this.operationId.getOrElse(() => `${this.method}${Method.pathToName(this.path)}`);
package/dist/parameter.js CHANGED
@@ -3,11 +3,13 @@ import { Collection, identity, none, option } from 'scats';
3
3
  import { Method } from './method.js';
4
4
  import { Property } from './property.js';
5
5
  export class Parameter {
6
- constructor(name, uniqueName, inValue, jsType, required, defaultValue, description) {
6
+ constructor(name, uniqueName, inValue, jsType, required, isArray, defaultValue, description) {
7
7
  this.name = name;
8
8
  this.uniqueName = uniqueName;
9
+ this.inValue = inValue;
9
10
  this.jsType = jsType;
10
11
  this.required = required;
12
+ this.isArray = isArray;
11
13
  this.defaultValue = defaultValue;
12
14
  this.description = description;
13
15
  this.in = inValue;
@@ -51,7 +53,8 @@ export class Parameter {
51
53
  throw new Error('Unknown schema type');
52
54
  }
53
55
  const required = option(def.required).exists(identity) || defaultValue.nonEmpty;
54
- return new Parameter(name, name, inValue, jsType, required, defaultValue, desc);
56
+ const isArray = def.schema.type === 'array';
57
+ return new Parameter(name, name, inValue, jsType, required, isArray, defaultValue, desc);
55
58
  }
56
59
  static toJSName(path) {
57
60
  const tokens = Collection.from(path.split(/\W/)).filter(t => t.length > 0);
@@ -60,6 +63,6 @@ export class Parameter {
60
63
  }).mkString();
61
64
  }
62
65
  copy(p) {
63
- return new Parameter(option(p.name).getOrElseValue(this.name), option(p.uniqueName).getOrElseValue(this.uniqueName), option(p.in).getOrElseValue(this.in), option(p.jsType).getOrElseValue(this.jsType), option(p.required).getOrElseValue(this.required), option(p.defaultValue).getOrElseValue(this.defaultValue), option(p.description).getOrElseValue(this.description));
66
+ return new Parameter(option(p.name).getOrElseValue(this.name), option(p.uniqueName).getOrElseValue(this.uniqueName), option(p.in).getOrElseValue(this.in), option(p.jsType).getOrElseValue(this.jsType), option(p.required).getOrElseValue(this.required), option(p.isArray).getOrElseValue(this.isArray), option(p.defaultValue).getOrElseValue(this.defaultValue), option(p.description).getOrElseValue(this.description));
64
67
  }
65
68
  }
package/dist/property.js CHANGED
@@ -1,9 +1,10 @@
1
- import { Collection, option } from 'scats';
1
+ import { Collection, none, option } from 'scats';
2
2
  const SCHEMA_PREFIX = '#/components/schemas/';
3
3
  export class Property {
4
- constructor(name, type, description, defaultValue, nullable, required, items, referencesObject, itemReferencesObject) {
4
+ constructor(name, type, format, description, defaultValue, nullable, required, items, referencesObject, itemReferencesObject, enumValues) {
5
5
  this.name = name;
6
6
  this.type = type;
7
+ this.format = format;
7
8
  this.description = description;
8
9
  this.defaultValue = defaultValue;
9
10
  this.nullable = nullable;
@@ -11,10 +12,11 @@ export class Property {
11
12
  this.items = items;
12
13
  this.referencesObject = referencesObject;
13
14
  this.itemReferencesObject = itemReferencesObject;
15
+ this.enumValues = enumValues;
14
16
  this.schemaType = 'property';
15
17
  }
16
18
  copy(p) {
17
- return new Property(option(p.name).getOrElseValue(this.name), option(p.type).getOrElseValue(this.type), option(p.description).getOrElseValue(this.description), option(p.defaultValue).getOrElseValue(this.defaultValue), option(p.nullable).getOrElseValue(this.nullable), option(p.required).getOrElseValue(this.required), option(p.items).getOrElseValue(this.items), option(p.referencesObject).getOrElseValue(this.referencesObject), option(p.itemReferencesObject).getOrElseValue(this.itemReferencesObject));
19
+ return new Property(option(p.name).getOrElseValue(this.name), option(p.type).getOrElseValue(this.type), option(p.format).getOrElseValue(this.format), option(p.description).getOrElseValue(this.description), option(p.defaultValue).getOrElseValue(this.defaultValue), option(p.nullable).getOrElseValue(this.nullable), option(p.required).getOrElseValue(this.required), option(p.items).getOrElseValue(this.items), option(p.referencesObject).getOrElseValue(this.referencesObject), option(p.itemReferencesObject).getOrElseValue(this.itemReferencesObject), option(p.enumValues).getOrElseValue(this.enumValues));
18
20
  }
19
21
  static fromDefinition(name, definition, schemas, options) {
20
22
  const referencesObject = option(definition.$ref)
@@ -59,21 +61,41 @@ export class Property {
59
61
  .map(ref => ref.substring(SCHEMA_PREFIX.length))
60
62
  .orElseValue(option(oneOfItem.type))).mkString(' | ')))
61
63
  .getOrElseValue('any');
62
- return new Property(name, type, description, null, nullable, required, items, referencesObject, itemReferencesObject);
64
+ const enumValues = option(definition.enum).map(x => Collection.from(x));
65
+ return new Property(name, type, option(definition.format), description, null, nullable, required, items, referencesObject, itemReferencesObject, enumValues);
63
66
  }
64
67
  get jsType() {
65
- let res = Property.toJsType(this.type, this.items);
68
+ let res = Property.toJsType(this.type, this.items, this.format);
66
69
  if (this.nullable) {
67
70
  res = res + ' | null';
68
71
  }
72
+ else if (this.enumValues.exists(x => x.nonEmpty)) {
73
+ res = this.enumValues.get
74
+ .map(enumValue => {
75
+ if (this.type === 'string') {
76
+ return `'${enumValue}'`;
77
+ }
78
+ else {
79
+ return enumValue;
80
+ }
81
+ })
82
+ .mkString(' | ');
83
+ }
69
84
  return res;
70
85
  }
71
86
  get isArray() {
72
87
  return this.type === 'array';
73
88
  }
74
- static toJsType(tpe, itemTpe = 'any') {
89
+ static toJsType(tpe, itemTpe = 'any', format = none) {
75
90
  switch (tpe) {
76
91
  case 'integer': return 'number';
92
+ case 'string':
93
+ if (format.contains('binary')) {
94
+ return 'Blob | Buffer';
95
+ }
96
+ else {
97
+ return 'string';
98
+ }
77
99
  case 'array': return `ReadonlyArray<${Property.toJsType(itemTpe)}>`;
78
100
  default: return tpe;
79
101
  }
@@ -104,7 +126,19 @@ export class Property {
104
126
  }
105
127
  }
106
128
  else {
107
- const jsType = Property.toJsType(this.type, this.items);
129
+ let jsType = Property.toJsType(this.type, this.items, this.format);
130
+ if (this.enumValues.exists(x => x.nonEmpty)) {
131
+ jsType = this.enumValues.get
132
+ .map(enumValue => {
133
+ if (this.type === 'string') {
134
+ return `'${enumValue}'`;
135
+ }
136
+ else {
137
+ return enumValue;
138
+ }
139
+ })
140
+ .mkString(' | ');
141
+ }
108
142
  return !this.nullable && this.required ? this.jsType : `Option<${jsType}>`;
109
143
  }
110
144
  }
package/dist/renderer.js CHANGED
@@ -2,11 +2,13 @@ import * as ejs from 'ejs';
2
2
  import * as fs from 'fs';
3
3
  import path, { dirname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
+ import * as scatsLib from 'scats';
5
6
  const __filename = fileURLToPath(import.meta.url);
6
7
  const __dirname = dirname(__filename);
7
8
  export class Renderer {
8
9
  async renderToFile(schemas, methods, enableScats, targetNode, file) {
9
10
  const view = await ejs.renderFile(path.resolve(__dirname, 'templates/index.ejs'), {
11
+ scatsLib: scatsLib,
10
12
  schemas: schemas,
11
13
  methods: methods,
12
14
  scats: enableScats,
@@ -1,3 +1,4 @@
1
+ /* eslint-disable */
1
2
  /*********************************************************
2
3
  *********************************************************
3
4
  *********************************************************
@@ -11,7 +12,7 @@
11
12
  *********************************************************
12
13
  *********************************************************/
13
14
  <% if (targetNode) {%>
14
- import fetch, {Request, Response} from 'node-fetch';
15
+ import fetch, {Request, Response, BodyInit} from 'node-fetch';
15
16
  <% } %>
16
17
  <% if (scats) {%>
17
18
  import {option, Option, Collection, Try, TryLike, none} from 'scats';
@@ -35,6 +36,38 @@ export const defaultRequestOptions: RequestOptions = {
35
36
 
36
37
  const defReqOpts = () => defaultRequestOptions;
37
38
 
39
+
40
+ function valueToString(value: any): string {
41
+ if (typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number') {
42
+ return value.toString();
43
+ } else {
44
+ return JSON.stringify(value);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Helper to serialize data for 'multipart/form-data'
50
+ */
51
+ function objectToForm(o: object): FormData {
52
+ const res = new FormData();
53
+ Object.keys(o)
54
+ .filter(k => o[k] !== undefined && o[k] !== null)
55
+ .forEach(k => {
56
+ res.append(k, valueToString(o[k]));
57
+ });
58
+ return res;
59
+ }
60
+
61
+ /**
62
+ * Helper to serialize data for 'application/x-www-form-urlencoded'
63
+ */
64
+ function objectToFormWwwEncoded(o: object): string {
65
+ return Object.keys(o)
66
+ .filter(k => o[k] !== undefined && o[k] !== null)
67
+ .map(k => `${k}=${encodeURIComponent(valueToString(o[k]))}`)
68
+ .join('&');
69
+ }
70
+
38
71
  async function requestImpl<T>(request: Request, requestOptions: RequestOptions): Promise<T> {
39
72
  const preProcessed = requestOptions.preProcessRequest ? await requestOptions.preProcessRequest(request) : request;
40
73
  const resp = await fetch(preProcessed);
@@ -61,9 +94,22 @@ async function requestImpl<T>(request: Request, requestOptions: RequestOptions):
61
94
 
62
95
 
63
96
 
64
- <% methods.foreach(method => { %>
65
- <%- include('method.ejs', {method: method}); %>
66
- <% }); %>
97
+ <%
98
+ methods.foreach(method => {
99
+ if (method.body.nonEmpty) {
100
+ method.body.foreach(body => {
101
+ %>
102
+ <%- include('method.ejs', {method: method, body: scatsLib.some(body)}); %>
103
+ <%
104
+ });
105
+ } else {
106
+ %>
107
+ <%- include('method.ejs', {method: method, body: scatsLib.none }); %>
108
+ <%
109
+ }
110
+ });
111
+ %>
112
+
67
113
 
68
114
 
69
115
  <% if (scats) {%>
@@ -71,7 +117,7 @@ async function requestImpl<T>(request: Request, requestOptions: RequestOptions):
71
117
  <%- include('scats-schema.ejs', {schema: schema}); %>
72
118
  <% }); %>
73
119
 
74
- <%- include('scats-method.ejs'); %>
120
+ <%- include('scats-api-client.ejs'); %>
75
121
 
76
122
  <% } %>
77
123
 
@@ -12,13 +12,13 @@
12
12
  <% method.parameters.foreach(p => { -%>
13
13
  * @param {<%= p.jsType %>} <%= method.wrapParamsInObject ? 'params.' : '' %><%= p.uniqueName %> <%= p.description.getOrElseValue('')%>
14
14
  <% }); -%>
15
- <%_ if (method.body.nonEmpty) { -%>
16
- * @param {<%= method.body.get.jsType %>} body <%= method.bodyDescription.getOrElseValue('') %>
15
+ <%_ if (body.nonEmpty) { -%>
16
+ * @param {<%= body.get.body.jsType %>} body <%= method.bodyDescription.getOrElseValue('') %>
17
17
  <%_ } -%>
18
18
  * @param {RequestOptions} requestOptions Additional request params
19
19
  * @return {<%= method.response.responseType %>} <%= method.response.description %>
20
20
  */
21
- export async function <%= method.endpointName %>(
21
+ export async function <%= method.endpointName %><%= body.map(b => b.suffix).getOrElseValue('') %>(
22
22
  <%_ if (!method.wrapParamsInObject) { -%>
23
23
  <%_ method.parameters.foreach(p => { -%>
24
24
  <%= p.uniqueName %><%= p.required ? '' : '?'%>: <%- p.jsType %><%- p.defaultValue.map(x => ` = ${x}`).getOrElseValue('') %>,
@@ -30,8 +30,8 @@ export async function <%= method.endpointName %>(
30
30
  <%_ }) -%>
31
31
  },
32
32
  <%_ } -%>
33
- <%_ if (method.body.nonEmpty) { -%>
34
- body<%= method.body.get.required ? '' : '?'%>: <%- method.body.get.jsType %>,
33
+ <%_ if (body.nonEmpty) { -%>
34
+ body<%= body.get.body.required ? '' : '?'%>: <%- body.get.body.jsType %>,
35
35
  <%_ } -%>
36
36
  requestOptions: RequestOptions = defReqOpts()
37
37
  ): Promise<<%- method.response.responseType %>> {
@@ -47,7 +47,11 @@ export async function <%= method.endpointName %>(
47
47
  <%_ }) -%>
48
48
  <%_ method.parameters.filter(x => x.in === 'query' && !x.required).foreach(p => { -%>
49
49
  if (!!<%= method.wrapParamsInObject ? 'params.' : '' %><%= p.uniqueName %>) {
50
- <%_ if (p.jsType === 'string') { -%>
50
+ <%_ if (p.isArray) { -%>
51
+ <%= method.wrapParamsInObject ? 'params.' : '' %><%= p.uniqueName %>.forEach(p => {
52
+ queryParams.push(`<%= p.name %>=${p}`);
53
+ });
54
+ <%_ } else if (p.jsType === 'string') { -%>
51
55
  queryParams.push(`<%= p.name %>=${encodeURIComponent(<%= method.wrapParamsInObject ? 'params.' : '' %><%= p.uniqueName %>)}`);
52
56
  <%_ } else { -%>
53
57
  queryParams.push(`<%= p.name %>=${<%= method.wrapParamsInObject ? 'params.' : '' %><%= p.uniqueName %>}`);
@@ -58,9 +62,19 @@ export async function <%= method.endpointName %>(
58
62
  query = '?' + queryParams.join('&');
59
63
  }
60
64
  <%_ } -%>
65
+ let bodySerialised: BodyInit | null = null;
66
+ <%_ if (body.nonEmpty && body.get.mimeType === 'application/json') {%>
67
+ bodySerialised = JSON.stringify(body);
68
+ <%_ } else if (body.nonEmpty && body.get.mimeType === 'application/x-www-form-urlencoded') { -%>
69
+ bodySerialised = objectToFormWwwEncoded(body);
70
+ <%_ } else if (body.nonEmpty && body.get.mimeType === 'multipart/form-data') { -%>
71
+ bodySerialised = objectToForm(body);
72
+ <%_ } else if (body.nonEmpty && body.get.mimeType === 'application/octet-stream') { -%>
73
+ bodySerialised = body;
74
+ <%_ } -%>
61
75
  const request = new Request(`${requestOptions.apiPrefix}<%- method.pathWithSubstitutions %>${query}`, {
62
76
  method: '<%= method.method %>',
63
- body: <%= method.body.map(() => 'JSON.stringify(body)').getOrElseValue('undefined') %>,
77
+ body: bodySerialised,
64
78
  headers: {
65
79
  ...requestOptions.headers || {},
66
80
  <%_ if (method.parameters.filter(x => x.in === 'header').nonEmpty) { -%>
@@ -0,0 +1,23 @@
1
+ export class ApiClient {
2
+
3
+ constructor(private readonly requestOptions: RequestOptions = defReqOpts()) {
4
+ }
5
+
6
+
7
+ <%
8
+ methods.foreach(method => {
9
+ if (method.body.nonEmpty) {
10
+ method.body.foreach(body => {
11
+ %>
12
+ <%- include('scats-method.ejs', {method: method, body: scatsLib.some(body)}); %>
13
+ <%
14
+ });
15
+ } else {
16
+ %>
17
+ <%- include('scats-method.ejs', {method: method, body: scatsLib.none }); %>
18
+ <%
19
+ }
20
+ });
21
+ %>
22
+
23
+ }
@@ -1,11 +1,5 @@
1
- export class ApiClient {
2
1
 
3
- constructor(private readonly requestOptions: RequestOptions = defReqOpts()) {
4
- }
5
-
6
-
7
- <% methods.foreach(method => { %>
8
- async <%= method.endpointName %>(
2
+ async <%= method.endpointName %><%= body.map(b => b.suffix).getOrElseValue('') %>(
9
3
  <%_ if (!method.wrapParamsInObject) { -%>
10
4
  <%_ method.parameters.foreach(p => { -%>
11
5
  <%= p.uniqueName %>: <%- p.required && !p.nullable ? p.jsType : `Option<${p.jsType}> = none` %>,
@@ -17,13 +11,13 @@ export class ApiClient {
17
11
  <%_ }) -%>
18
12
  },
19
13
  <%_ } -%>
20
- <%_ method.body.foreach(body => { -%>
21
- body: <%- body.scatsWrapperType %>,
14
+ <%_ body.foreach(body => { -%>
15
+ body: <%- body.body.scatsWrapperType %>,
22
16
  <%_ }); -%>
23
17
  requestOptions: RequestOptions = this.requestOptions
24
18
  ): Promise<TryLike<<%- method.response.asProperty.scatsWrapperType %>>> {
25
19
  return (await Try.promise(() =>
26
- <%= method.endpointName %>(
20
+ <%= method.endpointName %><%= body.map(b => b.suffix).getOrElseValue('') %>(
27
21
  <%_ if (!method.wrapParamsInObject) { -%>
28
22
  <%_ method.parameters.foreach(p => { -%>
29
23
  <%= p.uniqueName %><%- p.required && !p.nullable ? '' : `.orUndefined` %>,
@@ -31,7 +25,7 @@ export class ApiClient {
31
25
  <%_ } else { -%>
32
26
  params,
33
27
  <%_ } -%>
34
- <%_ method.body.foreach(body => { -%>
28
+ <%_ body.map(x => x.body).foreach(body => { -%>
35
29
  <%_ if (body.schemaType === 'object') { _%>
36
30
  body.toJson,
37
31
  <%_ } else if (body.schemaType === 'property') { _%>
@@ -64,7 +58,4 @@ export class ApiClient {
64
58
  <%_ } _%>
65
59
  <%_ } _%>;
66
60
  }
67
- <% }); %>
68
-
69
61
 
70
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@penkov/swagger-code-gen",
3
- "version": "1.6.8",
3
+ "version": "1.6.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "generate-client": "./dist/cli.mjs"