@tsed/platform-http 8.0.1 → 8.0.3
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/package.json +18 -16
- package/src/common/builder/PlatformBuilder.spec.ts +331 -0
- package/src/common/builder/PlatformBuilder.ts +426 -0
- package/src/common/builder/__fixtures__/PlatformCustom.ts +39 -0
- package/src/common/config/interfaces/PlatformJsonMapperSettings.ts +11 -0
- package/src/common/config/interfaces/PlatformMulterSettings.ts +21 -0
- package/src/common/config/interfaces/PlatformRouterSettings.ts +11 -0
- package/src/common/config/interfaces/PlatformStaticsSettings.ts +23 -0
- package/src/common/config/services/PlatformConfiguration.spec.ts +181 -0
- package/src/common/config/services/PlatformConfiguration.ts +157 -0
- package/src/common/constants/routerOptions.ts +1 -0
- package/src/common/decorators/PlatformProvider.ts +14 -0
- package/src/common/decorators/multer/multerFileSize.spec.ts +22 -0
- package/src/common/decorators/multer/multerFileSize.ts +32 -0
- package/src/common/decorators/multer/multerOptions.spec.ts +21 -0
- package/src/common/decorators/multer/multerOptions.ts +53 -0
- package/src/common/decorators/multer/multipartFile.spec.ts +259 -0
- package/src/common/decorators/multer/multipartFile.ts +88 -0
- package/src/common/decorators/params/error.spec.ts +15 -0
- package/src/common/decorators/params/error.ts +16 -0
- package/src/common/decorators/params/next.spec.ts +15 -0
- package/src/common/decorators/params/next.ts +31 -0
- package/src/common/decorators/params/request.spec.ts +49 -0
- package/src/common/decorators/params/request.ts +85 -0
- package/src/common/decorators/params/response.spec.ts +36 -0
- package/src/common/decorators/params/response.ts +73 -0
- package/src/common/domain/AnyToPromiseWithCtx.spec.ts +20 -0
- package/src/common/domain/AnyToPromiseWithCtx.ts +30 -0
- package/src/common/domain/EndpointMetadata.ts +1 -0
- package/src/common/domain/PlatformContext.spec.ts +124 -0
- package/src/common/domain/PlatformContext.ts +135 -0
- package/src/common/domain/PlatformRouteDetails.ts +7 -0
- package/src/common/exports.ts +4 -0
- package/src/common/fn/adapter.ts +23 -0
- package/src/common/fn/application.ts +11 -0
- package/src/common/index.ts +62 -0
- package/src/common/interfaces/AfterInit.ts +3 -0
- package/src/common/interfaces/AfterListen.ts +3 -0
- package/src/common/interfaces/AfterRoutesInit.ts +3 -0
- package/src/common/interfaces/BeforeInit.ts +3 -0
- package/src/common/interfaces/BeforeListen.ts +3 -0
- package/src/common/interfaces/BeforeRoutesInit.ts +3 -0
- package/src/common/interfaces/HandlerType.ts +12 -0
- package/src/common/interfaces/IncomingEvent.ts +6 -0
- package/src/common/interfaces/OnReady.ts +3 -0
- package/src/common/interfaces/OnRequest.ts +5 -0
- package/src/common/interfaces/OnResponse.ts +5 -0
- package/src/common/interfaces/OnRoutesInit.ts +3 -0
- package/src/common/interfaces/PlatformRouteOptions.ts +17 -0
- package/src/common/interfaces/ResponseErrorObject.ts +8 -0
- package/src/common/interfaces/Route.ts +12 -0
- package/src/common/middlewares/PlatformAcceptMimesMiddleware.spec.ts +215 -0
- package/src/common/middlewares/PlatformAcceptMimesMiddleware.ts +24 -0
- package/src/common/middlewares/PlatformMulterMiddleware.spec.ts +97 -0
- package/src/common/middlewares/PlatformMulterMiddleware.ts +59 -0
- package/src/common/services/Platform.spec.ts +145 -0
- package/src/common/services/Platform.ts +79 -0
- package/src/common/services/PlatformAdapter.ts +89 -0
- package/src/common/services/PlatformApplication.spec.ts +101 -0
- package/src/common/services/PlatformApplication.ts +54 -0
- package/src/common/services/PlatformHandler.spec.ts +152 -0
- package/src/common/services/PlatformHandler.ts +140 -0
- package/src/common/services/PlatformMiddlewaresChain.spec.ts +96 -0
- package/src/common/services/PlatformMiddlewaresChain.ts +55 -0
- package/src/common/services/PlatformRequest.spec.ts +185 -0
- package/src/common/services/PlatformRequest.ts +181 -0
- package/src/common/services/PlatformResponse.spec.ts +344 -0
- package/src/common/services/PlatformResponse.ts +388 -0
- package/src/common/utils/closeServer.spec.ts +11 -0
- package/src/common/utils/closeServer.ts +7 -0
- package/src/common/utils/createContext.spec.ts +111 -0
- package/src/common/utils/createContext.ts +52 -0
- package/src/common/utils/createHttpServer.spec.ts +74 -0
- package/src/common/utils/createHttpServer.ts +15 -0
- package/src/common/utils/createHttpsServer.spec.ts +73 -0
- package/src/common/utils/createHttpsServer.ts +16 -0
- package/src/common/utils/createInjector.spec.ts +42 -0
- package/src/common/utils/createInjector.ts +55 -0
- package/src/common/utils/createServer.ts +51 -0
- package/src/common/utils/ensureContext.spec.ts +41 -0
- package/src/common/utils/ensureContext.ts +17 -0
- package/src/common/utils/getConfiguration.spec.ts +163 -0
- package/src/common/utils/getConfiguration.ts +34 -0
- package/src/common/utils/getStaticsOptions.spec.ts +27 -0
- package/src/common/utils/getStaticsOptions.ts +24 -0
- package/src/common/utils/listenServer.ts +17 -0
- package/src/common/utils/mapReturnedResponse.spec.ts +35 -0
- package/src/common/utils/mapReturnedResponse.ts +10 -0
- package/src/common/utils/printRoutes.spec.ts +19 -0
- package/src/common/utils/printRoutes.ts +40 -0
- package/src/common/utils/resolveControllers.spec.ts +40 -0
- package/src/common/utils/resolveControllers.ts +54 -0
- package/src/common/utils/setResponseHeaders.spec.ts +84 -0
- package/src/common/utils/setResponseHeaders.ts +31 -0
- package/src/index.ts +2 -0
- package/src/testing/FakeAdapter.ts +69 -0
- package/src/testing/FakeResponse.ts +72 -0
- package/src/testing/PlatformTest.spec.ts +53 -0
- package/src/testing/PlatformTest.ts +157 -0
- package/src/testing/index.ts +5 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import {catchError} from "@tsed/core";
|
|
2
|
+
import {AcceptMime, EndpointMetadata, Get} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {PlatformTest} from "../../testing/index.js";
|
|
5
|
+
import {PlatformAcceptMimesMiddleware} from "./PlatformAcceptMimesMiddleware.js";
|
|
6
|
+
|
|
7
|
+
describe("PlatformMimesMiddleware", () => {
|
|
8
|
+
describe("when server has configuration", () => {
|
|
9
|
+
beforeEach(() =>
|
|
10
|
+
PlatformTest.create({
|
|
11
|
+
acceptMimes: ["application/json", "text"]
|
|
12
|
+
})
|
|
13
|
+
);
|
|
14
|
+
afterEach(() => PlatformTest.reset());
|
|
15
|
+
it("should accept type (application/json)", async () => {
|
|
16
|
+
class Test {
|
|
17
|
+
@Get("/")
|
|
18
|
+
@AcceptMime("application/json")
|
|
19
|
+
get() {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const endpoint = EndpointMetadata.get(Test, "get");
|
|
23
|
+
const request = PlatformTest.createRequest({
|
|
24
|
+
headers: {
|
|
25
|
+
accept: "application/json"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const ctx = PlatformTest.createRequestContext({
|
|
29
|
+
event: {request},
|
|
30
|
+
endpoint
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
vi.spyOn(request, "accepts");
|
|
34
|
+
|
|
35
|
+
const middleware = await PlatformTest.invoke<PlatformAcceptMimesMiddleware>(PlatformAcceptMimesMiddleware);
|
|
36
|
+
|
|
37
|
+
middleware.use(ctx as never);
|
|
38
|
+
|
|
39
|
+
expect(request.accepts).toHaveBeenCalledWith(["application/json", "text"]);
|
|
40
|
+
});
|
|
41
|
+
it("should accept type (text)", async () => {
|
|
42
|
+
class Test {
|
|
43
|
+
@Get("/")
|
|
44
|
+
@AcceptMime("text")
|
|
45
|
+
get() {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const endpoint = EndpointMetadata.get(Test, "get");
|
|
49
|
+
const request: any = PlatformTest.createRequest({
|
|
50
|
+
headers: {
|
|
51
|
+
accept: "text/*, application/json"
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const ctx = PlatformTest.createRequestContext({
|
|
55
|
+
event: {request},
|
|
56
|
+
endpoint
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
vi.spyOn(request, "accepts");
|
|
60
|
+
|
|
61
|
+
const middleware = await PlatformTest.invoke<PlatformAcceptMimesMiddleware>(PlatformAcceptMimesMiddleware);
|
|
62
|
+
middleware.use(ctx as never);
|
|
63
|
+
|
|
64
|
+
expect(request.accepts).toHaveBeenCalledWith(["text", "application/json"]);
|
|
65
|
+
});
|
|
66
|
+
it("should accept type (text) without endpoint", async () => {
|
|
67
|
+
class Test {
|
|
68
|
+
@Get("/")
|
|
69
|
+
@AcceptMime("text")
|
|
70
|
+
get() {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const request: any = PlatformTest.createRequest({
|
|
74
|
+
headers: {
|
|
75
|
+
accept: "text/*, application/json"
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const ctx = PlatformTest.createRequestContext({
|
|
79
|
+
event: {request}
|
|
80
|
+
});
|
|
81
|
+
vi.spyOn(request, "accepts");
|
|
82
|
+
|
|
83
|
+
const middleware = await PlatformTest.invoke<PlatformAcceptMimesMiddleware>(PlatformAcceptMimesMiddleware);
|
|
84
|
+
middleware.use(ctx as never);
|
|
85
|
+
|
|
86
|
+
expect(request.accepts).toHaveBeenCalledWith(["application/json", "text"]);
|
|
87
|
+
});
|
|
88
|
+
it("should refuse type", async () => {
|
|
89
|
+
class Test {
|
|
90
|
+
@Get("/")
|
|
91
|
+
@AcceptMime("application/json")
|
|
92
|
+
get() {}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const endpoint = EndpointMetadata.get(Test, "get");
|
|
96
|
+
const request: any = PlatformTest.createRequest({
|
|
97
|
+
headers: {
|
|
98
|
+
accept: "application/xml"
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const ctx = PlatformTest.createRequestContext({
|
|
102
|
+
event: {request},
|
|
103
|
+
endpoint
|
|
104
|
+
});
|
|
105
|
+
const middleware = await PlatformTest.invoke<PlatformAcceptMimesMiddleware>(PlatformAcceptMimesMiddleware);
|
|
106
|
+
|
|
107
|
+
const error: any = catchError(() => middleware.use(ctx as never));
|
|
108
|
+
|
|
109
|
+
expect(error.message).toEqual("You must accept content-type application/json, text");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
describe("when server hasn't configuration", () => {
|
|
113
|
+
beforeEach(() => PlatformTest.create());
|
|
114
|
+
afterEach(() => PlatformTest.reset());
|
|
115
|
+
it("should do noting", async () => {
|
|
116
|
+
class Test {
|
|
117
|
+
@Get("/")
|
|
118
|
+
get() {}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const endpoint = EndpointMetadata.get(Test, "get");
|
|
122
|
+
const request: any = PlatformTest.createRequest({
|
|
123
|
+
headers: {
|
|
124
|
+
accept: "application/json"
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const ctx = PlatformTest.createRequestContext({
|
|
128
|
+
event: {request},
|
|
129
|
+
endpoint
|
|
130
|
+
});
|
|
131
|
+
vi.spyOn(request, "accepts");
|
|
132
|
+
|
|
133
|
+
const middleware = await PlatformTest.invoke<PlatformAcceptMimesMiddleware>(PlatformAcceptMimesMiddleware);
|
|
134
|
+
middleware.use(ctx as never);
|
|
135
|
+
|
|
136
|
+
return expect(request.accepts).not.toHaveBeenCalled();
|
|
137
|
+
});
|
|
138
|
+
it("should accept type (application/json)", async () => {
|
|
139
|
+
class Test {
|
|
140
|
+
@Get("/")
|
|
141
|
+
@AcceptMime("application/json")
|
|
142
|
+
get() {}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const endpoint = EndpointMetadata.get(Test, "get");
|
|
146
|
+
const request: any = PlatformTest.createRequest({
|
|
147
|
+
headers: {
|
|
148
|
+
accept: "application/json"
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
const ctx = PlatformTest.createRequestContext({
|
|
152
|
+
event: {request},
|
|
153
|
+
endpoint
|
|
154
|
+
});
|
|
155
|
+
vi.spyOn(request, "accepts");
|
|
156
|
+
|
|
157
|
+
const middleware = await PlatformTest.invoke<PlatformAcceptMimesMiddleware>(PlatformAcceptMimesMiddleware);
|
|
158
|
+
middleware.use(ctx as never);
|
|
159
|
+
|
|
160
|
+
expect(request.accepts).toHaveBeenCalledWith(["application/json"]);
|
|
161
|
+
});
|
|
162
|
+
it("should accept type (text)", async () => {
|
|
163
|
+
class Test {
|
|
164
|
+
@Get("/")
|
|
165
|
+
@AcceptMime("text")
|
|
166
|
+
get() {}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const endpoint = EndpointMetadata.get(Test, "get");
|
|
170
|
+
const request: any = PlatformTest.createRequest({
|
|
171
|
+
headers: {
|
|
172
|
+
accept: "text/*, application/json"
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
const ctx = PlatformTest.createRequestContext({
|
|
176
|
+
event: {
|
|
177
|
+
request
|
|
178
|
+
},
|
|
179
|
+
endpoint
|
|
180
|
+
});
|
|
181
|
+
vi.spyOn(request, "accepts");
|
|
182
|
+
|
|
183
|
+
const middleware = await PlatformTest.invoke<PlatformAcceptMimesMiddleware>(PlatformAcceptMimesMiddleware);
|
|
184
|
+
middleware.use(ctx as never);
|
|
185
|
+
|
|
186
|
+
expect(request.accepts).toHaveBeenCalledWith(["text"]);
|
|
187
|
+
});
|
|
188
|
+
it("should refuse type", async () => {
|
|
189
|
+
class Test {
|
|
190
|
+
@Get("/")
|
|
191
|
+
@AcceptMime("application/json")
|
|
192
|
+
get() {}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const endpoint = EndpointMetadata.get(Test, "get");
|
|
196
|
+
const request: any = PlatformTest.createRequest({
|
|
197
|
+
headers: {
|
|
198
|
+
accept: "application/xml"
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
const ctx = PlatformTest.createRequestContext({
|
|
202
|
+
event: {request},
|
|
203
|
+
endpoint
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
vi.spyOn(request, "accepts");
|
|
207
|
+
|
|
208
|
+
const middleware = await PlatformTest.invoke<PlatformAcceptMimesMiddleware>(PlatformAcceptMimesMiddleware);
|
|
209
|
+
|
|
210
|
+
const error: any = catchError(() => middleware.use(ctx as never));
|
|
211
|
+
|
|
212
|
+
expect(error.message).toEqual("You must accept content-type application/json");
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {uniq} from "@tsed/core";
|
|
2
|
+
import {constant, injectable, ProviderType} from "@tsed/di";
|
|
3
|
+
import {NotAcceptable} from "@tsed/exceptions";
|
|
4
|
+
import {MiddlewareMethods} from "@tsed/platform-middlewares";
|
|
5
|
+
import {Context} from "@tsed/platform-params";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @middleware
|
|
9
|
+
* @platform
|
|
10
|
+
*/
|
|
11
|
+
export class PlatformAcceptMimesMiddleware implements MiddlewareMethods {
|
|
12
|
+
acceptMimes = constant<string[]>("acceptMimes", []);
|
|
13
|
+
|
|
14
|
+
public use(@Context() ctx: Context): void {
|
|
15
|
+
const {endpoint, request} = ctx;
|
|
16
|
+
const mimes = uniq((endpoint?.get("acceptMimes") || []).concat(this.acceptMimes));
|
|
17
|
+
|
|
18
|
+
if (mimes.length && !request.accepts(mimes)) {
|
|
19
|
+
throw new NotAcceptable(mimes.join(", "));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
injectable(PlatformAcceptMimesMiddleware).type(ProviderType.MIDDLEWARE).priority(-10);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {catchAsyncError} from "@tsed/core";
|
|
2
|
+
import {Exception} from "@tsed/exceptions";
|
|
3
|
+
import {MulterError} from "multer";
|
|
4
|
+
|
|
5
|
+
import {PlatformTest} from "../../testing/PlatformTest.js";
|
|
6
|
+
import {MulterOptions} from "../decorators/multer/multerOptions.js";
|
|
7
|
+
import {MultipartFile} from "../decorators/multer/multipartFile.js";
|
|
8
|
+
import {EndpointMetadata} from "../domain/EndpointMetadata.js";
|
|
9
|
+
import {PlatformApplication} from "../services/PlatformApplication.js";
|
|
10
|
+
import {PlatformMulterMiddleware} from "./PlatformMulterMiddleware.js";
|
|
11
|
+
|
|
12
|
+
async function build(options = {}) {
|
|
13
|
+
class Test {
|
|
14
|
+
@MulterOptions(options)
|
|
15
|
+
upload(@MultipartFile("file1") file1: any) {}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const multerMiddleware = vi.fn();
|
|
19
|
+
const multer = {
|
|
20
|
+
fields: vi.fn().mockReturnValue(multerMiddleware)
|
|
21
|
+
};
|
|
22
|
+
const app = {
|
|
23
|
+
multer: vi.fn().mockReturnValue(multer)
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const middleware = await PlatformTest.invoke<PlatformMulterMiddleware>(PlatformMulterMiddleware, [
|
|
27
|
+
{
|
|
28
|
+
token: PlatformApplication,
|
|
29
|
+
use: app
|
|
30
|
+
}
|
|
31
|
+
]);
|
|
32
|
+
const ctx: any = PlatformTest.createRequestContext();
|
|
33
|
+
ctx.endpoint = EndpointMetadata.get(Test, "upload");
|
|
34
|
+
|
|
35
|
+
return {middleware, ctx, multer, app, multerMiddleware};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("PlatformMulterMiddleware", () => {
|
|
39
|
+
beforeEach(() =>
|
|
40
|
+
PlatformTest.create({
|
|
41
|
+
multer: {
|
|
42
|
+
dest: "/dest"
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
afterEach(() => PlatformTest.reset());
|
|
47
|
+
it("should create middleware", async () => {
|
|
48
|
+
const {middleware, ctx, multer, app, multerMiddleware} = await build({});
|
|
49
|
+
|
|
50
|
+
await middleware.use(ctx);
|
|
51
|
+
|
|
52
|
+
expect(app.multer).toHaveBeenCalledWith({
|
|
53
|
+
dest: "/dest"
|
|
54
|
+
});
|
|
55
|
+
expect(multer.fields).toHaveBeenCalledWith([{maxCount: undefined, name: "file1"}]);
|
|
56
|
+
expect(multerMiddleware).toHaveBeenCalledWith(ctx.request.raw, ctx.response.raw);
|
|
57
|
+
});
|
|
58
|
+
it("should create middleware with storage", async () => {
|
|
59
|
+
const {middleware, ctx, multer, app, multerMiddleware} = await build({
|
|
60
|
+
storage: "storage"
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await middleware.use(ctx);
|
|
64
|
+
|
|
65
|
+
expect(app.multer).toHaveBeenCalledWith({
|
|
66
|
+
storage: "storage"
|
|
67
|
+
});
|
|
68
|
+
expect(multer.fields).toHaveBeenCalledWith([{maxCount: undefined, name: "file1"}]);
|
|
69
|
+
expect(multerMiddleware).toHaveBeenCalledWith(ctx.request.raw, ctx.response.raw);
|
|
70
|
+
});
|
|
71
|
+
it("should catch error with code", async () => {
|
|
72
|
+
const {middleware, ctx, multerMiddleware} = await build();
|
|
73
|
+
const error = new MulterError("LIMIT_FILE_SIZE", "field");
|
|
74
|
+
|
|
75
|
+
multerMiddleware.mockRejectedValue(error);
|
|
76
|
+
|
|
77
|
+
const actualError: any | undefined = await catchAsyncError(() => middleware.use(ctx));
|
|
78
|
+
|
|
79
|
+
expect(actualError).toBeInstanceOf(Exception);
|
|
80
|
+
expect(actualError?.message).toEqual("File too large");
|
|
81
|
+
expect(actualError?.status).toEqual(400);
|
|
82
|
+
});
|
|
83
|
+
it("should throw error without code", async () => {
|
|
84
|
+
const {middleware, ctx, multerMiddleware} = await build();
|
|
85
|
+
|
|
86
|
+
multerMiddleware.mockRejectedValue(new Error("test"));
|
|
87
|
+
|
|
88
|
+
let actualError: any;
|
|
89
|
+
try {
|
|
90
|
+
await middleware.use(ctx);
|
|
91
|
+
} catch (er) {
|
|
92
|
+
actualError = er;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
expect(actualError.message).toEqual("test");
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {constant, inject, injectable, ProviderType} from "@tsed/di";
|
|
2
|
+
import {BadRequest} from "@tsed/exceptions";
|
|
3
|
+
import {MiddlewareMethods} from "@tsed/platform-middlewares";
|
|
4
|
+
import {Context} from "@tsed/platform-params";
|
|
5
|
+
import type {MulterError} from "multer";
|
|
6
|
+
|
|
7
|
+
import {PlatformMulterField, PlatformMulterSettings} from "../config/interfaces/PlatformMulterSettings.js";
|
|
8
|
+
import {PlatformContext} from "../domain/PlatformContext.js";
|
|
9
|
+
import {PlatformApplication} from "../services/PlatformApplication.js";
|
|
10
|
+
|
|
11
|
+
export interface MulterInputOptions {
|
|
12
|
+
fields: PlatformMulterField[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class MulterException extends BadRequest {
|
|
16
|
+
constructor(er: MulterError) {
|
|
17
|
+
super(er.message);
|
|
18
|
+
this.origin = er;
|
|
19
|
+
this.name = er.code;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @middleware
|
|
25
|
+
*/
|
|
26
|
+
export class PlatformMulterMiddleware implements MiddlewareMethods {
|
|
27
|
+
protected app = inject(PlatformApplication);
|
|
28
|
+
|
|
29
|
+
async use(@Context() ctx: PlatformContext) {
|
|
30
|
+
try {
|
|
31
|
+
const {fields, options = {}} = ctx.endpoint.get(PlatformMulterMiddleware);
|
|
32
|
+
const settings: PlatformMulterSettings = {
|
|
33
|
+
...constant("multer", {}),
|
|
34
|
+
...options
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/* istanbul ignore next */
|
|
38
|
+
if (settings.storage) {
|
|
39
|
+
delete settings.dest;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const middleware: any = this.app.multer(settings).fields(this.getFields({fields}));
|
|
43
|
+
|
|
44
|
+
return await middleware(ctx.getRequest(), ctx.getResponse());
|
|
45
|
+
} catch (er) {
|
|
46
|
+
if (er.code) {
|
|
47
|
+
throw new MulterException(er);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
throw er;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
protected getFields(conf: MulterInputOptions) {
|
|
55
|
+
return conf.fields.map(({name, maxCount}) => ({name, maxCount}));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
injectable(PlatformMulterMiddleware).type(ProviderType.MIDDLEWARE).priority(-10);
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {nameOf} from "@tsed/core";
|
|
2
|
+
import {Controller} from "@tsed/di";
|
|
3
|
+
import {Get, Post} from "@tsed/schema";
|
|
4
|
+
|
|
5
|
+
import {PlatformTest} from "../../testing/PlatformTest.js";
|
|
6
|
+
import {application} from "../fn/application.js";
|
|
7
|
+
import {Platform} from "./Platform.js";
|
|
8
|
+
|
|
9
|
+
@Controller("/my-route")
|
|
10
|
+
class MyCtrl {
|
|
11
|
+
@Get("/")
|
|
12
|
+
get() {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Controller("/my-sub-route")
|
|
16
|
+
class MySubCtrl {
|
|
17
|
+
@Get("/")
|
|
18
|
+
get() {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Controller({
|
|
22
|
+
path: "/my-route",
|
|
23
|
+
children: [MySubCtrl]
|
|
24
|
+
})
|
|
25
|
+
class MyNestedCtrl {
|
|
26
|
+
@Get("/")
|
|
27
|
+
get() {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Controller("/")
|
|
31
|
+
export class FlaggedCommentController {
|
|
32
|
+
@Get("/flag")
|
|
33
|
+
async list() {}
|
|
34
|
+
|
|
35
|
+
@Post("/:commentID/flag")
|
|
36
|
+
async create() {}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Controller({
|
|
40
|
+
path: "/comments",
|
|
41
|
+
children: [FlaggedCommentController]
|
|
42
|
+
})
|
|
43
|
+
export class CommentController {
|
|
44
|
+
@Get("/all")
|
|
45
|
+
get() {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Controller({
|
|
49
|
+
path: "/domain/:contextID",
|
|
50
|
+
children: [CommentController]
|
|
51
|
+
})
|
|
52
|
+
export class DomainController {
|
|
53
|
+
@Get("/all")
|
|
54
|
+
get() {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Controller({
|
|
58
|
+
path: "/platform/:platform",
|
|
59
|
+
children: [CommentController]
|
|
60
|
+
})
|
|
61
|
+
export class PlatformController {
|
|
62
|
+
@Get("/")
|
|
63
|
+
get() {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("Platform", () => {
|
|
67
|
+
beforeEach(PlatformTest.create);
|
|
68
|
+
afterEach(PlatformTest.reset);
|
|
69
|
+
afterEach(() => {
|
|
70
|
+
vi.resetAllMocks();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("addRoute", () => {
|
|
74
|
+
it("should add a route", async () => {
|
|
75
|
+
// GIVEN
|
|
76
|
+
const platform = await PlatformTest.get<Platform>(Platform);
|
|
77
|
+
|
|
78
|
+
vi.spyOn(application(), "use");
|
|
79
|
+
|
|
80
|
+
// WHEN
|
|
81
|
+
platform.addRoutes([{route: "/test", token: MyCtrl}]);
|
|
82
|
+
});
|
|
83
|
+
it("should add nested controllers (1)", async () => {
|
|
84
|
+
// GIVEN
|
|
85
|
+
const platform = await PlatformTest.get<Platform>(Platform);
|
|
86
|
+
|
|
87
|
+
vi.spyOn(application(), "use");
|
|
88
|
+
|
|
89
|
+
// WHEN
|
|
90
|
+
platform.addRoutes([{route: "/rest", token: MyNestedCtrl}]);
|
|
91
|
+
platform.addRoutes([{route: "/rest", token: MySubCtrl}]);
|
|
92
|
+
|
|
93
|
+
platform.getLayers();
|
|
94
|
+
const result = platform.getMountedControllers();
|
|
95
|
+
|
|
96
|
+
// THEN
|
|
97
|
+
expect(result.length).toEqual(2);
|
|
98
|
+
expect(result[0].route).toEqual("/rest");
|
|
99
|
+
expect(result[1].route).toEqual("/rest/my-route");
|
|
100
|
+
});
|
|
101
|
+
it("should add nested controllers (2)", async () => {
|
|
102
|
+
// GIVEN
|
|
103
|
+
const platform = await PlatformTest.get<Platform>(Platform);
|
|
104
|
+
|
|
105
|
+
vi.spyOn(application(), "use");
|
|
106
|
+
|
|
107
|
+
// WHEN
|
|
108
|
+
platform.addRoutes([{route: "/rest", token: DomainController}]);
|
|
109
|
+
platform.addRoutes([{route: "/rest", token: PlatformController}]);
|
|
110
|
+
|
|
111
|
+
platform.getLayers();
|
|
112
|
+
|
|
113
|
+
const result = platform.getMountedControllers();
|
|
114
|
+
|
|
115
|
+
// THEN
|
|
116
|
+
const data = result.map((c) => ({route: c.route, name: nameOf(c.provider)}));
|
|
117
|
+
expect(data).toEqual([
|
|
118
|
+
{
|
|
119
|
+
name: "Token:DomainController:DomainController",
|
|
120
|
+
route: "/rest"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "Token:CommentController:CommentController",
|
|
124
|
+
route: "/rest/domain/:contextID"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "Token:FlaggedCommentController:FlaggedCommentController",
|
|
128
|
+
route: "/rest/domain/:contextID/comments"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "Token:PlatformController:PlatformController",
|
|
132
|
+
route: "/rest"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "Token:CommentController:CommentController",
|
|
136
|
+
route: "/rest/platform/:platform"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "Token:FlaggedCommentController:FlaggedCommentController",
|
|
140
|
+
route: "/rest/platform/:platform/comments"
|
|
141
|
+
}
|
|
142
|
+
]);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {ControllerProvider, inject, injectable, injector, ProviderScope, TokenProvider} from "@tsed/di";
|
|
2
|
+
import {PlatformLayer, PlatformRouters} from "@tsed/platform-router";
|
|
3
|
+
|
|
4
|
+
import {application} from "../fn/application.js";
|
|
5
|
+
import {Route, RouteController} from "../interfaces/Route.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `Platform` is used to provide all routes collected by annotation `@Controller`.
|
|
9
|
+
*
|
|
10
|
+
* @platform
|
|
11
|
+
*/
|
|
12
|
+
export class Platform {
|
|
13
|
+
readonly platformRouters = inject(PlatformRouters);
|
|
14
|
+
#layers: PlatformLayer[];
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
this.platformRouters.prebuild();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get app() {
|
|
21
|
+
return application();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public addRoutes(routes: Route[]) {
|
|
25
|
+
routes.forEach((routeSettings) => {
|
|
26
|
+
this.addRoute(routeSettings.route, routeSettings.token);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public addRoute(route: string, token: TokenProvider) {
|
|
31
|
+
const app = application();
|
|
32
|
+
const provider = injector().getProvider(token) as ControllerProvider;
|
|
33
|
+
|
|
34
|
+
if (!provider || provider.hasParent()) {
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const router = this.platformRouters.from(provider.token);
|
|
39
|
+
|
|
40
|
+
app.use(route, router);
|
|
41
|
+
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public getLayers() {
|
|
46
|
+
this.#layers = this.#layers || this.platformRouters.getLayers(application());
|
|
47
|
+
|
|
48
|
+
return this.#layers;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get all controllers mounted on the application.
|
|
53
|
+
* @returns {RouteController[]}
|
|
54
|
+
*/
|
|
55
|
+
public getMountedControllers() {
|
|
56
|
+
const controllers = this.getLayers().reduce((controllers, layer) => {
|
|
57
|
+
if (layer.isProvider()) {
|
|
58
|
+
const route = String(layer.getBasePath());
|
|
59
|
+
const key = `${layer.provider.toString()}:${route}`;
|
|
60
|
+
|
|
61
|
+
if (!controllers.has(key)) {
|
|
62
|
+
controllers.set(key, {
|
|
63
|
+
route,
|
|
64
|
+
routes: new Set(),
|
|
65
|
+
provider: layer.provider
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
controllers.get(key)!.routes.add(String(layer.path));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return controllers;
|
|
73
|
+
}, new Map<string, RouteController>());
|
|
74
|
+
|
|
75
|
+
return [...controllers.values()];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
injectable(Platform).scope(ProviderScope.SINGLETON);
|