@nestia/sdk 2.0.0-dev.20230991 → 2.0.1
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/assets/bundle/api/utils/NestiaSimulator.ts +5 -14
- package/lib/INestiaConfig.d.ts +60 -47
- package/lib/NestiaSdkApplication.d.ts +6 -5
- package/lib/NestiaSdkApplication.js +30 -87
- package/lib/NestiaSdkApplication.js.map +1 -1
- package/lib/analyses/ControllerAnalyzer.js +8 -5
- package/lib/analyses/ControllerAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectAnalyzer.js +9 -1
- package/lib/analyses/ReflectAnalyzer.js.map +1 -1
- package/lib/executable/internal/NestiaConfigLoader.d.ts +7 -0
- package/lib/executable/internal/NestiaConfigLoader.js +582 -0
- package/lib/executable/internal/NestiaConfigLoader.js.map +1 -0
- package/lib/executable/internal/NestiaProjectGetter.d.ts +3 -0
- package/lib/executable/internal/NestiaProjectGetter.js +28 -0
- package/lib/executable/internal/NestiaProjectGetter.js.map +1 -0
- package/lib/executable/internal/NestiaSdkCommand.d.ts +3 -3
- package/lib/executable/internal/NestiaSdkCommand.js +13 -104
- package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
- package/lib/executable/internal/{nestia.config.getter.js → nestia.project.getter.js} +3 -3
- package/lib/executable/internal/nestia.project.getter.js.map +1 -0
- package/lib/executable/sdk.js +3 -3
- package/lib/executable/sdk.js.map +1 -1
- package/lib/generates/SdkGenerator.d.ts +2 -1
- package/lib/generates/SdkGenerator.js +7 -3
- package/lib/generates/SdkGenerator.js.map +1 -1
- package/lib/generates/SwaggerGenerator.js +26 -9
- package/lib/generates/SwaggerGenerator.js.map +1 -1
- package/lib/generates/internal/E2eFileProgrammer.js +21 -15
- package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
- package/lib/generates/internal/{DistributionComposer.d.ts → SdkDistributionComposer.d.ts} +1 -1
- package/lib/generates/internal/{DistributionComposer.js → SdkDistributionComposer.js} +8 -12
- package/lib/generates/internal/SdkDistributionComposer.js.map +1 -0
- package/lib/generates/internal/SdkDtoGenerator.d.ts +9 -0
- package/lib/generates/internal/SdkDtoGenerator.js +264 -0
- package/lib/generates/internal/SdkDtoGenerator.js.map +1 -0
- package/lib/generates/internal/SdkFileProgrammer.js +8 -7
- package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkFunctionProgrammer.js +32 -22
- package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkImportWizard.d.ts +2 -0
- package/lib/generates/internal/SdkImportWizard.js +10 -0
- package/lib/generates/internal/SdkImportWizard.js.map +1 -1
- package/lib/generates/internal/SdkSimulationProgrammer.js +61 -25
- package/lib/generates/internal/SdkSimulationProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkTypeDefiner.d.ts +11 -0
- package/lib/generates/internal/SdkTypeDefiner.js +82 -0
- package/lib/generates/internal/SdkTypeDefiner.js.map +1 -0
- package/lib/generates/internal/SwaggerSchemaGenerator.d.ts +1 -1
- package/lib/generates/internal/SwaggerSchemaGenerator.js +67 -75
- package/lib/generates/internal/SwaggerSchemaGenerator.js.map +1 -1
- package/lib/generates/internal/SwaggerSchemaValidator.js +0 -8
- package/lib/generates/internal/SwaggerSchemaValidator.js.map +1 -1
- package/lib/structures/IController.d.ts +2 -0
- package/lib/structures/IRoute.d.ts +9 -2
- package/lib/utils/ImportDictionary.d.ts +1 -2
- package/lib/utils/ImportDictionary.js +28 -24
- package/lib/utils/ImportDictionary.js.map +1 -1
- package/package.json +8 -8
- package/src/INestiaConfig.ts +65 -50
- package/src/NestiaSdkApplication.ts +39 -84
- package/src/analyses/ControllerAnalyzer.ts +10 -8
- package/src/analyses/ReflectAnalyzer.ts +8 -0
- package/src/executable/internal/NestiaConfigLoader.ts +82 -0
- package/src/executable/internal/NestiaProjectGetter.ts +11 -0
- package/src/executable/internal/NestiaSdkCommand.ts +23 -146
- package/src/executable/internal/{nestia.config.getter.ts → nestia.project.getter.ts} +2 -2
- package/src/executable/sdk.ts +3 -3
- package/src/generates/SdkGenerator.ts +9 -2
- package/src/generates/SwaggerGenerator.ts +37 -9
- package/src/generates/internal/E2eFileProgrammer.ts +33 -20
- package/src/generates/internal/{DistributionComposer.ts → SdkDistributionComposer.ts} +3 -6
- package/src/generates/internal/SdkDtoGenerator.ts +384 -0
- package/src/generates/internal/SdkFileProgrammer.ts +8 -7
- package/src/generates/internal/SdkFunctionProgrammer.ts +71 -37
- package/src/generates/internal/SdkImportWizard.ts +14 -0
- package/src/generates/internal/SdkSimulationProgrammer.ts +71 -37
- package/src/generates/internal/SdkTypeDefiner.ts +120 -0
- package/src/generates/internal/SwaggerSchemaGenerator.ts +94 -92
- package/src/generates/internal/SwaggerSchemaValidator.ts +0 -12
- package/src/structures/IController.ts +2 -0
- package/src/structures/IRoute.ts +10 -2
- package/src/utils/ImportDictionary.ts +29 -26
- package/lib/executable/internal/NestiaConfigCompilerOptions.d.ts +0 -12
- package/lib/executable/internal/NestiaConfigCompilerOptions.js +0 -18
- package/lib/executable/internal/NestiaConfigCompilerOptions.js.map +0 -1
- package/lib/executable/internal/NestiaSdkConfig.d.ts +0 -4
- package/lib/executable/internal/NestiaSdkConfig.js +0 -1019
- package/lib/executable/internal/NestiaSdkConfig.js.map +0 -1
- package/lib/executable/internal/nestia.config.getter.js.map +0 -1
- package/lib/generates/internal/DistributionComposer.js.map +0 -1
- package/src/executable/internal/NestiaConfigCompilerOptions.ts +0 -19
- package/src/executable/internal/NestiaSdkConfig.ts +0 -36
- /package/lib/executable/internal/{nestia.config.getter.d.ts → nestia.project.getter.d.ts} +0 -0
|
@@ -2,27 +2,21 @@ import { INestiaConfig } from "../../INestiaConfig";
|
|
|
2
2
|
import { IRoute } from "../../structures/IRoute";
|
|
3
3
|
import { ImportDictionary } from "../../utils/ImportDictionary";
|
|
4
4
|
import { SdkImportWizard } from "./SdkImportWizard";
|
|
5
|
+
import { SdkTypeDefiner } from "./SdkTypeDefiner";
|
|
5
6
|
|
|
6
7
|
export namespace SdkSimulationProgrammer {
|
|
7
8
|
export const generate =
|
|
8
9
|
(config: INestiaConfig) =>
|
|
9
10
|
(importer: ImportDictionary) =>
|
|
10
11
|
(route: IRoute): string => {
|
|
11
|
-
const output: boolean =
|
|
12
|
-
|
|
13
|
-
`return random(`,
|
|
14
|
-
` typeof connection.simulate === 'object' &&`,
|
|
15
|
-
` connection.simulate !== null`,
|
|
16
|
-
` ? connection.simulate`,
|
|
17
|
-
` : undefined`,
|
|
18
|
-
`);`,
|
|
19
|
-
];
|
|
12
|
+
const output: boolean =
|
|
13
|
+
config.propagate === true || route.output.typeName !== "void";
|
|
20
14
|
const body: string[] = [
|
|
21
15
|
...(route.parameters.filter((p) => p.category !== "headers")
|
|
22
16
|
.length !== 0
|
|
23
|
-
? assert(config)(importer)(route
|
|
17
|
+
? assert(config)(importer)(route)
|
|
24
18
|
: []),
|
|
25
|
-
...(output ? returns() : []),
|
|
19
|
+
...(output ? returns(config)(route) : []),
|
|
26
20
|
];
|
|
27
21
|
return [
|
|
28
22
|
`export const simulate = async (`,
|
|
@@ -52,7 +46,7 @@ export namespace SdkSimulationProgrammer {
|
|
|
52
46
|
? "Query"
|
|
53
47
|
: "Input"
|
|
54
48
|
}`
|
|
55
|
-
: p
|
|
49
|
+
: SdkTypeDefiner.name(config)(importer)(p)
|
|
56
50
|
},`,
|
|
57
51
|
),
|
|
58
52
|
`): Promise<${output ? "Output" : "void"}> => {`,
|
|
@@ -66,8 +60,9 @@ export namespace SdkSimulationProgrammer {
|
|
|
66
60
|
const assert =
|
|
67
61
|
(config: INestiaConfig) =>
|
|
68
62
|
(importer: ImportDictionary) =>
|
|
69
|
-
(
|
|
70
|
-
|
|
63
|
+
(route: IRoute): string[] => {
|
|
64
|
+
const typia = SdkImportWizard.typia(importer);
|
|
65
|
+
const func: string[] = [
|
|
71
66
|
`const assert = ${importer.internal({
|
|
72
67
|
file: `${config.output}/utils/NestiaSimulator.ts`,
|
|
73
68
|
instance: "NestiaSimulator",
|
|
@@ -75,34 +70,73 @@ export namespace SdkSimulationProgrammer {
|
|
|
75
70
|
})}.assert({`,
|
|
76
71
|
` method: METADATA.method,`,
|
|
77
72
|
` host: connection.host,`,
|
|
78
|
-
` path: path(${parameters
|
|
73
|
+
` path: path(${route.parameters
|
|
79
74
|
.filter(
|
|
80
75
|
(p) => p.category === "param" || p.category === "query",
|
|
81
76
|
)
|
|
82
77
|
.map((p) => p.name)
|
|
83
|
-
.join(", ")})
|
|
78
|
+
.join(", ")}),`,
|
|
79
|
+
` contentType: ${JSON.stringify(route.output.contentType)},`,
|
|
84
80
|
`});`,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
81
|
+
];
|
|
82
|
+
const individual: string[] = route.parameters
|
|
83
|
+
.filter((p) => p.category !== "headers")
|
|
84
|
+
.map((p) =>
|
|
85
|
+
p.category === "body"
|
|
86
|
+
? `assert.body(() => ${typia}.assert(${p.name}));`
|
|
87
|
+
: p.category === "query"
|
|
88
|
+
? `assert.query(() => ${typia}.assert(${p.name}));`
|
|
89
|
+
: p.category === "headers"
|
|
90
|
+
? `assert.headers(() => ${typia}.assert(connection.headers);`
|
|
91
|
+
: `assert.param("${p.field}")(() => ${typia}.assert(${p.name}));`,
|
|
92
|
+
);
|
|
93
|
+
if (config.propagate !== true) return [...func, ...individual];
|
|
94
|
+
|
|
95
|
+
return [
|
|
96
|
+
...func,
|
|
97
|
+
`try {`,
|
|
98
|
+
...individual.map((l) => ` ${l}`),
|
|
99
|
+
`} catch (exp) {`,
|
|
100
|
+
` if (!${typia}.is<${SdkImportWizard.HttpError(
|
|
101
|
+
importer,
|
|
102
|
+
)}>(exp)) throw exp;`,
|
|
103
|
+
` return {`,
|
|
104
|
+
` success: false,`,
|
|
105
|
+
` status: exp.status,`,
|
|
106
|
+
` headers: exp.headers,`,
|
|
107
|
+
` data: exp.toJSON().message,`,
|
|
108
|
+
` } as any;`,
|
|
109
|
+
`}`,
|
|
110
|
+
];
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const returns =
|
|
114
|
+
(config: INestiaConfig) =>
|
|
115
|
+
(route: IRoute): string[] => {
|
|
116
|
+
const random = (prefix: string, postfix: string) =>
|
|
117
|
+
route.output.typeName === "void"
|
|
118
|
+
? [`${prefix} undefined${postfix}`]
|
|
119
|
+
: [
|
|
120
|
+
`${prefix} random(`,
|
|
121
|
+
` typeof connection.simulate === 'object' &&`,
|
|
122
|
+
` connection.simulate !== null`,
|
|
123
|
+
` ? connection.simulate`,
|
|
124
|
+
` : undefined`,
|
|
125
|
+
`)${postfix}`,
|
|
126
|
+
];
|
|
127
|
+
if (config.propagate !== true) return random("return", ";");
|
|
128
|
+
|
|
129
|
+
return [
|
|
130
|
+
`return {`,
|
|
131
|
+
` success: true,`,
|
|
132
|
+
` status: ${
|
|
133
|
+
route.status ?? route.method === "POST" ? 201 : 200
|
|
134
|
+
},`,
|
|
135
|
+
` headers: {`,
|
|
136
|
+
` "Content-Type": "${route.output.contentType}",`,
|
|
137
|
+
` },`,
|
|
138
|
+
...random("data:", ",").map((r) => ` ${r}`),
|
|
139
|
+
`}`,
|
|
106
140
|
];
|
|
107
141
|
};
|
|
108
142
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { INestiaConfig } from "../../INestiaConfig";
|
|
2
|
+
import { IRoute } from "../../structures/IRoute";
|
|
3
|
+
import { ImportDictionary } from "../../utils/ImportDictionary";
|
|
4
|
+
import { SdkDtoGenerator } from "./SdkDtoGenerator";
|
|
5
|
+
|
|
6
|
+
export namespace SdkTypeDefiner {
|
|
7
|
+
export const name =
|
|
8
|
+
(config: INestiaConfig) =>
|
|
9
|
+
(importer: ImportDictionary) =>
|
|
10
|
+
(p: IRoute.IParameter | IRoute.IOutput): string =>
|
|
11
|
+
p.metadata
|
|
12
|
+
? SdkDtoGenerator.decode(config)(importer)(p.metadata)
|
|
13
|
+
: p.typeName;
|
|
14
|
+
|
|
15
|
+
export const headers =
|
|
16
|
+
(config: INestiaConfig) =>
|
|
17
|
+
(importer: ImportDictionary) =>
|
|
18
|
+
(param: IRoute.IParameter): string => {
|
|
19
|
+
const type: string = name(config)(importer)(param);
|
|
20
|
+
if (config.primitive === false) return type;
|
|
21
|
+
|
|
22
|
+
const resolved: string = importer.external({
|
|
23
|
+
type: true,
|
|
24
|
+
library: "@nestia/fetcher",
|
|
25
|
+
instance: "Resolved",
|
|
26
|
+
});
|
|
27
|
+
return `${resolved}<${type}>`;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const query =
|
|
31
|
+
(config: INestiaConfig) =>
|
|
32
|
+
(importer: ImportDictionary) =>
|
|
33
|
+
(param: IRoute.IParameter): string => {
|
|
34
|
+
const type: string = name(config)(importer)(param);
|
|
35
|
+
if (config.primitive === false) return type;
|
|
36
|
+
|
|
37
|
+
const resolved: string = importer.external({
|
|
38
|
+
type: true,
|
|
39
|
+
library: "@nestia/fetcher",
|
|
40
|
+
instance: "Resolved",
|
|
41
|
+
});
|
|
42
|
+
return `${resolved}<${type}>`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const input =
|
|
46
|
+
(config: INestiaConfig) =>
|
|
47
|
+
(importer: ImportDictionary) =>
|
|
48
|
+
(param: IRoute.IParameter): string => {
|
|
49
|
+
const type: string = name(config)(importer)(param);
|
|
50
|
+
if (config.primitive === false) return type;
|
|
51
|
+
|
|
52
|
+
const primitive: string = importer.external({
|
|
53
|
+
type: true,
|
|
54
|
+
library: "@nestia/fetcher",
|
|
55
|
+
instance: "Primitive",
|
|
56
|
+
});
|
|
57
|
+
return `${primitive}<${type}>`;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const output =
|
|
61
|
+
(config: INestiaConfig) =>
|
|
62
|
+
(importer: ImportDictionary) =>
|
|
63
|
+
(route: IRoute): string => {
|
|
64
|
+
if (config.propagate !== true) {
|
|
65
|
+
const type: string = name(config)(importer)(route.output);
|
|
66
|
+
if (type === "void" || config.primitive === false) return type;
|
|
67
|
+
|
|
68
|
+
const primitive: string = importer.external({
|
|
69
|
+
type: true,
|
|
70
|
+
library: "@nestia/fetcher",
|
|
71
|
+
instance: "Primitive",
|
|
72
|
+
});
|
|
73
|
+
return `${primitive}<${type}>`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const propagation: string = importer.external({
|
|
77
|
+
type: true,
|
|
78
|
+
library: "@nestia/fetcher",
|
|
79
|
+
instance: "IPropagation",
|
|
80
|
+
});
|
|
81
|
+
const branches: IBranch[] = [
|
|
82
|
+
{
|
|
83
|
+
status: String(
|
|
84
|
+
route.status ?? route.method === "POST" ? 201 : 200,
|
|
85
|
+
),
|
|
86
|
+
type: name(config)(importer)(route.output),
|
|
87
|
+
},
|
|
88
|
+
...Object.entries(route.exceptions).map(([status, value]) => ({
|
|
89
|
+
status,
|
|
90
|
+
type: name(config)(importer)(value),
|
|
91
|
+
})),
|
|
92
|
+
];
|
|
93
|
+
return (
|
|
94
|
+
`${propagation}<{\n` +
|
|
95
|
+
branches
|
|
96
|
+
.map(
|
|
97
|
+
(b) =>
|
|
98
|
+
` ${
|
|
99
|
+
b.status.endsWith("XX")
|
|
100
|
+
? `"${b.status}"`
|
|
101
|
+
: b.status
|
|
102
|
+
}: ${b.type};`,
|
|
103
|
+
)
|
|
104
|
+
.join("\n") +
|
|
105
|
+
"\n" +
|
|
106
|
+
` }${route.status ? `, ${route.status}` : ""}>`
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const responseBody =
|
|
111
|
+
(config: INestiaConfig) =>
|
|
112
|
+
(importer: ImportDictionary) =>
|
|
113
|
+
(route: IRoute): string =>
|
|
114
|
+
output({ ...config, propagate: false })(importer)(route);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface IBranch {
|
|
118
|
+
status: string;
|
|
119
|
+
type: string;
|
|
120
|
+
}
|
|
@@ -49,7 +49,7 @@ export namespace SwaggerSchemaGenerator {
|
|
|
49
49
|
...result.errors.map((e) => ({
|
|
50
50
|
...e,
|
|
51
51
|
route,
|
|
52
|
-
from: `response
|
|
52
|
+
from: `response(status: ${status})`,
|
|
53
53
|
})),
|
|
54
54
|
);
|
|
55
55
|
|
|
@@ -57,14 +57,14 @@ export namespace SwaggerSchemaGenerator {
|
|
|
57
57
|
description: exp.description ?? "",
|
|
58
58
|
content: {
|
|
59
59
|
"application/json": {
|
|
60
|
-
schema:
|
|
60
|
+
schema: coalesce(props)(result),
|
|
61
61
|
},
|
|
62
62
|
},
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// FROM COMMENT TAGS -> ANY
|
|
67
|
-
for (const tag of route.
|
|
67
|
+
for (const tag of route.jsDocTags) {
|
|
68
68
|
if (tag.name !== "throw" && tag.name !== "throws") continue;
|
|
69
69
|
|
|
70
70
|
const text: string | undefined = tag.text?.find(
|
|
@@ -133,7 +133,7 @@ export namespace SwaggerSchemaGenerator {
|
|
|
133
133
|
...result.errors.map((e) => ({
|
|
134
134
|
...e,
|
|
135
135
|
route,
|
|
136
|
-
from:
|
|
136
|
+
from: "response",
|
|
137
137
|
})),
|
|
138
138
|
);
|
|
139
139
|
|
|
@@ -151,7 +151,7 @@ export namespace SwaggerSchemaGenerator {
|
|
|
151
151
|
? undefined
|
|
152
152
|
: {
|
|
153
153
|
[route.output.contentType]: {
|
|
154
|
-
schema:
|
|
154
|
+
schema: coalesce(props)(result),
|
|
155
155
|
},
|
|
156
156
|
},
|
|
157
157
|
"x-nestia-encrypted": route.encrypted,
|
|
@@ -180,7 +180,7 @@ export namespace SwaggerSchemaGenerator {
|
|
|
180
180
|
...result.errors.map((e) => ({
|
|
181
181
|
...e,
|
|
182
182
|
route,
|
|
183
|
-
from:
|
|
183
|
+
from: param.name,
|
|
184
184
|
})),
|
|
185
185
|
);
|
|
186
186
|
|
|
@@ -198,7 +198,7 @@ export namespace SwaggerSchemaGenerator {
|
|
|
198
198
|
);
|
|
199
199
|
|
|
200
200
|
// RETURNS WITH LAZY CONSTRUCTION
|
|
201
|
-
const schema: IJsonSchema =
|
|
201
|
+
const schema: IJsonSchema = coalesce(props)(result);
|
|
202
202
|
return {
|
|
203
203
|
description: encrypted
|
|
204
204
|
? `${warning.get(!!description).get("request")}${
|
|
@@ -218,49 +218,13 @@ export namespace SwaggerSchemaGenerator {
|
|
|
218
218
|
export const parameter =
|
|
219
219
|
(props: IProps) =>
|
|
220
220
|
(route: IRoute) =>
|
|
221
|
-
(param: IRoute.IParameter): ISwaggerRoute.IParameter =>
|
|
221
|
+
(param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
|
|
222
222
|
param.category === "headers"
|
|
223
223
|
? headers(props)(route)(param)
|
|
224
224
|
: param.category === "param"
|
|
225
|
-
? path(props)(route)(param)
|
|
225
|
+
? [path(props)(route)(param)]
|
|
226
226
|
: query(props)(route)(param);
|
|
227
227
|
|
|
228
|
-
const headers =
|
|
229
|
-
(props: IProps) =>
|
|
230
|
-
(route: IRoute) =>
|
|
231
|
-
(param: IRoute.IParameter): ISwaggerRoute.IParameter => {
|
|
232
|
-
// ANALYZE TYPE WITH VALIDATIONS
|
|
233
|
-
const result = (() => {
|
|
234
|
-
const result = MetadataFactory.analyze(props.checker)({
|
|
235
|
-
escape: false,
|
|
236
|
-
constant: true,
|
|
237
|
-
absorb: true,
|
|
238
|
-
})(props.collection)(param.type);
|
|
239
|
-
if (result.success === false)
|
|
240
|
-
props.errors.push(
|
|
241
|
-
...result.errors.map((e) => ({
|
|
242
|
-
...e,
|
|
243
|
-
route,
|
|
244
|
-
from: `header parameter ${param.name}`,
|
|
245
|
-
})),
|
|
246
|
-
);
|
|
247
|
-
else if (result.data.objects.length === 1) {
|
|
248
|
-
// WHEN OBJECT CASE, VALIDATE IT MORE DETAILY
|
|
249
|
-
const again = MetadataFactory.analyze(props.checker)({
|
|
250
|
-
escape: false,
|
|
251
|
-
constant: true,
|
|
252
|
-
absorb: true,
|
|
253
|
-
validate: SwaggerSchemaValidator.headers,
|
|
254
|
-
})(new MetadataCollection())(param.type);
|
|
255
|
-
if (again.success === false) return again;
|
|
256
|
-
}
|
|
257
|
-
return result;
|
|
258
|
-
})();
|
|
259
|
-
|
|
260
|
-
// RETURNS WITH LAZY CONSTRUCTION
|
|
261
|
-
return lazyParameterReturns(props)(route)("header")(param, result);
|
|
262
|
-
};
|
|
263
|
-
|
|
264
228
|
const path =
|
|
265
229
|
(props: IProps) =>
|
|
266
230
|
(route: IRoute) =>
|
|
@@ -277,86 +241,124 @@ export namespace SwaggerSchemaGenerator {
|
|
|
277
241
|
...result.errors.map((e) => ({
|
|
278
242
|
...e,
|
|
279
243
|
route,
|
|
280
|
-
from:
|
|
244
|
+
from: param.name,
|
|
281
245
|
})),
|
|
282
246
|
);
|
|
283
247
|
|
|
284
248
|
// RETURNS WITH LAZY CONSTRUCTION
|
|
285
|
-
return
|
|
249
|
+
return lazy(props)(route)(param, result);
|
|
286
250
|
};
|
|
287
251
|
|
|
288
|
-
const
|
|
252
|
+
const headers =
|
|
289
253
|
(props: IProps) =>
|
|
290
254
|
(route: IRoute) =>
|
|
291
|
-
(param: IRoute.IParameter): ISwaggerRoute.IParameter =>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const result = MetadataFactory.analyze(props.checker)({
|
|
255
|
+
(param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
|
|
256
|
+
decomposible(props)(route)(param)(
|
|
257
|
+
MetadataFactory.analyze(props.checker)({
|
|
295
258
|
escape: false,
|
|
296
259
|
constant: true,
|
|
297
260
|
absorb: true,
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
route,
|
|
304
|
-
from: `query parameter ${param.name}`,
|
|
305
|
-
})),
|
|
306
|
-
);
|
|
307
|
-
} else if (result.data.objects.length === 1) {
|
|
308
|
-
// WHEN OBJECT CASE, VALIDATE IT MORE DETAILY
|
|
309
|
-
const again = MetadataFactory.analyze(props.checker)({
|
|
310
|
-
escape: false,
|
|
311
|
-
constant: true,
|
|
312
|
-
absorb: true,
|
|
313
|
-
validate: SwaggerSchemaValidator.query,
|
|
314
|
-
})(new MetadataCollection())(param.type);
|
|
315
|
-
if (again.success === false) return again;
|
|
316
|
-
}
|
|
317
|
-
return result;
|
|
318
|
-
})();
|
|
261
|
+
validate: param.custom
|
|
262
|
+
? SwaggerSchemaValidator.headers
|
|
263
|
+
: undefined,
|
|
264
|
+
})(new MetadataCollection())(param.type),
|
|
265
|
+
);
|
|
319
266
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
267
|
+
const query =
|
|
268
|
+
(props: IProps) =>
|
|
269
|
+
(route: IRoute) =>
|
|
270
|
+
(param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
|
|
271
|
+
decomposible(props)(route)(param)(
|
|
272
|
+
MetadataFactory.analyze(props.checker)({
|
|
273
|
+
escape: false,
|
|
274
|
+
constant: true,
|
|
275
|
+
absorb: true,
|
|
276
|
+
validate: param.custom
|
|
277
|
+
? SwaggerSchemaValidator.query
|
|
278
|
+
: undefined,
|
|
279
|
+
})(new MetadataCollection())(param.type),
|
|
280
|
+
);
|
|
323
281
|
|
|
324
|
-
const
|
|
282
|
+
const decomposible =
|
|
325
283
|
(props: IProps) =>
|
|
284
|
+
(route: IRoute) =>
|
|
285
|
+
(param: IRoute.IParameter) =>
|
|
326
286
|
(
|
|
327
287
|
result: ValidationPipe<Metadata, MetadataFactory.IError>,
|
|
328
|
-
):
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
288
|
+
): ISwaggerRoute.IParameter[] => {
|
|
289
|
+
if (result.success === false) {
|
|
290
|
+
props.errors.push(
|
|
291
|
+
...result.errors.map((e) => ({
|
|
292
|
+
...e,
|
|
293
|
+
route,
|
|
294
|
+
from: param.name,
|
|
295
|
+
})),
|
|
296
|
+
);
|
|
297
|
+
return [lazy(props)(route)(param, result)];
|
|
298
|
+
} else if (
|
|
299
|
+
props.config.decompose !== true ||
|
|
300
|
+
result.data.objects.length === 0
|
|
301
|
+
)
|
|
302
|
+
return [lazy(props)(route)(param, result)];
|
|
303
|
+
|
|
304
|
+
return result.data.objects[0].properties.map((p) => {
|
|
305
|
+
const schema = coalesce(props)({
|
|
306
|
+
success: true,
|
|
307
|
+
data: p.value,
|
|
308
|
+
});
|
|
309
|
+
return {
|
|
310
|
+
name: p.key.constants[0].values[0] as string,
|
|
311
|
+
in:
|
|
312
|
+
param.category === "headers"
|
|
313
|
+
? "header"
|
|
314
|
+
: param.category,
|
|
315
|
+
schema,
|
|
316
|
+
description: p.description ?? undefined,
|
|
317
|
+
required: p.value.isRequired(),
|
|
318
|
+
};
|
|
333
319
|
});
|
|
334
|
-
return schema;
|
|
335
320
|
};
|
|
336
321
|
|
|
337
|
-
const
|
|
322
|
+
const lazy =
|
|
338
323
|
(props: IProps) =>
|
|
339
324
|
(route: IRoute) =>
|
|
340
|
-
(inKeyword: string) =>
|
|
341
325
|
(
|
|
342
326
|
param: IRoute.IParameter,
|
|
343
327
|
result: ValidationPipe<Metadata, MetadataFactory.IError>,
|
|
344
|
-
) => {
|
|
345
|
-
const schema: IJsonSchema =
|
|
328
|
+
): ISwaggerRoute.IParameter => {
|
|
329
|
+
const schema: IJsonSchema = coalesce(props)(result);
|
|
346
330
|
return {
|
|
347
331
|
name: param.field ?? param.name,
|
|
348
|
-
in:
|
|
332
|
+
in:
|
|
333
|
+
param.category === "headers"
|
|
334
|
+
? "header"
|
|
335
|
+
: param.category === "param"
|
|
336
|
+
? "path"
|
|
337
|
+
: param.category,
|
|
349
338
|
schema,
|
|
350
339
|
description: describe(route, "param", param.name) ?? "",
|
|
351
340
|
required: result.success ? result.data.isRequired() : true,
|
|
352
341
|
};
|
|
353
342
|
};
|
|
354
343
|
|
|
355
|
-
|
|
344
|
+
const coalesce =
|
|
345
|
+
(props: IProps) =>
|
|
346
|
+
(
|
|
347
|
+
result: ValidationPipe<Metadata, MetadataFactory.IError>,
|
|
348
|
+
): IJsonSchema => {
|
|
349
|
+
const schema: IJsonSchema = {} as any;
|
|
350
|
+
props.tuples.push({
|
|
351
|
+
metadata: result.success ? result.data : any.get(),
|
|
352
|
+
schema,
|
|
353
|
+
});
|
|
354
|
+
return schema;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const describe = (
|
|
356
358
|
route: IRoute,
|
|
357
359
|
tagName: string,
|
|
358
360
|
parameterName?: string,
|
|
359
|
-
): string | undefined {
|
|
361
|
+
): string | undefined => {
|
|
360
362
|
const parametric: (elem: ts.JSDocTagInfo) => boolean = parameterName
|
|
361
363
|
? (tag) =>
|
|
362
364
|
tag.text!.find(
|
|
@@ -366,13 +368,13 @@ export namespace SwaggerSchemaGenerator {
|
|
|
366
368
|
) !== undefined
|
|
367
369
|
: () => true;
|
|
368
370
|
|
|
369
|
-
const tag: ts.JSDocTagInfo | undefined = route.
|
|
371
|
+
const tag: ts.JSDocTagInfo | undefined = route.jsDocTags.find(
|
|
370
372
|
(tag) => tag.name === tagName && tag.text && parametric(tag),
|
|
371
373
|
);
|
|
372
374
|
return tag && tag.text
|
|
373
375
|
? tag.text.find((elem) => elem.kind === "text")?.text
|
|
374
376
|
: undefined;
|
|
375
|
-
}
|
|
377
|
+
};
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
const warning = new Singleton((described: boolean) => {
|
|
@@ -64,12 +64,6 @@ export namespace SwaggerSchemaValidator {
|
|
|
64
64
|
// PROPERTY MUST BE SOLE
|
|
65
65
|
if (typeof explore.property === "object")
|
|
66
66
|
insert("dynamic property is not allowed.");
|
|
67
|
-
// MUST BE LOWER-CASE
|
|
68
|
-
if (
|
|
69
|
-
typeof explore.property === "string" &&
|
|
70
|
-
explore.property !== explore.property.toLowerCase()
|
|
71
|
-
)
|
|
72
|
-
insert("property name must be lower-case.");
|
|
73
67
|
// DO NOT ALLOW TUPLE TYPE
|
|
74
68
|
if (meta.tuples.length) insert("tuple type is not allowed.");
|
|
75
69
|
// DO NOT ALLOW UNION TYPE
|
|
@@ -136,12 +130,6 @@ export namespace SwaggerSchemaValidator {
|
|
|
136
130
|
// PROPERTY MUST BE SOLE
|
|
137
131
|
if (typeof explore.property === "object")
|
|
138
132
|
insert("dynamic property is not allowed.");
|
|
139
|
-
// MUST BE LOWER-CASE
|
|
140
|
-
if (
|
|
141
|
-
typeof explore.property === "string" &&
|
|
142
|
-
explore.property !== explore.property.toLowerCase()
|
|
143
|
-
)
|
|
144
|
-
insert("property name must be lower-case.");
|
|
145
133
|
// DO NOT ALLOW TUPLE TYPE
|
|
146
134
|
if (meta.tuples.length) insert("tuple type is not allowed.");
|
|
147
135
|
// DO NOT ALLOW UNION TYPE
|
|
@@ -6,6 +6,7 @@ export interface IController {
|
|
|
6
6
|
paths: string[];
|
|
7
7
|
functions: IController.IFunction[];
|
|
8
8
|
security: Record<string, string[]>[];
|
|
9
|
+
swaggerTgas: string[];
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export namespace IController {
|
|
@@ -23,6 +24,7 @@ export namespace IController {
|
|
|
23
24
|
number | "2XX" | "3XX" | "4XX" | "5XX",
|
|
24
25
|
IController.IException
|
|
25
26
|
>;
|
|
27
|
+
swaggerTags: string[];
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export type IParameter =
|
package/src/structures/IRoute.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
|
|
3
|
+
import { Metadata } from "typia/lib/schemas/metadata/Metadata";
|
|
4
|
+
|
|
3
5
|
import { IController } from "./IController";
|
|
4
6
|
|
|
5
7
|
export interface IRoute {
|
|
@@ -15,16 +17,20 @@ export interface IRoute {
|
|
|
15
17
|
output: IRoute.IOutput;
|
|
16
18
|
|
|
17
19
|
location: string;
|
|
18
|
-
symbol:
|
|
20
|
+
symbol: {
|
|
21
|
+
class: string;
|
|
22
|
+
function: string;
|
|
23
|
+
};
|
|
19
24
|
description?: string;
|
|
20
25
|
operationId?: string;
|
|
21
|
-
|
|
26
|
+
jsDocTags: ts.JSDocTagInfo[];
|
|
22
27
|
setHeaders: Array<
|
|
23
28
|
| { type: "setter"; source: string; target?: string }
|
|
24
29
|
| { type: "assigner"; source: string }
|
|
25
30
|
>;
|
|
26
31
|
security: Record<string, string[]>[];
|
|
27
32
|
exceptions: Record<number | "2XX" | "3XX" | "4XX" | "5XX", IRoute.IOutput>;
|
|
33
|
+
swaggerTags: string[];
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export namespace IRoute {
|
|
@@ -32,10 +38,12 @@ export namespace IRoute {
|
|
|
32
38
|
optional: boolean;
|
|
33
39
|
type: ts.Type;
|
|
34
40
|
typeName: string;
|
|
41
|
+
metadata?: Metadata;
|
|
35
42
|
};
|
|
36
43
|
export interface IOutput {
|
|
37
44
|
type: ts.Type;
|
|
38
45
|
typeName: string;
|
|
46
|
+
metadata?: Metadata;
|
|
39
47
|
description?: string;
|
|
40
48
|
contentType: "application/json" | "text/plain";
|
|
41
49
|
}
|