@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.
Files changed (100) hide show
  1. package/package.json +18 -16
  2. package/src/common/builder/PlatformBuilder.spec.ts +331 -0
  3. package/src/common/builder/PlatformBuilder.ts +426 -0
  4. package/src/common/builder/__fixtures__/PlatformCustom.ts +39 -0
  5. package/src/common/config/interfaces/PlatformJsonMapperSettings.ts +11 -0
  6. package/src/common/config/interfaces/PlatformMulterSettings.ts +21 -0
  7. package/src/common/config/interfaces/PlatformRouterSettings.ts +11 -0
  8. package/src/common/config/interfaces/PlatformStaticsSettings.ts +23 -0
  9. package/src/common/config/services/PlatformConfiguration.spec.ts +181 -0
  10. package/src/common/config/services/PlatformConfiguration.ts +157 -0
  11. package/src/common/constants/routerOptions.ts +1 -0
  12. package/src/common/decorators/PlatformProvider.ts +14 -0
  13. package/src/common/decorators/multer/multerFileSize.spec.ts +22 -0
  14. package/src/common/decorators/multer/multerFileSize.ts +32 -0
  15. package/src/common/decorators/multer/multerOptions.spec.ts +21 -0
  16. package/src/common/decorators/multer/multerOptions.ts +53 -0
  17. package/src/common/decorators/multer/multipartFile.spec.ts +259 -0
  18. package/src/common/decorators/multer/multipartFile.ts +88 -0
  19. package/src/common/decorators/params/error.spec.ts +15 -0
  20. package/src/common/decorators/params/error.ts +16 -0
  21. package/src/common/decorators/params/next.spec.ts +15 -0
  22. package/src/common/decorators/params/next.ts +31 -0
  23. package/src/common/decorators/params/request.spec.ts +49 -0
  24. package/src/common/decorators/params/request.ts +85 -0
  25. package/src/common/decorators/params/response.spec.ts +36 -0
  26. package/src/common/decorators/params/response.ts +73 -0
  27. package/src/common/domain/AnyToPromiseWithCtx.spec.ts +20 -0
  28. package/src/common/domain/AnyToPromiseWithCtx.ts +30 -0
  29. package/src/common/domain/EndpointMetadata.ts +1 -0
  30. package/src/common/domain/PlatformContext.spec.ts +124 -0
  31. package/src/common/domain/PlatformContext.ts +135 -0
  32. package/src/common/domain/PlatformRouteDetails.ts +7 -0
  33. package/src/common/exports.ts +4 -0
  34. package/src/common/fn/adapter.ts +23 -0
  35. package/src/common/fn/application.ts +11 -0
  36. package/src/common/index.ts +62 -0
  37. package/src/common/interfaces/AfterInit.ts +3 -0
  38. package/src/common/interfaces/AfterListen.ts +3 -0
  39. package/src/common/interfaces/AfterRoutesInit.ts +3 -0
  40. package/src/common/interfaces/BeforeInit.ts +3 -0
  41. package/src/common/interfaces/BeforeListen.ts +3 -0
  42. package/src/common/interfaces/BeforeRoutesInit.ts +3 -0
  43. package/src/common/interfaces/HandlerType.ts +12 -0
  44. package/src/common/interfaces/IncomingEvent.ts +6 -0
  45. package/src/common/interfaces/OnReady.ts +3 -0
  46. package/src/common/interfaces/OnRequest.ts +5 -0
  47. package/src/common/interfaces/OnResponse.ts +5 -0
  48. package/src/common/interfaces/OnRoutesInit.ts +3 -0
  49. package/src/common/interfaces/PlatformRouteOptions.ts +17 -0
  50. package/src/common/interfaces/ResponseErrorObject.ts +8 -0
  51. package/src/common/interfaces/Route.ts +12 -0
  52. package/src/common/middlewares/PlatformAcceptMimesMiddleware.spec.ts +215 -0
  53. package/src/common/middlewares/PlatformAcceptMimesMiddleware.ts +24 -0
  54. package/src/common/middlewares/PlatformMulterMiddleware.spec.ts +97 -0
  55. package/src/common/middlewares/PlatformMulterMiddleware.ts +59 -0
  56. package/src/common/services/Platform.spec.ts +145 -0
  57. package/src/common/services/Platform.ts +79 -0
  58. package/src/common/services/PlatformAdapter.ts +89 -0
  59. package/src/common/services/PlatformApplication.spec.ts +101 -0
  60. package/src/common/services/PlatformApplication.ts +54 -0
  61. package/src/common/services/PlatformHandler.spec.ts +152 -0
  62. package/src/common/services/PlatformHandler.ts +140 -0
  63. package/src/common/services/PlatformMiddlewaresChain.spec.ts +96 -0
  64. package/src/common/services/PlatformMiddlewaresChain.ts +55 -0
  65. package/src/common/services/PlatformRequest.spec.ts +185 -0
  66. package/src/common/services/PlatformRequest.ts +181 -0
  67. package/src/common/services/PlatformResponse.spec.ts +344 -0
  68. package/src/common/services/PlatformResponse.ts +388 -0
  69. package/src/common/utils/closeServer.spec.ts +11 -0
  70. package/src/common/utils/closeServer.ts +7 -0
  71. package/src/common/utils/createContext.spec.ts +111 -0
  72. package/src/common/utils/createContext.ts +52 -0
  73. package/src/common/utils/createHttpServer.spec.ts +74 -0
  74. package/src/common/utils/createHttpServer.ts +15 -0
  75. package/src/common/utils/createHttpsServer.spec.ts +73 -0
  76. package/src/common/utils/createHttpsServer.ts +16 -0
  77. package/src/common/utils/createInjector.spec.ts +42 -0
  78. package/src/common/utils/createInjector.ts +55 -0
  79. package/src/common/utils/createServer.ts +51 -0
  80. package/src/common/utils/ensureContext.spec.ts +41 -0
  81. package/src/common/utils/ensureContext.ts +17 -0
  82. package/src/common/utils/getConfiguration.spec.ts +163 -0
  83. package/src/common/utils/getConfiguration.ts +34 -0
  84. package/src/common/utils/getStaticsOptions.spec.ts +27 -0
  85. package/src/common/utils/getStaticsOptions.ts +24 -0
  86. package/src/common/utils/listenServer.ts +17 -0
  87. package/src/common/utils/mapReturnedResponse.spec.ts +35 -0
  88. package/src/common/utils/mapReturnedResponse.ts +10 -0
  89. package/src/common/utils/printRoutes.spec.ts +19 -0
  90. package/src/common/utils/printRoutes.ts +40 -0
  91. package/src/common/utils/resolveControllers.spec.ts +40 -0
  92. package/src/common/utils/resolveControllers.ts +54 -0
  93. package/src/common/utils/setResponseHeaders.spec.ts +84 -0
  94. package/src/common/utils/setResponseHeaders.ts +31 -0
  95. package/src/index.ts +2 -0
  96. package/src/testing/FakeAdapter.ts +69 -0
  97. package/src/testing/FakeResponse.ts +72 -0
  98. package/src/testing/PlatformTest.spec.ts +53 -0
  99. package/src/testing/PlatformTest.ts +157 -0
  100. package/src/testing/index.ts +5 -0
@@ -0,0 +1,89 @@
1
+ import {Type} from "@tsed/core";
2
+ import {injectable, ProviderOpts} from "@tsed/di";
3
+ import {PlatformHandlerMetadata, PlatformLayer} from "@tsed/platform-router";
4
+ import {IncomingMessage, ServerResponse} from "http";
5
+
6
+ import {PlatformMulter, PlatformMulterSettings} from "../config/interfaces/PlatformMulterSettings.js";
7
+ import {PlatformStaticsOptions} from "../config/interfaces/PlatformStaticsSettings.js";
8
+ import {application} from "../fn/application.js";
9
+ import {createHttpServer} from "../utils/createHttpServer.js";
10
+ import {createHttpsServer} from "../utils/createHttpsServer.js";
11
+ import {CreateServerReturn} from "../utils/createServer.js";
12
+ import type {PlatformApplication} from "./PlatformApplication.js";
13
+
14
+ export abstract class PlatformAdapter<App = TsED.Application> {
15
+ abstract readonly NAME: string;
16
+ /**
17
+ * Load providers in top priority
18
+ */
19
+ providers: ProviderOpts[] = [];
20
+
21
+ get app(): PlatformApplication<App> {
22
+ return application();
23
+ }
24
+
25
+ getServers(): CreateServerReturn[] {
26
+ const app = application<App>();
27
+ return [createHttpServer(app.callback()), createHttpsServer(app.callback())].filter(Boolean) as any[];
28
+ }
29
+
30
+ onInit(): Promise<void> | void {
31
+ return Promise.resolve();
32
+ }
33
+
34
+ beforeLoadRoutes(): Promise<void> | void {
35
+ return Promise.resolve();
36
+ }
37
+
38
+ afterLoadRoutes(): Promise<void> | void {
39
+ return Promise.resolve();
40
+ }
41
+
42
+ /**
43
+ * create initial context
44
+ */
45
+ abstract useContext(): any;
46
+
47
+ /**
48
+ * Map router layer to the targeted framework
49
+ */
50
+ abstract mapLayers(layer: PlatformLayer[]): void;
51
+
52
+ /**
53
+ * Map handler to the targeted framework
54
+ */
55
+ mapHandler(handler: Function, layer: PlatformHandlerMetadata): Function {
56
+ return handler;
57
+ }
58
+
59
+ /**
60
+ * Return the app instance
61
+ */
62
+ abstract createApp(): {app: App; callback(): (req: IncomingMessage, res: ServerResponse) => void};
63
+
64
+ /**
65
+ * Return the statics middlewares
66
+ * @param endpoint
67
+ * @param options
68
+ */
69
+ abstract statics(endpoint: string, options: PlatformStaticsOptions): any;
70
+
71
+ /**
72
+ * Return the multipart middleware
73
+ * @param options
74
+ */
75
+ abstract multipart(options: PlatformMulterSettings): PlatformMulter;
76
+
77
+ /**
78
+ * Return the body parser for the given
79
+ * @param type
80
+ * @param opts
81
+ */
82
+ abstract bodyParser(type: string, opts?: Record<string, any>): any;
83
+ }
84
+
85
+ export interface PlatformBuilderSettings<App = TsED.Application> extends Partial<TsED.Configuration> {
86
+ adapter?: Type<PlatformAdapter<App>>;
87
+ }
88
+
89
+ injectable(PlatformAdapter).alias("PlatformAdapter");
@@ -0,0 +1,101 @@
1
+ import {configuration} from "@tsed/di";
2
+
3
+ import {PlatformTest} from "../../testing/PlatformTest.js";
4
+ import {createContext} from "../utils/createContext.js";
5
+ import {PlatformApplication} from "./PlatformApplication.js";
6
+ import {PlatformHandler} from "./PlatformHandler.js";
7
+
8
+ vi.mock("../utils/createContext");
9
+
10
+ function createDriver() {
11
+ return {
12
+ use: vi.fn(),
13
+ all: vi.fn(),
14
+ get: vi.fn(),
15
+ post: vi.fn(),
16
+ put: vi.fn(),
17
+ delete: vi.fn(),
18
+ patch: vi.fn(),
19
+ head: vi.fn(),
20
+ options: vi.fn()
21
+ };
22
+ }
23
+
24
+ async function getPlatformApp() {
25
+ const platformHandler = {
26
+ createHandler: vi.fn().mockImplementation((o) => o)
27
+ };
28
+ const platformApp = await PlatformTest.invoke<PlatformApplication<any>>(PlatformApplication, [
29
+ {
30
+ token: PlatformHandler,
31
+ use: platformHandler
32
+ }
33
+ ]);
34
+ configuration().logger = {};
35
+ platformApp.rawApp = createDriver() as any;
36
+ vi.spyOn(platformApp, "multer").mockReturnValue({} as never);
37
+
38
+ return {platformApp, platformHandler};
39
+ }
40
+
41
+ describe("PlatformApplication", () => {
42
+ beforeEach(PlatformTest.create);
43
+ afterEach(PlatformTest.reset);
44
+
45
+ describe("getApp()", () => {
46
+ it("should return app", async () => {
47
+ // GIVEN
48
+ const {platformApp} = await getPlatformApp();
49
+
50
+ // WHEN
51
+ expect(platformApp.getApp()).toEqual(platformApp.rawApp);
52
+ });
53
+ });
54
+
55
+ describe("statics()", () => {
56
+ it("should call statics", async () => {
57
+ // GIVEN
58
+ const {platformApp} = await getPlatformApp();
59
+
60
+ vi.spyOn(console, "warn");
61
+
62
+ // WHEN
63
+ platformApp.statics("/", {root: "/root"});
64
+ });
65
+ });
66
+ describe("multer()", () => {
67
+ it("should call statics", async () => {
68
+ // GIVEN
69
+ const {platformApp} = await getPlatformApp();
70
+
71
+ vi.spyOn(console, "warn");
72
+
73
+ // WHEN
74
+ platformApp.multer({});
75
+ });
76
+ });
77
+
78
+ describe("callback()", () => {
79
+ it("should return the callback", async () => {
80
+ const $ctx = {
81
+ runInContext: vi.fn().mockImplementation((cb) => {
82
+ cb();
83
+ })
84
+ };
85
+
86
+ const {platformApp} = await getPlatformApp();
87
+
88
+ (createContext as any).mockReturnValue(() => Promise.resolve($ctx));
89
+
90
+ // WHEN
91
+ const callback = vi.fn();
92
+
93
+ vi.spyOn(platformApp, "rawCallback").mockReturnValue(callback);
94
+
95
+ await platformApp.callback({} as any, {} as any);
96
+
97
+ expect(platformApp.rawCallback).toHaveBeenCalledWith();
98
+ expect(callback).toHaveBeenCalledWith({}, {});
99
+ });
100
+ });
101
+ });
@@ -0,0 +1,54 @@
1
+ import {inject, injectable, ProviderScope} from "@tsed/di";
2
+ import {PlatformRouter} from "@tsed/platform-router";
3
+ import {IncomingMessage, ServerResponse} from "http";
4
+
5
+ import {PlatformMulterSettings} from "../config/interfaces/PlatformMulterSettings.js";
6
+ import {PlatformAdapter} from "./PlatformAdapter.js";
7
+
8
+ declare global {
9
+ namespace TsED {
10
+ // @ts-ignore
11
+ export interface Application {}
12
+ }
13
+ }
14
+
15
+ /**
16
+ * `PlatformApplication` is used to provide all routes collected by annotation `@Controller`.
17
+ *
18
+ * @platform
19
+ */
20
+ export class PlatformApplication<App = TsED.Application> extends PlatformRouter {
21
+ adapter: PlatformAdapter<App> = inject(PlatformAdapter<App>);
22
+
23
+ rawApp: App;
24
+ rawCallback: () => any;
25
+
26
+ constructor() {
27
+ super();
28
+
29
+ const {app, callback} = this.adapter.createApp();
30
+
31
+ this.rawApp = app;
32
+ this.rawCallback = callback;
33
+ }
34
+
35
+ getApp(): App {
36
+ return this.rawApp;
37
+ }
38
+
39
+ multer(options: PlatformMulterSettings) {
40
+ return this.adapter.multipart(options);
41
+ }
42
+
43
+ callback(): (req: IncomingMessage, res: ServerResponse) => any;
44
+ callback(req: IncomingMessage, res: ServerResponse): any;
45
+ callback(req?: IncomingMessage, res?: ServerResponse) {
46
+ if (req && res) {
47
+ return this.callback()(req, res);
48
+ }
49
+
50
+ return this.rawCallback();
51
+ }
52
+ }
53
+
54
+ injectable(PlatformApplication).scope(ProviderScope.SINGLETON).alias("PlatformApplication");
@@ -0,0 +1,152 @@
1
+ import {Injectable, runInContext} from "@tsed/di";
2
+ import {PlatformHandlerMetadata, PlatformHandlerType} from "@tsed/platform-router";
3
+ import {EndpointMetadata, Get, View} from "@tsed/schema";
4
+
5
+ import {PlatformTest} from "../../testing/PlatformTest.js";
6
+ import {PlatformHandler} from "./PlatformHandler.js";
7
+
8
+ function getServiceFixture() {
9
+ const service = PlatformTest.get<PlatformHandler>(PlatformHandler);
10
+ return {service};
11
+ }
12
+
13
+ describe("PlatformHandler", () => {
14
+ beforeEach(() => PlatformTest.create());
15
+ afterEach(() => PlatformTest.create());
16
+
17
+ describe("createHandler()", () => {
18
+ it("should create handler", async () => {
19
+ const {service} = getServiceFixture();
20
+ const handler = vi.fn();
21
+ const metadata = new PlatformHandlerMetadata({
22
+ handler,
23
+ type: PlatformHandlerType.CTX_FN
24
+ });
25
+
26
+ const $ctx = PlatformTest.createRequestContext();
27
+
28
+ const result = service.createHandler(metadata);
29
+
30
+ expect(typeof result).toEqual("function");
31
+
32
+ await result($ctx);
33
+
34
+ expect(handler).toHaveBeenCalledWith($ctx);
35
+ });
36
+ it("should create handler from provider", async () => {
37
+ const {service} = getServiceFixture();
38
+
39
+ @Injectable()
40
+ class Test {
41
+ @Get("/")
42
+ get() {
43
+ return "test";
44
+ }
45
+ }
46
+
47
+ PlatformTest.injector.addProvider(Test);
48
+
49
+ const metadata = new PlatformHandlerMetadata({
50
+ provider: PlatformTest.injector.getProvider(Test),
51
+ propertyKey: "get",
52
+ type: PlatformHandlerType.ENDPOINT
53
+ });
54
+
55
+ const $ctx = PlatformTest.createRequestContext();
56
+ $ctx.endpoint = EndpointMetadata.get(Test, "get");
57
+
58
+ const result = service.createHandler(metadata);
59
+
60
+ expect(typeof result).toEqual("function");
61
+
62
+ await result($ctx);
63
+
64
+ expect($ctx.data).toEqual("test");
65
+ });
66
+ });
67
+ describe("createCustomHandler()", () => {
68
+ it("should create handler", async () => {
69
+ @Injectable()
70
+ class TestService {
71
+ @View("test")
72
+ use() {
73
+ return Promise.resolve("hello");
74
+ }
75
+ }
76
+
77
+ const {service} = getServiceFixture();
78
+
79
+ const $ctx = PlatformTest.createRequestContext();
80
+ const testService = await PlatformTest.invoke<TestService>(TestService)!;
81
+ const provider = PlatformTest.injector.getProvider(TestService)!;
82
+
83
+ vi.spyOn(testService, "use").mockResolvedValue("hello");
84
+
85
+ const result = service.createCustomHandler(provider, "use");
86
+
87
+ expect(result).toBeInstanceOf(Function);
88
+
89
+ await runInContext($ctx, () => result($ctx));
90
+
91
+ expect($ctx.data).toEqual("hello");
92
+ expect(testService.use).toHaveBeenCalledWith($ctx);
93
+ });
94
+ });
95
+ describe("onRequest()", () => {
96
+ it("should do nothing when the request is done", async () => {
97
+ const {service} = getServiceFixture();
98
+ const handler = vi.fn();
99
+
100
+ const $ctx = PlatformTest.createRequestContext();
101
+ $ctx.next = vi.fn();
102
+
103
+ await $ctx.finish();
104
+
105
+ $ctx.handlerMetadata = new PlatformHandlerMetadata({
106
+ handler
107
+ });
108
+
109
+ await service.onRequest(handler, $ctx);
110
+
111
+ expect(handler).not.toHaveBeenCalled();
112
+ });
113
+ it("should apply headers and status", async () => {
114
+ @Injectable()
115
+ class TestService {
116
+ @Get("/")
117
+ use() {
118
+ return Promise.resolve("hello");
119
+ }
120
+ }
121
+
122
+ const {service} = getServiceFixture();
123
+ const handler = vi.fn().mockResolvedValue({
124
+ status: 201,
125
+ statusText: "CREATED",
126
+ headers: {
127
+ test: "test"
128
+ },
129
+ data: "data"
130
+ });
131
+
132
+ const $ctx = PlatformTest.createRequestContext();
133
+ $ctx.next = vi.fn();
134
+ $ctx.endpoint = EndpointMetadata.get(TestService, "use");
135
+
136
+ $ctx.handlerMetadata = new PlatformHandlerMetadata({
137
+ handler: TestService.prototype.use
138
+ });
139
+
140
+ vi.spyOn(service, "flush").mockResolvedValue();
141
+
142
+ await service.onRequest(handler, $ctx);
143
+
144
+ expect(handler).toHaveBeenCalled();
145
+ expect($ctx.response.statusCode).toEqual(201);
146
+ expect($ctx.response.getHeaders()).toEqual({
147
+ test: "test",
148
+ "x-request-id": "id"
149
+ });
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,140 @@
1
+ import {AnyPromiseResult, AnyToPromiseStatus, catchAsyncError} from "@tsed/core";
2
+ import {inject, injectable, Provider, ProviderScope} from "@tsed/di";
3
+ import {PlatformExceptions} from "@tsed/platform-exceptions";
4
+ import {PlatformParams, PlatformParamsCallback} from "@tsed/platform-params";
5
+ import {PlatformResponseFilter} from "@tsed/platform-response-filter";
6
+ import {
7
+ AlterEndpointHandlersArg,
8
+ PlatformHandlerMetadata,
9
+ PlatformHandlerType,
10
+ PlatformRouters,
11
+ useResponseHandler
12
+ } from "@tsed/platform-router";
13
+ import {JsonOperationRoute} from "@tsed/schema";
14
+
15
+ import {AnyToPromiseWithCtx} from "../domain/AnyToPromiseWithCtx.js";
16
+ import {PlatformContext} from "../domain/PlatformContext.js";
17
+ import {setResponseHeaders} from "../utils/setResponseHeaders.js";
18
+ import {PlatformApplication} from "./PlatformApplication.js";
19
+ import {PlatformMiddlewaresChain} from "./PlatformMiddlewaresChain.js";
20
+
21
+ /**
22
+ * Platform Handler abstraction layer. Wrap original class method to a pure platform handler (Express, Koa, etc...).
23
+ * @platform
24
+ */
25
+ export class PlatformHandler {
26
+ protected responseFilter = inject(PlatformResponseFilter);
27
+ protected platformParams = inject(PlatformParams);
28
+ protected platformExceptions = inject(PlatformExceptions);
29
+ protected platformApplication = inject(PlatformApplication);
30
+ protected platformMiddlewaresChain = inject(PlatformMiddlewaresChain);
31
+ protected platformRouters = inject(PlatformRouters);
32
+
33
+ constructor() {
34
+ // configure the router module
35
+ this.platformRouters.hooks
36
+ .on("alterEndpointHandlers", (handlers: AlterEndpointHandlersArg, operationRoute: JsonOperationRoute) => {
37
+ handlers = this.platformMiddlewaresChain.get(handlers, operationRoute);
38
+
39
+ handlers.after.push(useResponseHandler(this.flush.bind(this)));
40
+
41
+ return handlers;
42
+ })
43
+ .on("alterHandler", (handlerMetadata: PlatformHandlerMetadata) => {
44
+ const handler = handlerMetadata.isInjectable() ? this.createHandler(handlerMetadata) : handlerMetadata.handler;
45
+
46
+ return this.platformApplication.adapter.mapHandler(handler, handlerMetadata);
47
+ });
48
+ }
49
+
50
+ createHandler(handlerMetadata: PlatformHandlerMetadata): any {
51
+ const handler = this.platformParams.compileHandler(handlerMetadata);
52
+
53
+ return ($ctx: PlatformContext) => {
54
+ $ctx.handlerMetadata = handlerMetadata;
55
+
56
+ return this.onRequest(handler, $ctx);
57
+ };
58
+ }
59
+
60
+ /**
61
+ * @param provider
62
+ * @param propertyKey
63
+ */
64
+ createCustomHandler(provider: Provider, propertyKey: string) {
65
+ const metadata = new PlatformHandlerMetadata({
66
+ provider,
67
+ type: PlatformHandlerType.CUSTOM,
68
+ propertyKey
69
+ });
70
+
71
+ return this.createHandler(metadata);
72
+ }
73
+
74
+ /**
75
+ * Call handler when a request his handle
76
+ */
77
+ async onRequest(handler: PlatformParamsCallback, $ctx: PlatformContext) {
78
+ const {handlerMetadata} = $ctx;
79
+
80
+ if (handlerMetadata.type === PlatformHandlerType.CTX_FN) {
81
+ return handler({$ctx});
82
+ }
83
+
84
+ const resolver = new AnyToPromiseWithCtx($ctx);
85
+
86
+ const response = await resolver.call(handler);
87
+ // Note: restore previous handler metadata (for OIDC)
88
+ $ctx.handlerMetadata = handlerMetadata;
89
+
90
+ if (response.state === AnyToPromiseStatus.RESOLVED && !$ctx.isDone()) {
91
+ return this.onResponse(response, $ctx);
92
+ }
93
+ }
94
+
95
+ onResponse({status, data, headers}: AnyPromiseResult, $ctx: PlatformContext) {
96
+ if (status) {
97
+ $ctx.response.status(status);
98
+ }
99
+
100
+ if (headers) {
101
+ $ctx.response.setHeaders(headers);
102
+ }
103
+
104
+ if (data !== undefined) {
105
+ $ctx.data = data;
106
+ }
107
+
108
+ $ctx.error = null;
109
+
110
+ // set headers each times that an endpoint is called
111
+ if ($ctx.handlerMetadata.isEndpoint()) {
112
+ setResponseHeaders($ctx);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Send the response to the consumer.
118
+ * @protected
119
+ * @param $ctx
120
+ */
121
+ async flush($ctx: PlatformContext) {
122
+ if (!$ctx.error) {
123
+ $ctx.error = await catchAsyncError(async () => {
124
+ const {response} = $ctx;
125
+
126
+ if (!$ctx.isDone()) {
127
+ let data = await this.responseFilter.serialize($ctx.data, $ctx as any);
128
+ data = await this.responseFilter.transform(data, $ctx as any);
129
+ response.body(data);
130
+ }
131
+ });
132
+ }
133
+
134
+ if ($ctx.error) {
135
+ this.platformExceptions.catch($ctx.error, $ctx);
136
+ }
137
+ }
138
+ }
139
+
140
+ injectable(PlatformHandler).scope(ProviderScope.SINGLETON);
@@ -0,0 +1,96 @@
1
+ import {Controller, ControllerProvider} from "@tsed/di";
2
+ import {Middleware, Use, UseAuth} from "@tsed/platform-middlewares";
3
+ import {AcceptMime, All, EndpointMetadata, Get, getOperationsRoutes, Post} from "@tsed/schema";
4
+
5
+ import {PlatformTest} from "../../testing/PlatformTest.js";
6
+ import {MultipartFile} from "../decorators/multer/multipartFile.js";
7
+ import {PlatformAcceptMimesMiddleware} from "../middlewares/PlatformAcceptMimesMiddleware.js";
8
+ import {PlatformMulterMiddleware} from "../middlewares/PlatformMulterMiddleware.js";
9
+ import {PlatformMiddlewaresChain} from "./PlatformMiddlewaresChain.js";
10
+
11
+ @Middleware()
12
+ class MyAuthMiddleware {
13
+ use() {}
14
+ }
15
+
16
+ @Middleware({
17
+ priority: -1
18
+ })
19
+ class TokenMiddleware {
20
+ use() {}
21
+ }
22
+
23
+ @Controller("/")
24
+ class TestCtrl {
25
+ @All("/")
26
+ allMethod() {}
27
+
28
+ @Get("/")
29
+ getMethod() {}
30
+
31
+ @Use("/")
32
+ use() {}
33
+
34
+ @Use()
35
+ use2() {}
36
+
37
+ @Post("/file")
38
+ @UseAuth(MyAuthMiddleware, {scope: "admin"})
39
+ @Use(TokenMiddleware)
40
+ @AcceptMime("application/json")
41
+ authEndpoint(@MultipartFile("file") file: MultipartFile) {}
42
+ }
43
+
44
+ function parentControllerBefore() {}
45
+
46
+ function getFixture() {
47
+ const provider = PlatformTest.injector.getProvider<ControllerProvider>(TestCtrl)!;
48
+ const platformMiddlewaresChain = PlatformTest.get<PlatformMiddlewaresChain>(PlatformMiddlewaresChain);
49
+ const operationRoutes = getOperationsRoutes<EndpointMetadata>(provider.token);
50
+ return {platformMiddlewaresChain, operationRoutes, provider};
51
+ }
52
+
53
+ describe("PlatformMiddlewaresChain", () => {
54
+ beforeEach(() => PlatformTest.create());
55
+ afterEach(() => PlatformTest.reset());
56
+
57
+ it("should return the middlewares for a given endpoint (getMethod)", () => {
58
+ const {platformMiddlewaresChain, operationRoutes} = getFixture();
59
+ const parentsMiddlewares = {
60
+ before: [parentControllerBefore]
61
+ };
62
+
63
+ const chain = platformMiddlewaresChain.get(parentsMiddlewares as any, operationRoutes[1]);
64
+
65
+ expect(chain.before[0]).toEqual(parentControllerBefore);
66
+ });
67
+ it("should return the middlewares for a given endpoint (allMethod)", () => {
68
+ const {platformMiddlewaresChain, operationRoutes} = getFixture();
69
+
70
+ const parentsMiddlewares: any = {
71
+ before: [parentControllerBefore]
72
+ };
73
+
74
+ const chain = platformMiddlewaresChain.get(parentsMiddlewares, operationRoutes[1]);
75
+
76
+ expect(chain.before[0]).toEqual(parentControllerBefore);
77
+ });
78
+
79
+ it("should merge middlewares and sort middleware by priority", () => {
80
+ const {platformMiddlewaresChain, operationRoutes} = getFixture();
81
+
82
+ const parentsMiddlewares: any = {
83
+ before: [parentControllerBefore, MyAuthMiddleware, TokenMiddleware]
84
+ };
85
+
86
+ const chain = platformMiddlewaresChain.get(parentsMiddlewares, operationRoutes[3]);
87
+
88
+ expect(chain.before).toEqual([
89
+ PlatformAcceptMimesMiddleware,
90
+ parentControllerBefore,
91
+ MyAuthMiddleware,
92
+ TokenMiddleware,
93
+ PlatformMulterMiddleware
94
+ ]);
95
+ });
96
+ });
@@ -0,0 +1,55 @@
1
+ import {isClass} from "@tsed/core";
2
+ import {constant, inject, injectable} from "@tsed/di";
3
+ import {ParamTypes} from "@tsed/platform-params";
4
+ import {AlterEndpointHandlersArg} from "@tsed/platform-router";
5
+ import {JsonEntityStore, JsonOperationRoute} from "@tsed/schema";
6
+
7
+ import {PlatformAcceptMimesMiddleware} from "../middlewares/PlatformAcceptMimesMiddleware.js";
8
+ import {PlatformMulterMiddleware} from "../middlewares/PlatformMulterMiddleware.js";
9
+ import {PlatformAdapter} from "./PlatformAdapter.js";
10
+
11
+ export class PlatformMiddlewaresChain {
12
+ protected acceptMimes = constant<string[]>("acceptMimes", []);
13
+ protected adapter = inject(PlatformAdapter);
14
+
15
+ get(handlers: AlterEndpointHandlersArg, operationRoute: JsonOperationRoute): AlterEndpointHandlersArg {
16
+ const {ACCEPT_MIMES, FILE} = this.getParamTypes(handlers, operationRoute);
17
+
18
+ return {
19
+ ...handlers,
20
+ before: [ACCEPT_MIMES && PlatformAcceptMimesMiddleware, ...handlers.before, FILE && PlatformMulterMiddleware].filter(Boolean) as any[]
21
+ };
22
+ }
23
+
24
+ protected hasAcceptMimes(operationRoute: JsonOperationRoute) {
25
+ return operationRoute.endpoint.acceptMimes.length || this.acceptMimes.length;
26
+ }
27
+
28
+ protected getParamTypes(middlewares: AlterEndpointHandlersArg, operationRoute: JsonOperationRoute) {
29
+ return middlewares.before
30
+ .concat(middlewares.after)
31
+ .filter(isClass)
32
+ .reduce(
33
+ (paramTypes, token) => {
34
+ const entity = JsonEntityStore.fromMethod(token, "use");
35
+
36
+ if (entity.decoratorType === "method") {
37
+ const {FILE, RAW_BODY, BODY} = entity.getParamTypes();
38
+ paramTypes.FILE = paramTypes.FILE || FILE;
39
+ paramTypes.RAW_BODY = paramTypes.RAW_BODY || RAW_BODY;
40
+ paramTypes.BODY = paramTypes.BODY || BODY;
41
+ }
42
+
43
+ return paramTypes;
44
+ },
45
+ {
46
+ ACCEPT_MIMES: this.hasAcceptMimes(operationRoute),
47
+ FILE: operationRoute.has(ParamTypes.FILES),
48
+ RAW_BODY: operationRoute.has(ParamTypes.RAW_BODY),
49
+ BODY: operationRoute.has(ParamTypes.BODY) || operationRoute.method === "ALL"
50
+ }
51
+ );
52
+ }
53
+ }
54
+
55
+ injectable(PlatformMiddlewaresChain);