@penkov/swagger-code-gen 1.13.0 → 1.15.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/method.js CHANGED
@@ -7,6 +7,12 @@ export const SHARED_BODIES_PREFIX = '#/components/requestBodies/';
7
7
  const sortByIn = HashMap.of(['path', 0], ['query', 1], ['header', 2], ['cookie', 3], ['body', 4]);
8
8
  export const supportedBodyMimeTypes = HashMap.of(['application/json', 'Json'], ['application/x-www-form-urlencoded', 'Form'], ['multipart/form-data', 'File'], ['application/octet-stream', 'Binary']);
9
9
  export class Method {
10
+ static parseModeByMimeType(mimeType) {
11
+ if (mimeType.startsWith('text/') || mimeType.includes('xml')) {
12
+ return 'text';
13
+ }
14
+ return 'json';
15
+ }
10
16
  constructor(path, method, def, schemasTypes, options, pool) {
11
17
  this.path = path;
12
18
  this.method = method;
@@ -132,10 +138,27 @@ export class Method {
132
138
  .getOrElseValue({});
133
139
  const mimeTypes = option(respDef.content)
134
140
  .map(content => Collection.from(Object.keys(content)).toMap(mimeType => [mimeType, content[mimeType]])).getOrElseValue(HashMap.empty);
135
- this.response = mimeTypes.get('application/json')
136
- .orElseValue(mimeTypes.values.headOption)
137
- .filter(p => option(p.schema).isDefined)
141
+ const responseMimeType = mimeTypes.get('application/json')
142
+ .map(_ => 'application/json')
143
+ .orElse(() => mimeTypes.keySet.headOption)
144
+ .getOrElseValue('application/json');
145
+ const responseParseMode = Method.parseModeByMimeType(responseMimeType);
146
+ this.response = mimeTypes.get(responseMimeType)
147
+ .filter(p => option(p.schema).isDefined || responseParseMode === 'text')
138
148
  .map(p => {
149
+ if (responseParseMode === 'text') {
150
+ const r = Property.fromDefinition('', '', { type: 'string' }, schemasTypes, options).copy({
151
+ nullable: false,
152
+ required: true,
153
+ });
154
+ return {
155
+ asProperty: r,
156
+ responseType: 'string',
157
+ description: respDef.description,
158
+ mimeType: responseMimeType,
159
+ parseMode: responseParseMode,
160
+ };
161
+ }
139
162
  if (p.schema.type === 'object' && p.schema['properties'] && Object.keys(p.schema['properties']).length > 0) {
140
163
  const inPlaceObject = NameUtils.normaliseClassname(def.operationId + 'Response$' + method);
141
164
  const r = Property.fromDefinition(inPlaceObject, '', {
@@ -149,7 +172,9 @@ export class Method {
149
172
  asProperty: r,
150
173
  responseType: inPlaceObject,
151
174
  description: respDef.description,
152
- inPlace: p.schema
175
+ inPlace: p.schema,
176
+ mimeType: responseMimeType,
177
+ parseMode: responseParseMode,
153
178
  };
154
179
  }
155
180
  else {
@@ -161,12 +186,16 @@ export class Method {
161
186
  asProperty: r,
162
187
  responseType: r.jsType,
163
188
  description: respDef.description,
189
+ mimeType: responseMimeType,
190
+ parseMode: responseParseMode,
164
191
  };
165
192
  }
166
193
  })
167
194
  .getOrElseValue(({
168
195
  asProperty: Property.fromDefinition('', 'UNKNOWN', { type: 'any' }, schemasTypes, options),
169
196
  responseType: 'any',
197
+ mimeType: responseMimeType,
198
+ parseMode: responseParseMode,
170
199
  }));
171
200
  this.wrapParamsInObject = this.parameters.size > 2 || (this.body.nonEmpty) && this.parameters.nonEmpty;
172
201
  }
@@ -86,20 +86,52 @@ function objectToFormWwwEncoded(o: Record<string, any>): string {
86
86
  .join('&');
87
87
  }
88
88
 
89
- async function requestImpl<T>(request: Request, requestOptions: RequestOptions): Promise<T> {
89
+ type ResponseParseMode = 'json' | 'text';
90
+
91
+ function normaliseCharset(charset: string): string {
92
+ const normalized = charset.trim().toLowerCase();
93
+ if (normalized === 'cp1251') {
94
+ return 'windows-1251';
95
+ }
96
+ return normalized;
97
+ }
98
+
99
+ function extractCharset(contentType: string | null): string {
100
+ if (contentType == null) {
101
+ return 'utf-8';
102
+ }
103
+ const match = contentType.match(/charset\s*=\s*("?)([^";\s]+)\1/i);
104
+ return normaliseCharset(match?.[2] || 'utf-8');
105
+ }
106
+
107
+ async function readResponseText(response: Response): Promise<string> {
108
+ const payload = await response.arrayBuffer();
109
+ const charset = extractCharset(response.headers.get('content-type'));
110
+ try {
111
+ return new TextDecoder(charset).decode(payload);
112
+ } catch {
113
+ return new TextDecoder('utf-8').decode(payload);
114
+ }
115
+ }
116
+
117
+ async function requestImpl<T>(request: Request, requestOptions: RequestOptions, parseMode: ResponseParseMode = 'json'): Promise<T> {
90
118
  const preProcessed = requestOptions.preProcessRequest ? await requestOptions.preProcessRequest(request) : request;
91
119
  const resp = await fetch(preProcessed);
92
120
  const postProcessed = requestOptions.postProcessResponse ? await requestOptions.postProcessResponse(preProcessed, resp) : resp;
93
121
  if (postProcessed.ok) {
122
+ if (parseMode === 'text') {
123
+ return (await readResponseText(postProcessed)) as T;
124
+ }
125
+
94
126
  let json: any = null;
95
127
  if (postProcessed.headers.has('content-length')) {
96
128
  const ctLent = postProcessed.headers.get('content-length');
97
129
  const ct = ctLent != null ? parseInt(ctLent): 0;
98
130
  if (ct > 0) {
99
- json = await postProcessed.json()
131
+ json = JSON.parse((await readResponseText(postProcessed)).replace(/^\uFEFF/, ''))
100
132
  }
101
133
  } else {
102
- json = await postProcessed.json()
134
+ json = JSON.parse((await readResponseText(postProcessed)).replace(/^\uFEFF/, ''))
103
135
  }
104
136
  return json as T;
105
137
  } else {
@@ -139,5 +171,3 @@ methods.foreach(method => {
139
171
  <%- include('scats-api-client.ejs'); %>
140
172
 
141
173
  <% } %>
142
-
143
-
@@ -76,7 +76,7 @@ export async function <%= method.endpointName %><%= body.map(b => b.suffix).getO
76
76
 
77
77
  const headers: HeadersInit = {
78
78
  ...requestOptions.headers || {},
79
- 'Accept': 'application/json',
79
+ 'Accept': '<%= method.response.mimeType %>',
80
80
  <%_ if (body.nonEmpty && body.get.mimeType !== 'multipart/form-data') {%>
81
81
  'Content-Type': '<%= body.get.mimeType %>',
82
82
  <%_ } -%>
@@ -97,5 +97,5 @@ export async function <%= method.endpointName %><%= body.map(b => b.suffix).getO
97
97
  signal: requestOptions.signal,
98
98
  headers: headers
99
99
  });
100
- return requestImpl<<%- method.response.responseType %>>(request, requestOptions);
100
+ return requestImpl<<%- method.response.responseType %>>(request, requestOptions, '<%= method.response.parseMode %>');
101
101
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@penkov/swagger-code-gen",
3
- "version": "1.13.0",
3
+ "version": "1.15.0",
4
4
  "type": "module",
5
5
  "bin": {
6
- "generate-client": "./dist/cli.mjs"
6
+ "generate-client": "dist/cli.mjs"
7
7
  },
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/papirosko/swagger-code-gen"
10
+ "url": "git+https://github.com/papirosko/swagger-code-gen.git"
11
11
  },
12
12
  "keywords": [
13
13
  "swagger",