@sdk-it/dart 0.16.0 → 0.18.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/README.md +82 -0
- package/dist/index.js +603 -100
- package/dist/index.js.map +4 -4
- package/dist/lib/dart-emitter.d.ts +3 -5
- package/dist/lib/dart-emitter.d.ts.map +1 -1
- package/dist/lib/generate.d.ts.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// packages/dart/src/lib/generate.ts
|
|
2
|
+
import { parse as partContentType } from "fast-content-type-parse";
|
|
2
3
|
import { merge as merge2 } from "lodash-es";
|
|
3
4
|
import assert2 from "node:assert";
|
|
4
5
|
import { writeFile } from "node:fs/promises";
|
|
5
6
|
import { join } from "node:path";
|
|
6
|
-
import { camelcase as
|
|
7
|
+
import { camelcase as camelcase3 } from "stringcase";
|
|
8
|
+
import yaml from "yaml";
|
|
7
9
|
import {
|
|
8
10
|
followRef as followRef2,
|
|
9
|
-
forEachOperation,
|
|
10
11
|
getFolderExportsV2,
|
|
11
12
|
isEmpty as isEmpty2,
|
|
12
13
|
isRef as isRef2,
|
|
@@ -16,10 +17,302 @@ import {
|
|
|
16
17
|
writeFiles
|
|
17
18
|
} from "@sdk-it/core";
|
|
18
19
|
|
|
20
|
+
// packages/spec/dist/lib/loaders/local-loader.js
|
|
21
|
+
import { parse } from "yaml";
|
|
22
|
+
|
|
23
|
+
// packages/spec/dist/lib/loaders/remote-loader.js
|
|
24
|
+
import { parse as parse2 } from "yaml";
|
|
25
|
+
|
|
26
|
+
// packages/spec/dist/lib/operation.js
|
|
27
|
+
import { camelcase } from "stringcase";
|
|
28
|
+
var defaults = {
|
|
29
|
+
operationId: (operation, path, method) => {
|
|
30
|
+
if (operation.operationId) {
|
|
31
|
+
return camelcase(operation.operationId);
|
|
32
|
+
}
|
|
33
|
+
const metadata = operation["x-oaiMeta"];
|
|
34
|
+
if (metadata && metadata.name) {
|
|
35
|
+
return camelcase(metadata.name);
|
|
36
|
+
}
|
|
37
|
+
return camelcase(
|
|
38
|
+
[method, ...path.replace(/[\\/\\{\\}]/g, " ").split(" ")].filter(Boolean).join(" ").trim()
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
tag: (operation, path) => {
|
|
42
|
+
return operation.tags?.[0] || determineGenericTag(path, operation);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
function forEachOperation(config, callback) {
|
|
46
|
+
const result = [];
|
|
47
|
+
for (const [path, pathItem] of Object.entries(config.spec.paths ?? {})) {
|
|
48
|
+
const { parameters = [], ...methods } = pathItem;
|
|
49
|
+
const fixedPath = path.replace(/:([^/]+)/g, "{$1}");
|
|
50
|
+
for (const [method, operation] of Object.entries(methods)) {
|
|
51
|
+
const formatOperationId = config.operationId ?? defaults.operationId;
|
|
52
|
+
const formatTag = config.tag ?? defaults.tag;
|
|
53
|
+
const operationName = formatOperationId(operation, fixedPath, method);
|
|
54
|
+
const operationTag = formatTag(operation, fixedPath);
|
|
55
|
+
const metadata = operation["x-oaiMeta"] ?? {};
|
|
56
|
+
result.push(
|
|
57
|
+
callback(
|
|
58
|
+
{
|
|
59
|
+
name: metadata.name,
|
|
60
|
+
method,
|
|
61
|
+
path: fixedPath,
|
|
62
|
+
groupName: operationTag,
|
|
63
|
+
tag: operationTag
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
...operation,
|
|
67
|
+
parameters: [...parameters, ...operation.parameters ?? []],
|
|
68
|
+
operationId: operationName
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
var reservedKeywords = /* @__PURE__ */ new Set([
|
|
77
|
+
"abstract",
|
|
78
|
+
"arguments",
|
|
79
|
+
"await",
|
|
80
|
+
"boolean",
|
|
81
|
+
"break",
|
|
82
|
+
"byte",
|
|
83
|
+
"case",
|
|
84
|
+
"catch",
|
|
85
|
+
"char",
|
|
86
|
+
"class",
|
|
87
|
+
"const",
|
|
88
|
+
"continue",
|
|
89
|
+
"debugger",
|
|
90
|
+
"default",
|
|
91
|
+
"delete",
|
|
92
|
+
"do",
|
|
93
|
+
"double",
|
|
94
|
+
"else",
|
|
95
|
+
"enum",
|
|
96
|
+
"eval",
|
|
97
|
+
"export",
|
|
98
|
+
"extends",
|
|
99
|
+
"false",
|
|
100
|
+
"final",
|
|
101
|
+
"finally",
|
|
102
|
+
"float",
|
|
103
|
+
"for",
|
|
104
|
+
"function",
|
|
105
|
+
"goto",
|
|
106
|
+
"if",
|
|
107
|
+
"implements",
|
|
108
|
+
"import",
|
|
109
|
+
"in",
|
|
110
|
+
"instanceof",
|
|
111
|
+
"int",
|
|
112
|
+
"interface",
|
|
113
|
+
"let",
|
|
114
|
+
"long",
|
|
115
|
+
"native",
|
|
116
|
+
"new",
|
|
117
|
+
"null",
|
|
118
|
+
"package",
|
|
119
|
+
"private",
|
|
120
|
+
"protected",
|
|
121
|
+
"public",
|
|
122
|
+
"return",
|
|
123
|
+
"short",
|
|
124
|
+
"static",
|
|
125
|
+
"super",
|
|
126
|
+
"switch",
|
|
127
|
+
"synchronized",
|
|
128
|
+
"this",
|
|
129
|
+
"throw",
|
|
130
|
+
"throws",
|
|
131
|
+
"transient",
|
|
132
|
+
"true",
|
|
133
|
+
"try",
|
|
134
|
+
"typeof",
|
|
135
|
+
"var",
|
|
136
|
+
"void",
|
|
137
|
+
"volatile",
|
|
138
|
+
"while",
|
|
139
|
+
"with",
|
|
140
|
+
"yield",
|
|
141
|
+
// Potentially problematic identifiers / Common Verbs used as tags
|
|
142
|
+
"object",
|
|
143
|
+
"string",
|
|
144
|
+
"number",
|
|
145
|
+
"any",
|
|
146
|
+
"unknown",
|
|
147
|
+
"never",
|
|
148
|
+
"get",
|
|
149
|
+
"list",
|
|
150
|
+
"create",
|
|
151
|
+
"update",
|
|
152
|
+
"delete",
|
|
153
|
+
"post",
|
|
154
|
+
"put",
|
|
155
|
+
"patch",
|
|
156
|
+
"do",
|
|
157
|
+
"send",
|
|
158
|
+
"add",
|
|
159
|
+
"remove",
|
|
160
|
+
"set",
|
|
161
|
+
"find",
|
|
162
|
+
"search",
|
|
163
|
+
"check",
|
|
164
|
+
"make"
|
|
165
|
+
// Added make, check
|
|
166
|
+
]);
|
|
167
|
+
function sanitizeTag(camelCasedTag) {
|
|
168
|
+
if (/^\d/.test(camelCasedTag)) {
|
|
169
|
+
return `_${camelCasedTag}`;
|
|
170
|
+
}
|
|
171
|
+
return reservedKeywords.has(camelCasedTag) ? `${camelCasedTag}_` : camelCasedTag;
|
|
172
|
+
}
|
|
173
|
+
function determineGenericTag(pathString, operation) {
|
|
174
|
+
const operationId = operation.operationId || "";
|
|
175
|
+
const VERSION_REGEX = /^[vV]\d+$/;
|
|
176
|
+
const commonVerbs = /* @__PURE__ */ new Set([
|
|
177
|
+
// Verbs to potentially strip from operationId prefix
|
|
178
|
+
"get",
|
|
179
|
+
"list",
|
|
180
|
+
"create",
|
|
181
|
+
"update",
|
|
182
|
+
"delete",
|
|
183
|
+
"post",
|
|
184
|
+
"put",
|
|
185
|
+
"patch",
|
|
186
|
+
"do",
|
|
187
|
+
"send",
|
|
188
|
+
"add",
|
|
189
|
+
"remove",
|
|
190
|
+
"set",
|
|
191
|
+
"find",
|
|
192
|
+
"search",
|
|
193
|
+
"check",
|
|
194
|
+
"make"
|
|
195
|
+
// Added make
|
|
196
|
+
]);
|
|
197
|
+
const segments = pathString.split("/").filter(Boolean);
|
|
198
|
+
const potentialCandidates = segments.filter(
|
|
199
|
+
(segment) => segment && !segment.startsWith("{") && !segment.endsWith("}") && !VERSION_REGEX.test(segment)
|
|
200
|
+
);
|
|
201
|
+
for (let i = potentialCandidates.length - 1; i >= 0; i--) {
|
|
202
|
+
const segment = potentialCandidates[i];
|
|
203
|
+
if (!segment.startsWith("@")) {
|
|
204
|
+
return sanitizeTag(camelcase(segment));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const canFallbackToPathSegment = potentialCandidates.length > 0;
|
|
208
|
+
if (operationId) {
|
|
209
|
+
const lowerOpId = operationId.toLowerCase();
|
|
210
|
+
const parts = operationId.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").replace(/([a-zA-Z])(\d)/g, "$1_$2").replace(/(\d)([a-zA-Z])/g, "$1_$2").toLowerCase().split(/[_-\s]+/);
|
|
211
|
+
const validParts = parts.filter(Boolean);
|
|
212
|
+
if (commonVerbs.has(lowerOpId) && validParts.length === 1 && canFallbackToPathSegment) {
|
|
213
|
+
} else if (validParts.length > 0) {
|
|
214
|
+
const firstPart = validParts[0];
|
|
215
|
+
const isFirstPartVerb = commonVerbs.has(firstPart);
|
|
216
|
+
if (isFirstPartVerb && validParts.length > 1) {
|
|
217
|
+
const verbPrefixLength = firstPart.length;
|
|
218
|
+
let nextPartStartIndex = -1;
|
|
219
|
+
if (operationId.length > verbPrefixLength) {
|
|
220
|
+
const charAfterPrefix = operationId[verbPrefixLength];
|
|
221
|
+
if (charAfterPrefix >= "A" && charAfterPrefix <= "Z") {
|
|
222
|
+
nextPartStartIndex = verbPrefixLength;
|
|
223
|
+
} else if (charAfterPrefix >= "0" && charAfterPrefix <= "9") {
|
|
224
|
+
nextPartStartIndex = verbPrefixLength;
|
|
225
|
+
} else if (["_", "-"].includes(charAfterPrefix)) {
|
|
226
|
+
nextPartStartIndex = verbPrefixLength + 1;
|
|
227
|
+
} else {
|
|
228
|
+
const match = operationId.substring(verbPrefixLength).match(/[A-Z0-9]/);
|
|
229
|
+
if (match && match.index !== void 0) {
|
|
230
|
+
nextPartStartIndex = verbPrefixLength + match.index;
|
|
231
|
+
}
|
|
232
|
+
if (nextPartStartIndex === -1 && operationId.length > verbPrefixLength) {
|
|
233
|
+
nextPartStartIndex = verbPrefixLength;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (nextPartStartIndex !== -1 && nextPartStartIndex < operationId.length) {
|
|
238
|
+
const remainingOriginalSubstring = operationId.substring(nextPartStartIndex);
|
|
239
|
+
const potentialTag = camelcase(remainingOriginalSubstring);
|
|
240
|
+
if (potentialTag) {
|
|
241
|
+
return sanitizeTag(potentialTag);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const potentialTagJoined = camelcase(validParts.slice(1).join("_"));
|
|
245
|
+
if (potentialTagJoined) {
|
|
246
|
+
return sanitizeTag(potentialTagJoined);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const potentialTagFull = camelcase(operationId);
|
|
250
|
+
if (potentialTagFull) {
|
|
251
|
+
const isResultSingleVerb = validParts.length === 1 && isFirstPartVerb;
|
|
252
|
+
if (!(isResultSingleVerb && canFallbackToPathSegment)) {
|
|
253
|
+
if (potentialTagFull.length > 0) {
|
|
254
|
+
return sanitizeTag(potentialTagFull);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const firstPartCamel = camelcase(firstPart);
|
|
259
|
+
if (firstPartCamel) {
|
|
260
|
+
const isFirstPartCamelVerb = commonVerbs.has(firstPartCamel);
|
|
261
|
+
if (!isFirstPartCamelVerb || validParts.length === 1 || !canFallbackToPathSegment) {
|
|
262
|
+
return sanitizeTag(firstPartCamel);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (isFirstPartVerb && validParts.length > 1 && validParts[1] && canFallbackToPathSegment) {
|
|
266
|
+
const secondPartCamel = camelcase(validParts[1]);
|
|
267
|
+
if (secondPartCamel) {
|
|
268
|
+
return sanitizeTag(secondPartCamel);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (potentialCandidates.length > 0) {
|
|
274
|
+
let firstCandidate = potentialCandidates[0];
|
|
275
|
+
if (firstCandidate.startsWith("@")) {
|
|
276
|
+
firstCandidate = firstCandidate.substring(1);
|
|
277
|
+
}
|
|
278
|
+
if (firstCandidate) {
|
|
279
|
+
return sanitizeTag(camelcase(firstCandidate));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
console.warn(
|
|
283
|
+
`Could not determine a suitable tag for path: ${pathString}, operationId: ${operationId}. Using 'unknown'.`
|
|
284
|
+
);
|
|
285
|
+
return "unknown";
|
|
286
|
+
}
|
|
287
|
+
function parseJsonContentType(contentType) {
|
|
288
|
+
if (!contentType) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
let mainType = contentType.trim();
|
|
292
|
+
const semicolonIndex = mainType.indexOf(";");
|
|
293
|
+
if (semicolonIndex !== -1) {
|
|
294
|
+
mainType = mainType.substring(0, semicolonIndex).trim();
|
|
295
|
+
}
|
|
296
|
+
mainType = mainType.toLowerCase();
|
|
297
|
+
if (mainType.endsWith("/json")) {
|
|
298
|
+
return mainType.split("/")[1];
|
|
299
|
+
} else if (mainType.endsWith("+json")) {
|
|
300
|
+
return mainType.split("+")[1];
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
function isStreamingContentType(contentType) {
|
|
305
|
+
return contentType === "application/octet-stream";
|
|
306
|
+
}
|
|
307
|
+
function isSuccessStatusCode(statusCode) {
|
|
308
|
+
statusCode = Number(statusCode);
|
|
309
|
+
return statusCode >= 200 && statusCode < 300;
|
|
310
|
+
}
|
|
311
|
+
|
|
19
312
|
// packages/dart/src/lib/dart-emitter.ts
|
|
20
313
|
import { merge } from "lodash-es";
|
|
21
314
|
import assert from "node:assert";
|
|
22
|
-
import { camelcase, snakecase } from "stringcase";
|
|
315
|
+
import { camelcase as camelcase2, snakecase } from "stringcase";
|
|
23
316
|
import {
|
|
24
317
|
cleanRef,
|
|
25
318
|
followRef,
|
|
@@ -30,8 +323,11 @@ import {
|
|
|
30
323
|
pascalcase
|
|
31
324
|
} from "@sdk-it/core";
|
|
32
325
|
var formatName = (it) => {
|
|
33
|
-
const startsWithDigitPattern =
|
|
326
|
+
const startsWithDigitPattern = /^-?\d/;
|
|
34
327
|
if (typeof it === "number") {
|
|
328
|
+
if (Math.sign(it) === -1) {
|
|
329
|
+
return `$_${Math.abs(it)}`;
|
|
330
|
+
}
|
|
35
331
|
return `$${it}`;
|
|
36
332
|
}
|
|
37
333
|
if (it === "default") {
|
|
@@ -39,7 +335,12 @@ var formatName = (it) => {
|
|
|
39
335
|
}
|
|
40
336
|
if (typeof it === "string") {
|
|
41
337
|
if (startsWithDigitPattern.test(it)) {
|
|
42
|
-
|
|
338
|
+
if (typeof it === "number") {
|
|
339
|
+
if (Math.sign(it) === -1) {
|
|
340
|
+
return `$_${Math.abs(it)}`;
|
|
341
|
+
}
|
|
342
|
+
return `$${it}`;
|
|
343
|
+
}
|
|
43
344
|
}
|
|
44
345
|
let nameToFormat = it;
|
|
45
346
|
if (nameToFormat.startsWith("[")) {
|
|
@@ -87,12 +388,14 @@ var DartSerializer = class {
|
|
|
87
388
|
}
|
|
88
389
|
#object(className, schema, context) {
|
|
89
390
|
if (schema.additionalProperties) {
|
|
391
|
+
this.#emit(className, `typedef ${className} = Map<String, dynamic>;`);
|
|
90
392
|
return {
|
|
91
393
|
content: "",
|
|
92
394
|
use: "Map<String, dynamic>",
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
395
|
+
encode: "input",
|
|
396
|
+
toJson: `this.${camelcase2(context.name)}`,
|
|
397
|
+
fromJson: `json['${camelcase2(context.name)}']`,
|
|
398
|
+
matches: `json['${camelcase2(context.name)}'] is Map<String, dynamic>`
|
|
96
399
|
};
|
|
97
400
|
}
|
|
98
401
|
if (isEmpty(schema.properties)) {
|
|
@@ -118,6 +421,7 @@ var DartSerializer = class {
|
|
|
118
421
|
}
|
|
119
422
|
return {
|
|
120
423
|
content: "",
|
|
424
|
+
encode: "input.toJson()",
|
|
121
425
|
use: className,
|
|
122
426
|
toJson: `${this.#safe(context.name, context.required)}`,
|
|
123
427
|
fromJson: `${className}.fromJson(json['${context.name}'])`,
|
|
@@ -137,24 +441,24 @@ var DartSerializer = class {
|
|
|
137
441
|
required,
|
|
138
442
|
propName: [className, propName].filter(Boolean).join("_")
|
|
139
443
|
});
|
|
140
|
-
const nullable2 = !required;
|
|
444
|
+
const nullable2 = typeStr.nullable || !required;
|
|
141
445
|
const nullableSuffix = nullable2 ? "?" : "";
|
|
142
446
|
props.push(
|
|
143
|
-
`final ${typeStr.use}${nullableSuffix} ${
|
|
447
|
+
`final ${typeStr.use}${nullableSuffix} ${camelcase2(propName)};`
|
|
144
448
|
);
|
|
145
|
-
fromJsonParams.push(`${
|
|
449
|
+
fromJsonParams.push(`${camelcase2(propName)}: ${typeStr.fromJson}`);
|
|
146
450
|
toJsonProperties.push(`'${propName}': ${typeStr.toJson}`);
|
|
147
451
|
constructorParams.push(
|
|
148
|
-
`${required ? "required " : ""}this.${
|
|
452
|
+
`${required ? "required " : ""}this.${camelcase2(propName)},`
|
|
149
453
|
);
|
|
150
454
|
if (required) {
|
|
151
455
|
matches.push(`(
|
|
152
|
-
json.containsKey('${
|
|
456
|
+
json.containsKey('${camelcase2(propName)}')
|
|
153
457
|
? ${nullable2 ? `json['${propName}'] == null` : `json['${propName}'] != null`} && ${typeStr.matches}
|
|
154
458
|
: false)`);
|
|
155
459
|
} else {
|
|
156
460
|
matches.push(`(
|
|
157
|
-
json.containsKey('${
|
|
461
|
+
json.containsKey('${camelcase2(propName)}')
|
|
158
462
|
? ${nullable2 ? `json['${propName}'] == null` : `json['${propName}'] != null`} || ${typeStr.matches}
|
|
159
463
|
: true)`);
|
|
160
464
|
}
|
|
@@ -182,38 +486,55 @@ return ${matches.join(" && ")};
|
|
|
182
486
|
return {
|
|
183
487
|
use: className,
|
|
184
488
|
content,
|
|
489
|
+
encode: "input.toJson()",
|
|
185
490
|
toJson: `${this.#safe(context.name, context.required)}`,
|
|
186
|
-
fromJson: `${className}.fromJson(json['${context.name}'])`,
|
|
491
|
+
fromJson: context.name ? `${context.forJson || className}.fromJson(json['${context.name}'])` : `${context.forJson || className}.fromJson(json)`,
|
|
187
492
|
matches: `${className}.matches(json['${context.name}'])`
|
|
188
493
|
};
|
|
189
494
|
}
|
|
190
495
|
#safe(accces, required) {
|
|
191
|
-
return required ? `this.${
|
|
496
|
+
return required ? `this.${camelcase2(accces)}.toJson()` : `this.${camelcase2(accces)} != null ? this.${camelcase2(accces)}!.toJson() : null`;
|
|
192
497
|
}
|
|
193
498
|
#array(className, schema, required = false, context) {
|
|
194
|
-
|
|
195
|
-
if (!items) {
|
|
196
|
-
|
|
499
|
+
let serialized;
|
|
500
|
+
if (!schema.items) {
|
|
501
|
+
serialized = {
|
|
197
502
|
content: "",
|
|
198
503
|
use: "List<dynamic>",
|
|
199
504
|
toJson: "",
|
|
200
|
-
fromJson:
|
|
505
|
+
fromJson: `List<dynamic>.from(${context.name ? `json['${context.name}']` : `json`})})`,
|
|
201
506
|
matches: ""
|
|
202
507
|
};
|
|
508
|
+
} else {
|
|
509
|
+
const itemsType = this.handle(className, schema.items, true, context);
|
|
510
|
+
const fromJson = required ? context.name ? `(json['${context.name}'] as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
|
|
511
|
+
.map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
|
|
512
|
+
.toList()` : `(json as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
|
|
513
|
+
.map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
|
|
514
|
+
.toList()` : context.name ? `json['${context.name}'] != null
|
|
515
|
+
? (json['${context.name}'] as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
|
|
516
|
+
.map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
|
|
517
|
+
.toList()
|
|
518
|
+
: null` : `json != null
|
|
519
|
+
? (json as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
|
|
520
|
+
.map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
|
|
521
|
+
.toList()
|
|
522
|
+
: null`;
|
|
523
|
+
serialized = {
|
|
524
|
+
encode: `input.map((it) => ${itemsType.simple ? "it" : `it.toJson()`}).toList()`,
|
|
525
|
+
content: "",
|
|
526
|
+
use: `List<${itemsType.use}>`,
|
|
527
|
+
fromJson,
|
|
528
|
+
toJson: `${context.required ? `this.${camelcase2(context.name)}${itemsType.simple ? "" : ".map((it) => it.toJson()).toList()"}` : `this.${camelcase2(context.name)}!= null? this.${camelcase2(context.name)}${itemsType.simple ? "" : "!.map((it) => it.toJson()).toList()"} : null`}`,
|
|
529
|
+
matches: `json['${camelcase2(context.name)}'].every((it) => ${itemsType.matches})`
|
|
530
|
+
};
|
|
203
531
|
}
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
content: "",
|
|
207
|
-
use: `List<${itemsType.use}>`,
|
|
208
|
-
fromJson: required ? `(json['${context.name}'] as List<${itemsType.simple ? itemsType.use : "dynamic"}>).map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`}).toList()` : `json['${context.name}'] != null ? (json['${context.name}'] as List).map((it) => ${itemsType.fromJson}).toList() : null`,
|
|
209
|
-
toJson: `${context.required ? `this.${camelcase(context.name)}${itemsType.simple ? "" : ".map((it) => it.toJson()).toList()"}` : `this.${camelcase(context.name)}!= null? this.${camelcase(context.name)}${itemsType.simple ? "" : "!.map((it) => it.toJson()).toList()"} : null`}`,
|
|
210
|
-
matches: `json['${camelcase(context.name)}'].every((it) => ${itemsType.matches})`
|
|
211
|
-
};
|
|
532
|
+
return serialized;
|
|
212
533
|
}
|
|
213
534
|
/**
|
|
214
535
|
* Convert a basic type to Dart
|
|
215
536
|
*/
|
|
216
|
-
primitive(className, type, schema, context, required = false) {
|
|
537
|
+
#primitive(className, type, schema, context, required = false) {
|
|
217
538
|
switch (type) {
|
|
218
539
|
case "string":
|
|
219
540
|
return this.#string(schema, context);
|
|
@@ -224,7 +545,7 @@ return ${matches.join(" && ")};
|
|
|
224
545
|
return {
|
|
225
546
|
content: "",
|
|
226
547
|
use: "bool",
|
|
227
|
-
toJson: `${
|
|
548
|
+
toJson: `${camelcase2(context.name)}`,
|
|
228
549
|
fromJson: `json['${context.name}']`,
|
|
229
550
|
matches: `json['${context.name}'] is bool`
|
|
230
551
|
};
|
|
@@ -236,31 +557,31 @@ return ${matches.join(" && ")};
|
|
|
236
557
|
return {
|
|
237
558
|
content: "",
|
|
238
559
|
use: "Null",
|
|
239
|
-
toJson: `${
|
|
560
|
+
toJson: `${camelcase2(context.name)}`,
|
|
240
561
|
fromJson: `json['${context.name}']`
|
|
241
562
|
};
|
|
242
563
|
default:
|
|
243
564
|
return {
|
|
244
565
|
content: "",
|
|
245
566
|
use: "dynamic",
|
|
246
|
-
toJson: `${
|
|
567
|
+
toJson: `${camelcase2(context.name)}`,
|
|
247
568
|
fromJson: `json['${context.name}']`
|
|
248
569
|
};
|
|
249
570
|
}
|
|
250
571
|
}
|
|
251
|
-
#ref($ref, required, context) {
|
|
572
|
+
#ref(className, $ref, required, context) {
|
|
252
573
|
const schemaName = cleanRef($ref).split("/").pop();
|
|
253
|
-
const
|
|
254
|
-
|
|
574
|
+
const serialized = this.handle(
|
|
575
|
+
schemaName,
|
|
255
576
|
followRef(this.#spec, $ref),
|
|
256
577
|
required,
|
|
257
578
|
{
|
|
258
579
|
...context,
|
|
259
580
|
propName: schemaName,
|
|
260
|
-
noEmit: !context.forceEmit
|
|
581
|
+
noEmit: !!context.alias || !!className || !context.forceEmit
|
|
261
582
|
}
|
|
262
583
|
);
|
|
263
|
-
return
|
|
584
|
+
return serialized;
|
|
264
585
|
}
|
|
265
586
|
// fixme: this method should no longer be needed because the logic in it is being preprocessed before emitting begins
|
|
266
587
|
#allOf(className, schemas, context) {
|
|
@@ -278,16 +599,30 @@ return ${matches.join(" && ")};
|
|
|
278
599
|
delete objectSchema.allOf;
|
|
279
600
|
return this.handle(name, objectSchema, true, context);
|
|
280
601
|
}
|
|
281
|
-
anyOf(className, schemas, context) {
|
|
602
|
+
#anyOf(className, schemas, context) {
|
|
282
603
|
if (schemas.length === 0) {
|
|
283
604
|
return {
|
|
284
605
|
content: "",
|
|
285
606
|
use: "dynamic",
|
|
286
|
-
toJson: `${
|
|
607
|
+
toJson: `${camelcase2(context.name)}`,
|
|
287
608
|
fromJson: `json['${context.name}']`
|
|
288
609
|
};
|
|
289
610
|
}
|
|
290
|
-
|
|
611
|
+
const nullSchemaIndex = schemas.findIndex((schema) => {
|
|
612
|
+
if (isRef(schema)) {
|
|
613
|
+
const refSchema = followRef(this.#spec, schema.$ref);
|
|
614
|
+
return refSchema.type === "null";
|
|
615
|
+
}
|
|
616
|
+
return schema.type === "null";
|
|
617
|
+
});
|
|
618
|
+
const anyOfSchemas = schemas.slice(0);
|
|
619
|
+
if (nullSchemaIndex >= 0) {
|
|
620
|
+
anyOfSchemas.splice(nullSchemaIndex, 1);
|
|
621
|
+
}
|
|
622
|
+
return this.handle(className, anyOfSchemas[0], true, {
|
|
623
|
+
...context,
|
|
624
|
+
nullable: nullSchemaIndex >= 0
|
|
625
|
+
});
|
|
291
626
|
}
|
|
292
627
|
#mixinise(name, context) {
|
|
293
628
|
const mixins = this.#getRefUsage(name);
|
|
@@ -306,7 +641,7 @@ return ${matches.join(" && ")};
|
|
|
306
641
|
return {
|
|
307
642
|
content: "",
|
|
308
643
|
use: "dynamic",
|
|
309
|
-
toJson: `${
|
|
644
|
+
toJson: `${camelcase2(context.name)}`,
|
|
310
645
|
fromJson: `json['${context.name}']`
|
|
311
646
|
};
|
|
312
647
|
}
|
|
@@ -315,7 +650,7 @@ return ${matches.join(" && ")};
|
|
|
315
650
|
const objects = schemas.filter(notRef).filter((it) => it.type === "object");
|
|
316
651
|
for (const schema of schemas) {
|
|
317
652
|
if (isRef(schema)) {
|
|
318
|
-
const refType = this.#ref(schema.$ref, true, context);
|
|
653
|
+
const refType = this.#ref(className, schema.$ref, true, context);
|
|
319
654
|
patterns.push({
|
|
320
655
|
pattern: `case ${refType.type || "Map<String, dynamic>"} map when ${refType.use}.matches(map): return ${refType.use}.fromJson(map);`,
|
|
321
656
|
name: refType.use
|
|
@@ -457,7 +792,9 @@ return ${matches.join(" && ")};
|
|
|
457
792
|
${values.map((it) => `static const _EnumValue ${formatName(it)} = _EnumValue(${typeof it === "number" ? it : `'${it}'`});`).join("\n")}
|
|
458
793
|
dynamic toJson();
|
|
459
794
|
|
|
460
|
-
|
|
795
|
+
${valType} get value;
|
|
796
|
+
|
|
797
|
+
static _EnumValue fromJson(${valType} value) {
|
|
461
798
|
switch (value) {
|
|
462
799
|
${values.map(
|
|
463
800
|
(it) => `case ${typeof it === "number" ? it : `'${it}'`}: return ${formatName(it)};`
|
|
@@ -484,7 +821,7 @@ return false;
|
|
|
484
821
|
type: Array.isArray(schema.type) ? this.#simple(schema.type[0]) : schema.type ? this.#simple(schema.type) : void 0,
|
|
485
822
|
content,
|
|
486
823
|
use: pascalcase(name),
|
|
487
|
-
toJson: `${context.required ? `this.${
|
|
824
|
+
toJson: `${context.required ? `this.${camelcase2(context.name)}.toJson()` : `this.${camelcase2(context.name)} != null ? this.${camelcase2(context.name)}!.toJson() : null`}`,
|
|
488
825
|
fromJson: `${pascalcase(name)}.fromJson(json['${context.name}'])`,
|
|
489
826
|
matches: `${pascalcase(name)}.matches(json['${context.name}'])`
|
|
490
827
|
};
|
|
@@ -501,27 +838,30 @@ return false;
|
|
|
501
838
|
content: "",
|
|
502
839
|
use: "DateTime",
|
|
503
840
|
simple: true,
|
|
504
|
-
toJson: `this.${
|
|
505
|
-
|
|
841
|
+
toJson: context.required ? `this.${camelcase2(context.name)}.toIso8601String()` : `this.${camelcase2(context.name)} != null ? this.${camelcase2(
|
|
842
|
+
context.name
|
|
843
|
+
)}!.toIso8601String() : null`,
|
|
844
|
+
fromJson: context.name ? `json['${context.name}'] != null ? DateTime.parse(json['${context.name}']) : null` : "json",
|
|
506
845
|
matches: `json['${context.name}'] is String`
|
|
507
846
|
};
|
|
508
847
|
case "binary":
|
|
509
848
|
case "byte":
|
|
510
849
|
return {
|
|
511
850
|
content: "",
|
|
512
|
-
use: "
|
|
513
|
-
toJson: `this.${
|
|
851
|
+
use: "File",
|
|
852
|
+
toJson: `this.${camelcase2(context.name)}`,
|
|
514
853
|
simple: true,
|
|
515
|
-
fromJson: `json['${context.name}']
|
|
854
|
+
fromJson: context.name ? `json['${context.name}']` : "json",
|
|
516
855
|
matches: `json['${context.name}'] is Uint8List`
|
|
517
856
|
};
|
|
518
857
|
default:
|
|
519
858
|
return {
|
|
520
|
-
|
|
859
|
+
encode: "input",
|
|
860
|
+
use: `String`,
|
|
521
861
|
content: "",
|
|
522
862
|
simple: true,
|
|
523
|
-
toJson: `this.${
|
|
524
|
-
fromJson: `json['${context.name}'] as String
|
|
863
|
+
toJson: `this.${camelcase2(context.name)}`,
|
|
864
|
+
fromJson: context.name ? `json['${context.name}'] as String` : "json",
|
|
525
865
|
matches: `json['${context.name}'] is String`
|
|
526
866
|
};
|
|
527
867
|
}
|
|
@@ -530,35 +870,44 @@ return false;
|
|
|
530
870
|
* Handle number/integer types with formats
|
|
531
871
|
*/
|
|
532
872
|
number(schema, context) {
|
|
533
|
-
|
|
534
|
-
if (schema.format === "int64") {
|
|
873
|
+
if (schema.type === "integer") {
|
|
535
874
|
return {
|
|
536
875
|
content: "",
|
|
537
876
|
use: "int",
|
|
538
877
|
simple: true,
|
|
539
|
-
toJson: `this.${
|
|
878
|
+
toJson: `this.${camelcase2(context.name)}`,
|
|
540
879
|
fromJson: `json['${context.name}']`,
|
|
541
880
|
matches: `json['${context.name}'] is int`
|
|
542
881
|
};
|
|
543
882
|
}
|
|
883
|
+
if (["float", "double"].includes(schema.format)) {
|
|
884
|
+
return {
|
|
885
|
+
content: "",
|
|
886
|
+
use: "double",
|
|
887
|
+
simple: true,
|
|
888
|
+
toJson: `this.${camelcase2(context.name)}`,
|
|
889
|
+
fromJson: `json['${context.name}']`,
|
|
890
|
+
matches: `json['${context.name}'] is double`
|
|
891
|
+
};
|
|
892
|
+
}
|
|
544
893
|
return {
|
|
545
894
|
content: "",
|
|
895
|
+
use: "num",
|
|
546
896
|
simple: true,
|
|
547
|
-
|
|
548
|
-
toJson: `this.${camelcase(context.name)}`,
|
|
897
|
+
toJson: `this.${camelcase2(context.name)}`,
|
|
549
898
|
fromJson: `json['${context.name}']`,
|
|
550
|
-
matches: `json['${context.name}'] is
|
|
899
|
+
matches: `json['${context.name}'] is double`
|
|
551
900
|
};
|
|
552
901
|
}
|
|
553
|
-
|
|
902
|
+
#serialize(className, schema, required = true, context = {}) {
|
|
554
903
|
if (isRef(schema)) {
|
|
555
|
-
return this.#ref(schema.$ref, required, context);
|
|
904
|
+
return this.#ref(className, schema.$ref, required, context);
|
|
556
905
|
}
|
|
557
906
|
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
558
907
|
return this.#allOf(className, schema.allOf, context);
|
|
559
908
|
}
|
|
560
909
|
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
561
|
-
return this
|
|
910
|
+
return this.#anyOf(className, schema.anyOf, context);
|
|
562
911
|
}
|
|
563
912
|
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
564
913
|
return this.#oneOf(className, schema.oneOf, context);
|
|
@@ -585,11 +934,11 @@ return false;
|
|
|
585
934
|
return {
|
|
586
935
|
content: "",
|
|
587
936
|
use: "dynamic",
|
|
588
|
-
toJson: `${
|
|
937
|
+
toJson: `${camelcase2(context.name)}`,
|
|
589
938
|
fromJson: `json['${context.name}']`
|
|
590
939
|
};
|
|
591
940
|
}
|
|
592
|
-
return this
|
|
941
|
+
return this.#primitive(
|
|
593
942
|
className,
|
|
594
943
|
types[0],
|
|
595
944
|
schema,
|
|
@@ -597,9 +946,28 @@ return false;
|
|
|
597
946
|
required
|
|
598
947
|
);
|
|
599
948
|
}
|
|
949
|
+
handle(className, schema, required = true, context = {}) {
|
|
950
|
+
const alias = context.alias;
|
|
951
|
+
context.alias = void 0;
|
|
952
|
+
const serialized = this.#serialize(className, schema, required, {
|
|
953
|
+
...context,
|
|
954
|
+
forJson: alias
|
|
955
|
+
});
|
|
956
|
+
if (alias) {
|
|
957
|
+
this.#emit(className, `typedef ${alias} = ${serialized.use};`);
|
|
958
|
+
return serialized;
|
|
959
|
+
}
|
|
960
|
+
return serialized;
|
|
961
|
+
}
|
|
600
962
|
};
|
|
963
|
+
function isObjectSchema(schema) {
|
|
964
|
+
return !isRef(schema) && (schema.type === "object" || !!schema.properties);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// packages/dart/src/lib/http/dispatcher.txt
|
|
968
|
+
var dispatcher_default = "import 'package:mime/mime.dart' as mime;\nimport 'dart:convert';\nimport 'dart:io';\n\nimport './interceptors.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:http_parser/http_parser.dart';\n\nclass Dispatcher {\n final List<Interceptor> interceptors;\n\n Dispatcher(this.interceptors);\n\n Future<http.StreamedResponse> multipart(\n RequestConfig config,\n Map<String, dynamic> body,\n ) async {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n final request = http.MultipartRequest(\n modifiedConfig.method,\n modifiedConfig.url,\n );\n request.headers.addAll(modifiedConfig.headers);\n for (var entry in body.entries) {\n final key = entry.key;\n final value = entry.value;\n if (value is File) {\n final mimeType = mime.lookupMimeType(value.path);\n request.files.add(\n http.MultipartFile(\n key,\n value.openRead(),\n await value.length(),\n filename: value.uri.pathSegments.last,\n contentType: mimeType != null ? MediaType.parse(mimeType) : null,\n ),\n );\n } else {\n request.fields[key] = value.toString();\n }\n }\n\n return request.send();\n }\n\n Future<http.StreamedResponse> empty(RequestConfig config) {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n final request = http.Request(modifiedConfig.method, modifiedConfig.url);\n request.headers.addAll(modifiedConfig.headers);\n return request.send();\n }\n\n Future<http.StreamedResponse> json(RequestConfig config, dynamic body) {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n final request = http.Request(modifiedConfig.method, modifiedConfig.url);\n request.headers.addAll(modifiedConfig.headers);\n\n if ((body is Map || body is List)) {\n request.headers['Content-Type'] = 'application/json';\n request.body = jsonEncode(body);\n } else if (body is String) {\n request.body = body;\n } else {\n throw ArgumentError('Unsupported body type: ${body.runtimeType}');\n }\n\n return request.send();\n }\n}\n";
|
|
601
969
|
|
|
602
|
-
// packages/dart/src/lib/interceptors.txt
|
|
970
|
+
// packages/dart/src/lib/http/interceptors.txt
|
|
603
971
|
var interceptors_default = "abstract class Interceptor {\n RequestConfig before(RequestConfig config);\n void after();\n}\n\nclass BaseUrlInterceptor extends Interceptor {\n final String Function() getBaseUrl;\n BaseUrlInterceptor(this.getBaseUrl);\n\n @override\n RequestConfig before(RequestConfig config) {\n final baseUrl = getBaseUrl();\n if (config.url.scheme.isEmpty) {\n config.url = Uri.parse(baseUrl + config.url.toString());\n }\n return config;\n }\n\n @override\n void after() {\n //\n }\n}\n\nclass RequestConfig {\n final String method;\n Uri url;\n final Map<String, String> headers;\n RequestConfig({required this.method, required this.url, required this.headers});\n}\n";
|
|
604
972
|
|
|
605
973
|
// packages/dart/src/lib/generate.ts
|
|
@@ -665,25 +1033,58 @@ function tuneSpec(spec, schemas, refs) {
|
|
|
665
1033
|
}
|
|
666
1034
|
}
|
|
667
1035
|
async function generate(spec, settings) {
|
|
668
|
-
const
|
|
1036
|
+
const clientName = settings.name || "Client";
|
|
1037
|
+
const output = join(settings.output, "lib");
|
|
669
1038
|
const groups = {};
|
|
670
1039
|
spec.components ??= {};
|
|
671
1040
|
spec.components.schemas ??= {};
|
|
1041
|
+
const inputs = {};
|
|
1042
|
+
const outputs = {};
|
|
672
1043
|
forEachOperation({ spec }, (entry, operation) => {
|
|
1044
|
+
operation.responses ??= {};
|
|
1045
|
+
for (const status in operation.responses) {
|
|
1046
|
+
if (!isSuccessStatusCode(status))
|
|
1047
|
+
continue;
|
|
1048
|
+
const response2 = isRef2(operation.responses[status]) ? followRef2(spec, operation.responses[status].$ref) : operation.responses[status];
|
|
1049
|
+
if (response2.content && Object.keys(response2.content).length) {
|
|
1050
|
+
for (const [contentType, mediaType] of Object.entries(
|
|
1051
|
+
response2.content
|
|
1052
|
+
)) {
|
|
1053
|
+
if (parseJsonContentType(contentType)) {
|
|
1054
|
+
if (mediaType.schema && !isRef2(mediaType.schema)) {
|
|
1055
|
+
spec.components ??= {};
|
|
1056
|
+
spec.components.schemas ??= {};
|
|
1057
|
+
const outputName = pascalcase2(`${operation.operationId} output`);
|
|
1058
|
+
spec.components.schemas[outputName] = mediaType.schema;
|
|
1059
|
+
operation.responses[status].content[contentType].schema = {
|
|
1060
|
+
$ref: `#/components/schemas/${outputName}`
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
673
1067
|
console.log(`Processing ${entry.method} ${entry.path}`);
|
|
674
1068
|
const group = groups[entry.groupName] ?? (groups[entry.groupName] = {
|
|
675
1069
|
methods: [],
|
|
676
1070
|
use: `final ${entry.groupName} = new ${pascalcase2(entry.groupName)}();`
|
|
677
1071
|
});
|
|
1072
|
+
const input = toInputs(spec, { entry, operation });
|
|
1073
|
+
Object.assign(inputs, input.inputs);
|
|
1074
|
+
const response = toOutput(spec, operation);
|
|
1075
|
+
if (response) {
|
|
1076
|
+
Object.assign(outputs, response.outputs);
|
|
1077
|
+
}
|
|
678
1078
|
group.methods.push(`
|
|
679
|
-
Future
|
|
680
|
-
|
|
1079
|
+
Future<${response ? response.returnType : "http.StreamedResponse"}> ${camelcase3(operation.operationId)}(
|
|
1080
|
+
${isEmpty2(operation.requestBody) ? "" : `${input.inputName} input`}
|
|
1081
|
+
) async {
|
|
1082
|
+
final stream = await this.dispatcher.${input.contentType}(RequestConfig(
|
|
681
1083
|
method: '${entry.method}',
|
|
682
1084
|
url: Uri.parse('${entry.path}'),
|
|
683
1085
|
headers: {},
|
|
684
|
-
));
|
|
685
|
-
|
|
686
|
-
return response;
|
|
1086
|
+
), ${["json", "multipart"].includes(input.contentType) ? input.encode : ``});
|
|
1087
|
+
${response ? `${response.decode};` : "return stream;"}
|
|
687
1088
|
}
|
|
688
1089
|
`);
|
|
689
1090
|
});
|
|
@@ -698,7 +1099,7 @@ async function generate(spec, settings) {
|
|
|
698
1099
|
);
|
|
699
1100
|
const models = Object.entries(spec.components.schemas).reduce((acc, [name, schema]) => {
|
|
700
1101
|
const serializer = new DartSerializer(spec, (name2, content) => {
|
|
701
|
-
acc[`models/${snakecase2(name2)}.dart`] = `import 'dart:typed_data'; import './index.dart';
|
|
1102
|
+
acc[`models/${snakecase2(name2)}.dart`] = `import 'dart:io';import 'dart:typed_data'; import './index.dart';
|
|
702
1103
|
|
|
703
1104
|
${content}`;
|
|
704
1105
|
});
|
|
@@ -710,13 +1111,19 @@ ${content}`;
|
|
|
710
1111
|
return {
|
|
711
1112
|
...acc,
|
|
712
1113
|
[`api/${snakecase2(name)}.dart`]: `
|
|
1114
|
+
import 'dart:convert';
|
|
1115
|
+
|
|
713
1116
|
import 'package:http/http.dart' as http;
|
|
1117
|
+
|
|
714
1118
|
import '../interceptors.dart';
|
|
1119
|
+
import '../inputs/index.dart';
|
|
1120
|
+
import '../outputs/index.dart';
|
|
1121
|
+
import '../models/index.dart';
|
|
715
1122
|
import '../http.dart';
|
|
716
1123
|
|
|
717
|
-
class ${pascalcase2(name)} {
|
|
1124
|
+
class ${pascalcase2(name)}Client {
|
|
718
1125
|
final Dispatcher dispatcher;
|
|
719
|
-
${pascalcase2(name)}(this.dispatcher);
|
|
1126
|
+
${pascalcase2(name)}Client(this.dispatcher);
|
|
720
1127
|
${methods.join("\n")}
|
|
721
1128
|
}
|
|
722
1129
|
`
|
|
@@ -729,15 +1136,15 @@ import '../http.dart';
|
|
|
729
1136
|
import './interceptors.dart';
|
|
730
1137
|
import './http.dart';
|
|
731
1138
|
|
|
732
|
-
class
|
|
1139
|
+
class ${clientName} {
|
|
733
1140
|
final Options options;
|
|
734
|
-
${Object.keys(groups).map((name) => `late final ${pascalcase2(name)} ${
|
|
1141
|
+
${Object.keys(groups).map((name) => `late final ${pascalcase2(name)}Client ${camelcase3(name)};`).join("\n")}
|
|
735
1142
|
|
|
736
|
-
|
|
1143
|
+
${clientName}(this.options) {
|
|
737
1144
|
final interceptors = [new BaseUrlInterceptor(() => this.options.baseUrl)];
|
|
738
1145
|
final dispatcher = new Dispatcher(interceptors);
|
|
739
1146
|
${Object.keys(groups).map(
|
|
740
|
-
(name) => `this.${
|
|
1147
|
+
(name) => `this.${camelcase3(name)} = new ${pascalcase2(name)}Client(dispatcher);`
|
|
741
1148
|
).join("\n")}
|
|
742
1149
|
|
|
743
1150
|
}
|
|
@@ -757,41 +1164,137 @@ class Options {
|
|
|
757
1164
|
|
|
758
1165
|
`;
|
|
759
1166
|
await writeFiles(output, {
|
|
760
|
-
...models
|
|
1167
|
+
...models,
|
|
1168
|
+
...inputs,
|
|
1169
|
+
...outputs
|
|
761
1170
|
});
|
|
762
1171
|
await writeFiles(output, {
|
|
763
1172
|
"models/index.dart": await getFolderExportsV2(join(output, "models"), {
|
|
764
1173
|
exportSyntax: "export",
|
|
765
|
-
extensions:
|
|
1174
|
+
extensions: "dart"
|
|
1175
|
+
}),
|
|
1176
|
+
"inputs/index.dart": await getFolderExportsV2(join(output, "inputs"), {
|
|
1177
|
+
exportSyntax: "export",
|
|
1178
|
+
extensions: "dart"
|
|
1179
|
+
}),
|
|
1180
|
+
"outputs/index.dart": await getFolderExportsV2(join(output, "outputs"), {
|
|
1181
|
+
exportSyntax: "export",
|
|
1182
|
+
extensions: "dart"
|
|
766
1183
|
}),
|
|
767
|
-
"index.dart": client,
|
|
768
1184
|
"interceptors.dart": interceptors_default,
|
|
769
|
-
"http.dart":
|
|
770
|
-
import 'interceptors.dart';
|
|
771
|
-
import 'package:http/http.dart' as http;
|
|
772
|
-
|
|
773
|
-
class Dispatcher {
|
|
774
|
-
final List<Interceptor> interceptors;
|
|
775
|
-
|
|
776
|
-
Dispatcher(this.interceptors);
|
|
777
|
-
|
|
778
|
-
Future<http.StreamedResponse> dispatch(RequestConfig config) {
|
|
779
|
-
final modifedConfig = interceptors.fold(
|
|
780
|
-
config,
|
|
781
|
-
(acc, interceptor) => interceptor.before(acc),
|
|
782
|
-
);
|
|
783
|
-
final request = http.Request(modifedConfig.method, modifedConfig.url);
|
|
784
|
-
return request.send();
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
`,
|
|
1185
|
+
"http.dart": dispatcher_default,
|
|
788
1186
|
...clazzez
|
|
789
|
-
|
|
1187
|
+
});
|
|
1188
|
+
await writeFiles(output, {
|
|
1189
|
+
"package.dart": `${await getFolderExportsV2(join(output), {
|
|
1190
|
+
exportSyntax: "export",
|
|
1191
|
+
extensions: "dart",
|
|
1192
|
+
ignore(dirent) {
|
|
1193
|
+
return dirent.isFile() && dirent.name === "package.dart";
|
|
1194
|
+
}
|
|
1195
|
+
})}${client}`
|
|
1196
|
+
});
|
|
1197
|
+
await writeFiles(settings.output, {
|
|
1198
|
+
"pubspec.yaml": {
|
|
1199
|
+
ignoreIfExists: true,
|
|
1200
|
+
content: yaml.stringify({
|
|
1201
|
+
name: settings.name ? `${snakecase2(clientName.toLowerCase())}_sdk` : "sdk",
|
|
1202
|
+
version: "0.0.1",
|
|
1203
|
+
environment: {
|
|
1204
|
+
sdk: "^3.7.2"
|
|
1205
|
+
},
|
|
1206
|
+
dependencies: {
|
|
1207
|
+
http: "^1.3.0",
|
|
1208
|
+
mime: "^2.0.0"
|
|
1209
|
+
}
|
|
1210
|
+
})
|
|
1211
|
+
}
|
|
790
1212
|
});
|
|
791
1213
|
await settings.formatCode?.({
|
|
792
1214
|
output
|
|
793
1215
|
});
|
|
794
1216
|
}
|
|
1217
|
+
function toInputs(spec, { entry, operation }) {
|
|
1218
|
+
const inputs = {};
|
|
1219
|
+
const inputName = pascalcase2(`${operation.operationId} input`);
|
|
1220
|
+
let contentType = "empty";
|
|
1221
|
+
let encode = "";
|
|
1222
|
+
if (!isEmpty2(operation.requestBody)) {
|
|
1223
|
+
const requestBody = isRef2(operation.requestBody) ? followRef2(spec, operation.requestBody.$ref) : operation.requestBody;
|
|
1224
|
+
for (const type in requestBody.content) {
|
|
1225
|
+
const ctSchema = isRef2(requestBody.content[type].schema) ? followRef2(spec, requestBody.content[type].schema.$ref) : requestBody.content[type].schema;
|
|
1226
|
+
if (!ctSchema) {
|
|
1227
|
+
console.warn(
|
|
1228
|
+
`Schema not found for ${type} in ${entry.method} ${entry.path}`
|
|
1229
|
+
);
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
const serializer = new DartSerializer(spec, (name, content) => {
|
|
1233
|
+
inputs[join(`inputs/${name}.dart`)] = `import 'dart:io';import 'dart:typed_data';import '../models/index.dart'; import './index.dart';
|
|
1234
|
+
|
|
1235
|
+
${content}`;
|
|
1236
|
+
});
|
|
1237
|
+
const serialized = serializer.handle(inputName, ctSchema, true, {
|
|
1238
|
+
alias: isObjectSchema(ctSchema) ? void 0 : inputName
|
|
1239
|
+
});
|
|
1240
|
+
encode = serialized.encode;
|
|
1241
|
+
if (contentType) {
|
|
1242
|
+
console.warn(
|
|
1243
|
+
`${entry.method} ${entry.path} have more than one content type`
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
const [mediaType, mediaSubType] = partContentType(type).type.split("/");
|
|
1247
|
+
if (mediaType === "application") {
|
|
1248
|
+
contentType = parseJsonContentType(type);
|
|
1249
|
+
} else {
|
|
1250
|
+
contentType = mediaType;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return { inputs, inputName, contentType, encode };
|
|
1255
|
+
}
|
|
1256
|
+
function toOutput(spec, operation) {
|
|
1257
|
+
const outputName = pascalcase2(`${operation.operationId} output`);
|
|
1258
|
+
operation.responses ??= {};
|
|
1259
|
+
const outputs = {};
|
|
1260
|
+
for (const status in operation.responses) {
|
|
1261
|
+
const response = isRef2(operation.responses[status]) ? followRef2(spec, operation.responses[status].$ref) : operation.responses[status];
|
|
1262
|
+
for (const type in response.content) {
|
|
1263
|
+
const { schema } = response.content[type];
|
|
1264
|
+
if (!schema) {
|
|
1265
|
+
console.warn(
|
|
1266
|
+
`Schema not found for ${type} in ${operation.operationId}`
|
|
1267
|
+
);
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
const serializer = new DartSerializer(spec, (name, content) => {
|
|
1271
|
+
});
|
|
1272
|
+
if (isStreamingContentType(type)) {
|
|
1273
|
+
return {
|
|
1274
|
+
type: "stream",
|
|
1275
|
+
outputName,
|
|
1276
|
+
outputs,
|
|
1277
|
+
decode: `return stream`,
|
|
1278
|
+
returnType: `http.StreamedResponse`
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
if (parseJsonContentType(type)) {
|
|
1282
|
+
const serialized = serializer.handle(outputName, schema, true, {
|
|
1283
|
+
// alias: outputName,
|
|
1284
|
+
noEmit: true
|
|
1285
|
+
});
|
|
1286
|
+
return {
|
|
1287
|
+
type: "json",
|
|
1288
|
+
outputName,
|
|
1289
|
+
outputs,
|
|
1290
|
+
decode: `final response = await http.Response.fromStream(stream);final dynamic json = jsonDecode(response.body); return ${serialized.fromJson}`,
|
|
1291
|
+
returnType: serialized.use
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return null;
|
|
1297
|
+
}
|
|
795
1298
|
export {
|
|
796
1299
|
generate
|
|
797
1300
|
};
|