@nestia/migrate 0.7.5 → 0.7.6

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.
Files changed (114) hide show
  1. package/lib/MigrateApplication.d.ts +9 -2
  2. package/lib/MigrateApplication.js +14 -8
  3. package/lib/MigrateApplication.js.map +1 -1
  4. package/lib/analyzers/MigrateAnalyzer.js +2 -2
  5. package/lib/analyzers/MigrateAnalyzer.js.map +1 -1
  6. package/lib/analyzers/{ControllerAnalyzer.d.ts → MigrateControllerAnalyzer.d.ts} +1 -1
  7. package/lib/analyzers/{ControllerAnalyzer.js → MigrateControllerAnalyzer.js} +11 -9
  8. package/lib/analyzers/MigrateControllerAnalyzer.js.map +1 -0
  9. package/lib/analyzers/{MethodAnalyzer.d.ts → MigrateMethodAnalyzer.d.ts} +1 -1
  10. package/lib/analyzers/{MethodAnalyzer.js → MigrateMethodAnalyzer.js} +26 -25
  11. package/lib/analyzers/MigrateMethodAnalyzer.js.map +1 -0
  12. package/lib/archivers/{FileArchiver.d.ts → MigrateFileArchiver.d.ts} +1 -1
  13. package/lib/archivers/{FileArchiver.js → MigrateFileArchiver.js} +6 -6
  14. package/lib/archivers/MigrateFileArchiver.js.map +1 -0
  15. package/lib/bundles/SDK_TEMPLATE.js +0 -5
  16. package/lib/bundles/SDK_TEMPLATE.js.map +1 -1
  17. package/lib/executable/bundle.js +1 -0
  18. package/lib/executable/bundle.js.map +1 -1
  19. package/lib/internal/MigrateCommander.js +3 -3
  20. package/lib/internal/MigrateCommander.js.map +1 -1
  21. package/lib/programmers/{ApiFileProgrammer.d.ts → MigrateApiFileProgrammer.d.ts} +1 -1
  22. package/lib/programmers/MigrateApiFileProgrammer.js +28 -0
  23. package/lib/programmers/MigrateApiFileProgrammer.js.map +1 -0
  24. package/lib/programmers/{ApiFunctionProgrammer.d.ts → MigrateApiFunctionProgrammer.d.ts} +4 -4
  25. package/lib/programmers/{ApiFunctionProgrammer.js → MigrateApiFunctionProgrammer.js} +10 -10
  26. package/lib/programmers/MigrateApiFunctionProgrammer.js.map +1 -0
  27. package/lib/programmers/{ApiNamespaceProgrammer.d.ts → MigrateApiNamespaceProgrammer.d.ts} +3 -3
  28. package/lib/programmers/{ApiNamespaceProgrammer.js → MigrateApiNamespaceProgrammer.js} +19 -19
  29. package/lib/programmers/MigrateApiNamespaceProgrammer.js.map +1 -0
  30. package/lib/programmers/{ApiProgrammer.d.ts → MigrateApiProgrammer.d.ts} +1 -1
  31. package/lib/programmers/{ApiProgrammer.js → MigrateApiProgrammer.js} +25 -20
  32. package/lib/programmers/MigrateApiProgrammer.js.map +1 -0
  33. package/lib/programmers/{ApiSimulatationProgrammer.d.ts → MigrateApiSimulatationProgrammer.d.ts} +4 -4
  34. package/lib/programmers/{ApiSimulatationProgrammer.js → MigrateApiSimulatationProgrammer.js} +16 -16
  35. package/lib/programmers/MigrateApiSimulatationProgrammer.js.map +1 -0
  36. package/lib/programmers/{DtoProgrammer.d.ts → MigrateDtoProgrammer.d.ts} +3 -3
  37. package/lib/programmers/{DtoProgrammer.js → MigrateDtoProgrammer.js} +8 -8
  38. package/lib/programmers/MigrateDtoProgrammer.js.map +1 -0
  39. package/lib/programmers/{ImportProgrammer.d.ts → MigrateImportProgrammer.d.ts} +3 -3
  40. package/lib/programmers/{ImportProgrammer.js → MigrateImportProgrammer.js} +7 -5
  41. package/lib/programmers/MigrateImportProgrammer.js.map +1 -0
  42. package/lib/programmers/{NestControllerProgrammer.d.ts → MigrateNestControllerProgrammer.d.ts} +1 -1
  43. package/lib/programmers/{NestControllerProgrammer.js → MigrateNestControllerProgrammer.js} +11 -11
  44. package/lib/programmers/MigrateNestControllerProgrammer.js.map +1 -0
  45. package/lib/programmers/{NestMethodProgrammer.d.ts → MigrateNestMethodProgrammer.d.ts} +3 -3
  46. package/lib/programmers/{NestMethodProgrammer.js → MigrateNestMethodProgrammer.js} +13 -11
  47. package/lib/programmers/MigrateNestMethodProgrammer.js.map +1 -0
  48. package/lib/programmers/{NestModuleProgrammer.d.ts → MigrateNestModuleProgrammer.d.ts} +1 -1
  49. package/lib/programmers/{NestModuleProgrammer.js → MigrateNestModuleProgrammer.js} +8 -8
  50. package/lib/programmers/MigrateNestModuleProgrammer.js.map +1 -0
  51. package/lib/programmers/{NestProgrammer.d.ts → MigrateNestProgrammer.d.ts} +1 -1
  52. package/lib/programmers/{NestProgrammer.js → MigrateNestProgrammer.js} +17 -15
  53. package/lib/programmers/MigrateNestProgrammer.js.map +1 -0
  54. package/lib/programmers/{SchemaProgrammer.d.ts → MigrateSchemaProgrammer.d.ts} +3 -3
  55. package/lib/programmers/{SchemaProgrammer.js → MigrateSchemaProgrammer.js} +23 -23
  56. package/lib/programmers/MigrateSchemaProgrammer.js.map +1 -0
  57. package/lib/structures/IMigrateRoute.d.ts +1 -0
  58. package/lib/utils/FilePrinter.d.ts +1 -1
  59. package/lib/utils/FilePrinter.js +1 -1
  60. package/lib/utils/FilePrinter.js.map +1 -1
  61. package/lib/utils/{JsonTypeChecker.d.ts → SwaggerTypeChecker.d.ts} +1 -1
  62. package/lib/utils/SwaggerTypeChecker.js +34 -0
  63. package/lib/utils/SwaggerTypeChecker.js.map +1 -0
  64. package/package.json +68 -68
  65. package/src/IMigrateConfig.ts +4 -4
  66. package/src/MigrateApplication.ts +48 -35
  67. package/src/analyzers/MigrateAnalyzer.ts +13 -13
  68. package/src/analyzers/{ControllerAnalyzer.ts → MigrateControllerAnalyzer.ts} +122 -123
  69. package/src/analyzers/{MethodAnalyzer.ts → MigrateMethodAnalyzer.ts} +348 -347
  70. package/src/archivers/{FileArchiver.ts → MigrateFileArchiver.ts} +38 -38
  71. package/src/bundles/SDK_TEMPLATE.ts +0 -5
  72. package/src/executable/bundle.ts +109 -108
  73. package/src/executable/migrate.ts +7 -7
  74. package/src/internal/MigrateCommander.ts +51 -52
  75. package/src/internal/MigrateInquirer.ts +79 -79
  76. package/src/module.ts +4 -4
  77. package/src/programmers/{ApiFileProgrammer.ts → MigrateApiFileProgrammer.ts} +53 -53
  78. package/src/programmers/{ApiFunctionProgrammer.ts → MigrateApiFunctionProgrammer.ts} +203 -203
  79. package/src/programmers/{ApiNamespaceProgrammer.ts → MigrateApiNamespaceProgrammer.ts} +430 -418
  80. package/src/programmers/{ApiProgrammer.ts → MigrateApiProgrammer.ts} +132 -128
  81. package/src/programmers/{ApiSimulatationProgrammer.ts → MigrateApiSimulatationProgrammer.ts} +328 -328
  82. package/src/programmers/{DtoProgrammer.ts → MigrateDtoProgrammer.ts} +78 -74
  83. package/src/programmers/{ImportProgrammer.ts → MigrateImportProgrammer.ts} +116 -114
  84. package/src/programmers/{NestControllerProgrammer.ts → MigrateNestControllerProgrammer.ts} +50 -48
  85. package/src/programmers/{NestMethodProgrammer.ts → MigrateNestMethodProgrammer.ts} +249 -243
  86. package/src/programmers/{NestModuleProgrammer.ts → MigrateNestModuleProgrammer.ts} +62 -62
  87. package/src/programmers/{NestProgrammer.ts → MigrateNestProgrammer.ts} +74 -74
  88. package/src/programmers/{SchemaProgrammer.ts → MigrateSchemaProgrammer.ts} +257 -255
  89. package/src/structures/IMigrateProgram.ts +9 -9
  90. package/src/structures/IMigrateRoute.ts +46 -45
  91. package/src/structures/ISwaggeSchema.ts +90 -90
  92. package/src/structures/ISwaggerInfo.ts +80 -80
  93. package/src/utils/FilePrinter.ts +36 -36
  94. package/src/utils/SetupWizard.ts +12 -12
  95. package/src/utils/StringUtil.ts +60 -60
  96. package/src/utils/{JsonTypeChecker.ts → SwaggerTypeChecker.ts} +67 -67
  97. package/lib/analyzers/ControllerAnalyzer.js.map +0 -1
  98. package/lib/analyzers/MethodAnalyzer.js.map +0 -1
  99. package/lib/archivers/FileArchiver.js.map +0 -1
  100. package/lib/programmers/ApiFileProgrammer.js +0 -28
  101. package/lib/programmers/ApiFileProgrammer.js.map +0 -1
  102. package/lib/programmers/ApiFunctionProgrammer.js.map +0 -1
  103. package/lib/programmers/ApiNamespaceProgrammer.js.map +0 -1
  104. package/lib/programmers/ApiProgrammer.js.map +0 -1
  105. package/lib/programmers/ApiSimulatationProgrammer.js.map +0 -1
  106. package/lib/programmers/DtoProgrammer.js.map +0 -1
  107. package/lib/programmers/ImportProgrammer.js.map +0 -1
  108. package/lib/programmers/NestControllerProgrammer.js.map +0 -1
  109. package/lib/programmers/NestMethodProgrammer.js.map +0 -1
  110. package/lib/programmers/NestModuleProgrammer.js.map +0 -1
  111. package/lib/programmers/NestProgrammer.js.map +0 -1
  112. package/lib/programmers/SchemaProgrammer.js.map +0 -1
  113. package/lib/utils/JsonTypeChecker.js +0 -34
  114. package/lib/utils/JsonTypeChecker.js.map +0 -1
@@ -1,347 +1,348 @@
1
- import { Escaper } from "typia/lib/utils/Escaper";
2
-
3
- import { ISwagger } from "../module";
4
- import { IMigrateRoute } from "../structures/IMigrateRoute";
5
- import { ISwaggerSchema } from "../structures/ISwaggeSchema";
6
- import { ISwaggerRoute } from "../structures/ISwaggerRoute";
7
- import { SwaggerTypeChecker } from "../utils/JsonTypeChecker";
8
- import { StringUtil } from "../utils/StringUtil";
9
-
10
- export namespace MethodAnalzyer {
11
- export const analyze =
12
- (swagger: ISwagger) =>
13
- (props: { path: string; method: string }) =>
14
- (route: ISwaggerRoute): IMigrateRoute | null => {
15
- const body = emplaceBodySchema("request")(
16
- emplaceReference(swagger)("body"),
17
- )(route.requestBody);
18
- const success = emplaceBodySchema("response")(
19
- emplaceReference(swagger)("response"),
20
- )(route.responses?.["201"] ?? route.responses?.["200"]);
21
-
22
- const failures: string[] = [];
23
- if (body === false)
24
- failures.push(
25
- `supports only "application/json", "application/x-www-form-urlencoded", "multipart/form-data" and "text/plain" content type in the request body.`,
26
- );
27
- if (success === false)
28
- failures.push(
29
- `supports only "application/json", "application/x-www-form-urlencoded" and "text/plain" content type in the response body.`,
30
- );
31
- if (SUPPORTED_METHODS.has(props.method.toUpperCase()) === false)
32
- failures.push(`does not support ${props.method.toUpperCase()} method.`);
33
- if (failures.length) {
34
- console.log(
35
- `Failed to migrate ${props.method.toUpperCase()} ${props.path}`,
36
- ...failures.map((f) => ` - ${f}`),
37
- );
38
- return null;
39
- }
40
-
41
- const [headers, query] = ["header", "query"].map((type) => {
42
- const parameters = (route.parameters ?? []).filter(
43
- (p) => p.in === type,
44
- );
45
- if (parameters.length === 0) return null;
46
-
47
- const objects = parameters
48
- .map((p) =>
49
- SwaggerTypeChecker.isObject(p.schema)
50
- ? p.schema
51
- : SwaggerTypeChecker.isReference(p.schema) &&
52
- SwaggerTypeChecker.isObject(
53
- (swagger.components.schemas ?? {})[
54
- p.schema.$ref.replace(`#/components/schemas/`, ``)
55
- ] ?? {},
56
- )
57
- ? p.schema
58
- : null!,
59
- )
60
- .filter((s) => !!s);
61
- const primitives = parameters.filter(
62
- (p) =>
63
- SwaggerTypeChecker.isBoolean(p.schema) ||
64
- SwaggerTypeChecker.isNumber(p.schema) ||
65
- SwaggerTypeChecker.isInteger(p.schema) ||
66
- SwaggerTypeChecker.isString(p.schema) ||
67
- SwaggerTypeChecker.isArray(p.schema),
68
- );
69
- if (objects.length === 1 && primitives.length === 0) return objects[0];
70
- else if (objects.length > 1)
71
- throw new Error(
72
- `Error on nestia.migrate.RouteProgrammer.analze(): ${type} typed parameters must be only one object type - ${StringUtil.capitalize(
73
- props.method,
74
- )} "${props.path}".`,
75
- );
76
-
77
- const dto: ISwaggerSchema.IObject | null = objects[0]
78
- ? SwaggerTypeChecker.isObject(objects[0])
79
- ? objects[0]
80
- : ((swagger.components.schemas ?? {})[
81
- (objects[0] as ISwaggerSchema.IReference).$ref.replace(
82
- `#/components/schemas/`,
83
- ``,
84
- )
85
- ] as ISwaggerSchema.IObject)
86
- : null;
87
- const entire: ISwaggerSchema.IObject[] = [
88
- ...objects.map((o) =>
89
- SwaggerTypeChecker.isObject(o)
90
- ? o
91
- : (swagger.components.schemas?.[
92
- o.$ref.replace(`#/components/schemas/`, ``)
93
- ]! as ISwaggerSchema.IObject),
94
- ),
95
- {
96
- type: "object",
97
- properties: Object.fromEntries([
98
- ...primitives.map((p) => [
99
- p.name,
100
- {
101
- ...p.schema,
102
- description: p.schema.description ?? p.description,
103
- },
104
- ]),
105
- ...(dto ? Object.entries(dto.properties ?? {}) : []),
106
- ]),
107
- required: [
108
- ...primitives.filter((p) => p.required).map((p) => p.name!),
109
- ...(dto ? dto.required ?? [] : []),
110
- ],
111
- },
112
- ];
113
- return parameters.length === 0
114
- ? null
115
- : emplaceReference(swagger)(
116
- StringUtil.pascal(`I/Api/${props.path}`) +
117
- "." +
118
- StringUtil.pascal(`${props.method}/${type}`),
119
- )({
120
- type: "object",
121
- properties: Object.fromEntries([
122
- ...new Map<string, ISwaggerSchema>(
123
- entire
124
- .map((o) =>
125
- Object.entries(o.properties ?? {}).map(
126
- ([name, schema]) =>
127
- [
128
- name,
129
- {
130
- ...schema,
131
- description:
132
- schema.description ?? schema.description,
133
- } as ISwaggerSchema,
134
- ] as const,
135
- ),
136
- )
137
- .flat(),
138
- ),
139
- ]),
140
- required: [
141
- ...new Set(entire.map((o) => o.required ?? []).flat()),
142
- ],
143
- });
144
- });
145
-
146
- const parameterNames: string[] = StringUtil.splitWithNormalization(
147
- props.path,
148
- )
149
- .filter((str) => str[0] === "{" || str[0] === ":")
150
- .map((str) =>
151
- str[0] === "{" ? str.substring(1, str.length - 1) : str.substring(1),
152
- );
153
- if (
154
- parameterNames.length !==
155
- (route.parameters ?? []).filter((p) => p.in === "path").length
156
- ) {
157
- console.log(
158
- `Failed to migrate ${props.method.toUpperCase()} ${
159
- props.path
160
- }: number of path parameters are not matched with its full path.`,
161
- );
162
- return null;
163
- }
164
- return {
165
- name: "@lazy",
166
- path: props.path,
167
- method: props.method,
168
- headers: headers
169
- ? {
170
- key: "headers",
171
- schema: headers,
172
- }
173
- : null,
174
- parameters: (route.parameters ?? [])
175
- .filter((p) => p.in === "path")
176
- .map((p, i) => ({
177
- key: (() => {
178
- let key: string = StringUtil.normalize(parameterNames[i]);
179
- if (Escaper.variable(key)) return key;
180
- while (true) {
181
- key = "_" + key;
182
- if (!parameterNames.some((s) => s === key)) return key;
183
- }
184
- })(),
185
- schema: {
186
- ...p.schema,
187
- description: p.schema.description ?? p.description,
188
- },
189
- })),
190
- query: query
191
- ? {
192
- key: "query",
193
- schema: query,
194
- }
195
- : null,
196
- body: body as IMigrateRoute.IBody,
197
- success: success as IMigrateRoute.IBody,
198
- exceptions: Object.fromEntries(
199
- Object.entries(route.responses ?? {})
200
- .filter(
201
- ([key, value]) =>
202
- key !== "200" &&
203
- key !== "201" &&
204
- !!value.content?.["application/json"],
205
- )
206
- .map(([key, value]) => [
207
- key,
208
- {
209
- description: value.description,
210
- schema: value.content?.["application/json"]?.schema ?? {},
211
- },
212
- ]),
213
- ),
214
- deprecated: route.deprecated ?? false,
215
- description: describe(route),
216
- tags: route.tags ?? [],
217
- };
218
- };
219
-
220
- const describe = (route: ISwaggerRoute): string | undefined => {
221
- const commentTags: string[] = [];
222
- const add = (text: string) => {
223
- if (commentTags.every((line) => line !== text)) commentTags.push(text);
224
- };
225
-
226
- let description: string | undefined = route.description;
227
- if (route.summary) {
228
- const emended: string = route.summary.endsWith(".")
229
- ? route.summary
230
- : route.summary + ".";
231
- if (
232
- description !== undefined &&
233
- !description?.startsWith(route.summary) &&
234
- !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
235
- )
236
- description = `${emended}\n${description}`;
237
- }
238
- if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
239
- if (route.deprecated) add("@deprecated");
240
- for (const security of route.security ?? [])
241
- for (const [name, scopes] of Object.entries(security))
242
- add(`@security ${[name, ...scopes].join("")}`);
243
- for (const jsDocTag of route["x-nestia-jsDocTags"] ?? [])
244
- if (jsDocTag.text?.length)
245
- add(
246
- `@${jsDocTag.name} ${jsDocTag.text
247
- .map((text) => text.text)
248
- .join("")}`,
249
- );
250
- else add(`@${jsDocTag.name}`);
251
- return description?.length
252
- ? commentTags.length
253
- ? `${description}\n\n${commentTags.join("\n")}`
254
- : description
255
- : commentTags.join("\n");
256
- };
257
-
258
- const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
259
- SwaggerTypeChecker.isReference(schema) ||
260
- SwaggerTypeChecker.isBoolean(schema) ||
261
- SwaggerTypeChecker.isNumber(schema) ||
262
- SwaggerTypeChecker.isString(schema) ||
263
- SwaggerTypeChecker.isUnknown(schema) ||
264
- (SwaggerTypeChecker.isAnyOf(schema) &&
265
- schema.anyOf.every(isNotObjectLiteral)) ||
266
- (SwaggerTypeChecker.isOneOf(schema) &&
267
- schema.oneOf.every(isNotObjectLiteral)) ||
268
- (SwaggerTypeChecker.isArray(schema) && isNotObjectLiteral(schema.items));
269
-
270
- const emplaceBodySchema =
271
- (from: "request" | "response") =>
272
- (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
273
- (meta?: {
274
- description?: string;
275
- content?: ISwaggerRoute.IContent;
276
- "x-nestia-encrypted"?: boolean;
277
- }): false | null | IMigrateRoute.IBody => {
278
- if (!meta?.content) return null;
279
-
280
- const entries: [string, { schema: ISwaggerSchema }][] = Object.entries(
281
- meta.content,
282
- );
283
- const json = entries.find((e) =>
284
- meta["x-nestia-encrypted"] === true
285
- ? e[0].includes("text/plain") || e[0].includes("application/json")
286
- : e[0].includes("application/json") || e[0].includes("*/*"),
287
- );
288
- if (json) {
289
- const { schema } = json[1];
290
- return {
291
- type: "application/json",
292
- key: "body",
293
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
294
- "x-nestia-encrypted": meta["x-nestia-encrypted"],
295
- };
296
- }
297
-
298
- const query = entries.find((e) =>
299
- e[0].includes("application/x-www-form-urlencoded"),
300
- );
301
- if (query) {
302
- const { schema } = query[1];
303
- return {
304
- type: "application/x-www-form-urlencoded",
305
- key: "body",
306
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
307
- };
308
- }
309
-
310
- const text = entries.find((e) => e[0].includes("text/plain"));
311
- if (text)
312
- return { type: "text/plain", key: "body", schema: { type: "string" } };
313
-
314
- if (from === "request") {
315
- const multipart = entries.find((e) =>
316
- e[0].includes("multipart/form-data"),
317
- );
318
- if (multipart) {
319
- const { schema } = multipart[1];
320
- return {
321
- type: "multipart/form-data",
322
- key: "body",
323
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
324
- };
325
- }
326
- }
327
- return false;
328
- };
329
-
330
- const emplaceReference =
331
- (swagger: ISwagger) =>
332
- (name: string) =>
333
- (schema: ISwaggerSchema): ISwaggerSchema.IReference => {
334
- swagger.components.schemas ??= {};
335
- swagger.components.schemas[name] = schema;
336
- return { $ref: `#/components/schemas/${name}` };
337
- };
338
- }
339
-
340
- const SUPPORTED_METHODS: Set<string> = new Set([
341
- "GET",
342
- "POST",
343
- "PUT",
344
- "PATCH",
345
- "DELETE",
346
- "HEAD",
347
- ]);
1
+ import { Escaper } from "typia/lib/utils/Escaper";
2
+
3
+ import { ISwagger } from "../module";
4
+ import { IMigrateRoute } from "../structures/IMigrateRoute";
5
+ import { ISwaggerSchema } from "../structures/ISwaggeSchema";
6
+ import { ISwaggerRoute } from "../structures/ISwaggerRoute";
7
+ import { StringUtil } from "../utils/StringUtil";
8
+ import { SwaggerSwaggerChecker } from "../utils/SwaggerTypeChecker";
9
+
10
+ export namespace MigrateMethodAnalzyer {
11
+ export const analyze =
12
+ (swagger: ISwagger) =>
13
+ (props: { path: string; method: string }) =>
14
+ (route: ISwaggerRoute): IMigrateRoute | null => {
15
+ const body = emplaceBodySchema("request")(
16
+ emplaceReference(swagger)("body"),
17
+ )(route.requestBody);
18
+ const success = emplaceBodySchema("response")(
19
+ emplaceReference(swagger)("response"),
20
+ )(route.responses?.["201"] ?? route.responses?.["200"]);
21
+
22
+ const failures: string[] = [];
23
+ if (body === false)
24
+ failures.push(
25
+ `supports only "application/json", "application/x-www-form-urlencoded", "multipart/form-data" and "text/plain" content type in the request body.`,
26
+ );
27
+ if (success === false)
28
+ failures.push(
29
+ `supports only "application/json", "application/x-www-form-urlencoded" and "text/plain" content type in the response body.`,
30
+ );
31
+ if (SUPPORTED_METHODS.has(props.method.toUpperCase()) === false)
32
+ failures.push(`does not support ${props.method.toUpperCase()} method.`);
33
+ if (failures.length) {
34
+ console.log(
35
+ `Failed to migrate ${props.method.toUpperCase()} ${props.path}`,
36
+ ...failures.map((f) => ` - ${f}`),
37
+ );
38
+ return null;
39
+ }
40
+
41
+ const [headers, query] = ["header", "query"].map((type) => {
42
+ const parameters = (route.parameters ?? []).filter(
43
+ (p) => p.in === type,
44
+ );
45
+ if (parameters.length === 0) return null;
46
+
47
+ const objects = parameters
48
+ .map((p) =>
49
+ SwaggerSwaggerChecker.isObject(p.schema)
50
+ ? p.schema
51
+ : SwaggerSwaggerChecker.isReference(p.schema) &&
52
+ SwaggerSwaggerChecker.isObject(
53
+ (swagger.components.schemas ?? {})[
54
+ p.schema.$ref.replace(`#/components/schemas/`, ``)
55
+ ] ?? {},
56
+ )
57
+ ? p.schema
58
+ : null!,
59
+ )
60
+ .filter((s) => !!s);
61
+ const primitives = parameters.filter(
62
+ (p) =>
63
+ SwaggerSwaggerChecker.isBoolean(p.schema) ||
64
+ SwaggerSwaggerChecker.isNumber(p.schema) ||
65
+ SwaggerSwaggerChecker.isInteger(p.schema) ||
66
+ SwaggerSwaggerChecker.isString(p.schema) ||
67
+ SwaggerSwaggerChecker.isArray(p.schema),
68
+ );
69
+ if (objects.length === 1 && primitives.length === 0) return objects[0];
70
+ else if (objects.length > 1)
71
+ throw new Error(
72
+ `Error on nestia.migrate.RouteProgrammer.analze(): ${type} typed parameters must be only one object type - ${StringUtil.capitalize(
73
+ props.method,
74
+ )} "${props.path}".`,
75
+ );
76
+
77
+ const dto: ISwaggerSchema.IObject | null = objects[0]
78
+ ? SwaggerSwaggerChecker.isObject(objects[0])
79
+ ? objects[0]
80
+ : ((swagger.components.schemas ?? {})[
81
+ (objects[0] as ISwaggerSchema.IReference).$ref.replace(
82
+ `#/components/schemas/`,
83
+ ``,
84
+ )
85
+ ] as ISwaggerSchema.IObject)
86
+ : null;
87
+ const entire: ISwaggerSchema.IObject[] = [
88
+ ...objects.map((o) =>
89
+ SwaggerSwaggerChecker.isObject(o)
90
+ ? o
91
+ : (swagger.components.schemas?.[
92
+ o.$ref.replace(`#/components/schemas/`, ``)
93
+ ]! as ISwaggerSchema.IObject),
94
+ ),
95
+ {
96
+ type: "object",
97
+ properties: Object.fromEntries([
98
+ ...primitives.map((p) => [
99
+ p.name,
100
+ {
101
+ ...p.schema,
102
+ description: p.schema.description ?? p.description,
103
+ },
104
+ ]),
105
+ ...(dto ? Object.entries(dto.properties ?? {}) : []),
106
+ ]),
107
+ required: [
108
+ ...primitives.filter((p) => p.required).map((p) => p.name!),
109
+ ...(dto ? dto.required ?? [] : []),
110
+ ],
111
+ },
112
+ ];
113
+ return parameters.length === 0
114
+ ? null
115
+ : emplaceReference(swagger)(
116
+ StringUtil.pascal(`I/Api/${props.path}`) +
117
+ "." +
118
+ StringUtil.pascal(`${props.method}/${type}`),
119
+ )({
120
+ type: "object",
121
+ properties: Object.fromEntries([
122
+ ...new Map<string, ISwaggerSchema>(
123
+ entire
124
+ .map((o) =>
125
+ Object.entries(o.properties ?? {}).map(
126
+ ([name, schema]) =>
127
+ [
128
+ name,
129
+ {
130
+ ...schema,
131
+ description:
132
+ schema.description ?? schema.description,
133
+ } as ISwaggerSchema,
134
+ ] as const,
135
+ ),
136
+ )
137
+ .flat(),
138
+ ),
139
+ ]),
140
+ required: [
141
+ ...new Set(entire.map((o) => o.required ?? []).flat()),
142
+ ],
143
+ });
144
+ });
145
+
146
+ const parameterNames: string[] = StringUtil.splitWithNormalization(
147
+ props.path,
148
+ )
149
+ .filter((str) => str[0] === "{" || str[0] === ":")
150
+ .map((str) =>
151
+ str[0] === "{" ? str.substring(1, str.length - 1) : str.substring(1),
152
+ );
153
+ if (
154
+ parameterNames.length !==
155
+ (route.parameters ?? []).filter((p) => p.in === "path").length
156
+ ) {
157
+ console.log(
158
+ `Failed to migrate ${props.method.toUpperCase()} ${
159
+ props.path
160
+ }: number of path parameters are not matched with its full path.`,
161
+ );
162
+ return null;
163
+ }
164
+ return {
165
+ name: "@lazy",
166
+ path: props.path,
167
+ method: props.method,
168
+ accessor: ["@lazy"],
169
+ headers: headers
170
+ ? {
171
+ key: "headers",
172
+ schema: headers,
173
+ }
174
+ : null,
175
+ parameters: (route.parameters ?? [])
176
+ .filter((p) => p.in === "path")
177
+ .map((p, i) => ({
178
+ key: (() => {
179
+ let key: string = StringUtil.normalize(parameterNames[i]);
180
+ if (Escaper.variable(key)) return key;
181
+ while (true) {
182
+ key = "_" + key;
183
+ if (!parameterNames.some((s) => s === key)) return key;
184
+ }
185
+ })(),
186
+ schema: {
187
+ ...p.schema,
188
+ description: p.schema.description ?? p.description,
189
+ },
190
+ })),
191
+ query: query
192
+ ? {
193
+ key: "query",
194
+ schema: query,
195
+ }
196
+ : null,
197
+ body: body as IMigrateRoute.IBody,
198
+ success: success as IMigrateRoute.IBody,
199
+ exceptions: Object.fromEntries(
200
+ Object.entries(route.responses ?? {})
201
+ .filter(
202
+ ([key, value]) =>
203
+ key !== "200" &&
204
+ key !== "201" &&
205
+ !!value.content?.["application/json"],
206
+ )
207
+ .map(([key, value]) => [
208
+ key,
209
+ {
210
+ description: value.description,
211
+ schema: value.content?.["application/json"]?.schema ?? {},
212
+ },
213
+ ]),
214
+ ),
215
+ deprecated: route.deprecated ?? false,
216
+ description: describe(route),
217
+ tags: route.tags ?? [],
218
+ };
219
+ };
220
+
221
+ const describe = (route: ISwaggerRoute): string | undefined => {
222
+ const commentTags: string[] = [];
223
+ const add = (text: string) => {
224
+ if (commentTags.every((line) => line !== text)) commentTags.push(text);
225
+ };
226
+
227
+ let description: string | undefined = route.description;
228
+ if (route.summary) {
229
+ const emended: string = route.summary.endsWith(".")
230
+ ? route.summary
231
+ : route.summary + ".";
232
+ if (
233
+ description !== undefined &&
234
+ !description?.startsWith(route.summary) &&
235
+ !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
236
+ )
237
+ description = `${emended}\n${description}`;
238
+ }
239
+ if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
240
+ if (route.deprecated) add("@deprecated");
241
+ for (const security of route.security ?? [])
242
+ for (const [name, scopes] of Object.entries(security))
243
+ add(`@security ${[name, ...scopes].join("")}`);
244
+ for (const jsDocTag of route["x-nestia-jsDocTags"] ?? [])
245
+ if (jsDocTag.text?.length)
246
+ add(
247
+ `@${jsDocTag.name} ${jsDocTag.text
248
+ .map((text) => text.text)
249
+ .join("")}`,
250
+ );
251
+ else add(`@${jsDocTag.name}`);
252
+ return description?.length
253
+ ? commentTags.length
254
+ ? `${description}\n\n${commentTags.join("\n")}`
255
+ : description
256
+ : commentTags.join("\n");
257
+ };
258
+
259
+ const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
260
+ SwaggerSwaggerChecker.isReference(schema) ||
261
+ SwaggerSwaggerChecker.isBoolean(schema) ||
262
+ SwaggerSwaggerChecker.isNumber(schema) ||
263
+ SwaggerSwaggerChecker.isString(schema) ||
264
+ SwaggerSwaggerChecker.isUnknown(schema) ||
265
+ (SwaggerSwaggerChecker.isAnyOf(schema) &&
266
+ schema.anyOf.every(isNotObjectLiteral)) ||
267
+ (SwaggerSwaggerChecker.isOneOf(schema) &&
268
+ schema.oneOf.every(isNotObjectLiteral)) ||
269
+ (SwaggerSwaggerChecker.isArray(schema) && isNotObjectLiteral(schema.items));
270
+
271
+ const emplaceBodySchema =
272
+ (from: "request" | "response") =>
273
+ (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
274
+ (meta?: {
275
+ description?: string;
276
+ content?: ISwaggerRoute.IContent;
277
+ "x-nestia-encrypted"?: boolean;
278
+ }): false | null | IMigrateRoute.IBody => {
279
+ if (!meta?.content) return null;
280
+
281
+ const entries: [string, { schema: ISwaggerSchema }][] = Object.entries(
282
+ meta.content,
283
+ );
284
+ const json = entries.find((e) =>
285
+ meta["x-nestia-encrypted"] === true
286
+ ? e[0].includes("text/plain") || e[0].includes("application/json")
287
+ : e[0].includes("application/json") || e[0].includes("*/*"),
288
+ );
289
+ if (json) {
290
+ const { schema } = json[1];
291
+ return {
292
+ type: "application/json",
293
+ key: "body",
294
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
295
+ "x-nestia-encrypted": meta["x-nestia-encrypted"],
296
+ };
297
+ }
298
+
299
+ const query = entries.find((e) =>
300
+ e[0].includes("application/x-www-form-urlencoded"),
301
+ );
302
+ if (query) {
303
+ const { schema } = query[1];
304
+ return {
305
+ type: "application/x-www-form-urlencoded",
306
+ key: "body",
307
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
308
+ };
309
+ }
310
+
311
+ const text = entries.find((e) => e[0].includes("text/plain"));
312
+ if (text)
313
+ return { type: "text/plain", key: "body", schema: { type: "string" } };
314
+
315
+ if (from === "request") {
316
+ const multipart = entries.find((e) =>
317
+ e[0].includes("multipart/form-data"),
318
+ );
319
+ if (multipart) {
320
+ const { schema } = multipart[1];
321
+ return {
322
+ type: "multipart/form-data",
323
+ key: "body",
324
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
325
+ };
326
+ }
327
+ }
328
+ return false;
329
+ };
330
+
331
+ const emplaceReference =
332
+ (swagger: ISwagger) =>
333
+ (name: string) =>
334
+ (schema: ISwaggerSchema): ISwaggerSchema.IReference => {
335
+ swagger.components.schemas ??= {};
336
+ swagger.components.schemas[name] = schema;
337
+ return { $ref: `#/components/schemas/${name}` };
338
+ };
339
+ }
340
+
341
+ const SUPPORTED_METHODS: Set<string> = new Set([
342
+ "GET",
343
+ "POST",
344
+ "PUT",
345
+ "PATCH",
346
+ "DELETE",
347
+ "HEAD",
348
+ ]);