From 1540b2fefe8c82ae42b423ad915f41dd28c84680 Mon Sep 17 00:00:00 2001 From: Arthur Melin Date: Mon, 10 Feb 2020 23:28:24 +0000 Subject: [PATCH 1/3] Add support for wildcard "all" routes --- src/decorator/All.ts | 28 ++++++++++++++++++++++++++++ src/driver/express/ExpressDriver.ts | 4 ++-- src/index.ts | 1 + src/metadata/types/ActionType.ts | 5 +++-- 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 src/decorator/All.ts diff --git a/src/decorator/All.ts b/src/decorator/All.ts new file mode 100644 index 00000000..1dea5dcc --- /dev/null +++ b/src/decorator/All.ts @@ -0,0 +1,28 @@ +import {getMetadataArgsStorage} from "../index"; + +/** + * Registers an action to be executed when a request comes on a given route. + * Must be applied on a controller action. + */ +export function All(route?: RegExp): Function; + +/** + * Registers an action to be executed when a request comes on a given route. + * Must be applied on a controller action. + */ +export function All(route?: string): Function; + +/** + * Registers an action to be executed when a request comes on a given route. + * Must be applied on a controller action. + */ +export function All(route?: string|RegExp): Function { + return function (object: Object, methodName: string) { + getMetadataArgsStorage().actions.push({ + type: "all", + target: object.constructor, + method: methodName, + route: route + }); + }; +} diff --git a/src/driver/express/ExpressDriver.ts b/src/driver/express/ExpressDriver.ts index c6ea6a6d..c15595cc 100644 --- a/src/driver/express/ExpressDriver.ts +++ b/src/driver/express/ExpressDriver.ts @@ -165,7 +165,7 @@ export class ExpressDriver extends BaseDriver { // This causes a double action execution on our side, which results in an unhandled rejection, // saying: "Can't set headers after they are sent". // The following line skips action processing when the request method does not match the action method. - if (request.method.toLowerCase() !== actionMetadata.type) + if (actionMetadata.type !== "all" && request.method.toLowerCase() !== actionMetadata.type) return next(); return executeCallback({request, response, next}); @@ -207,7 +207,7 @@ export class ExpressDriver extends BaseDriver { case "session-param": return request.session[param.name]; - + case "session": return request.session; diff --git a/src/index.ts b/src/index.ts index e143ab93..01014e5a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import {importClassesFromDirectories} from "./util/importClassesFromDirectories" export * from "./container"; +export * from "./decorator/All"; export * from "./decorator/Authorized"; export * from "./decorator/Body"; export * from "./decorator/BodyParam"; diff --git a/src/metadata/types/ActionType.ts b/src/metadata/types/ActionType.ts index 4460da17..89996c90 100644 --- a/src/metadata/types/ActionType.ts +++ b/src/metadata/types/ActionType.ts @@ -1,7 +1,8 @@ /** * Controller action type. */ -export type ActionType = "checkout" +export type ActionType = "all" + |"checkout" |"connect" |"copy" |"delete" @@ -26,4 +27,4 @@ export type ActionType = "checkout" |"subscribe" |"trace" |"unlock" - |"unsubscribe"; \ No newline at end of file + |"unsubscribe"; From 7471682c51945cbe7b060c35f524c238ff09484a Mon Sep 17 00:00:00 2001 From: Arthur Melin Date: Thu, 20 Feb 2020 14:43:02 +0000 Subject: [PATCH 2/3] Document @All in README --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6626a260..e1b9aa10 100644 --- a/README.md +++ b/README.md @@ -733,7 +733,7 @@ There are set of prepared errors you can use: * UnauthorizedError -You can also create and use your own errors by extending `HttpError` class. +You can also create and use your own errors by extending `HttpError` class. To define the data returned to the client, you could define a toJSON method in your error. ```typescript @@ -755,7 +755,7 @@ class DbError extends HttpError { } } } -``` +``` #### Enable CORS @@ -796,7 +796,7 @@ app.listen(3000); #### Default settings -You can override default status code in routing-controllers options. +You can override default status code in routing-controllers options. ```typescript import "reflect-metadata"; @@ -809,9 +809,9 @@ const app = createExpressServer({ //with this option, null will return 404 by default nullResultCode: 404, - //with this option, void or Promise will return 204 by default + //with this option, void or Promise will return 204 by default undefinedResultCode: 204, - + paramOptions: { //with this option, argument will be required by default required: true @@ -1486,7 +1486,8 @@ export class QuestionController { | `@Patch(route: string\|RegExp)` | `@Patch("/users/:id") patch()` | Methods marked with this decorator will register a request made with PATCH HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.patch("/users/:id", patch)` | | `@Delete(route: string\|RegExp)` | `@Delete("/users/:id") delete()` | Methods marked with this decorator will register a request made with DELETE HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.delete("/users/:id", delete)` | | `@Head(route: string\|RegExp)` | `@Head("/users/:id") head()` | Methods marked with this decorator will register a request made with HEAD HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.head("/users/:id", head)` | -| `@Method(methodName: string, route: string\|RegExp)` | `@Method("move", "/users/:id") move()` | Methods marked with this decorator will register a request made with given `methodName` HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.move("/users/:id", move)` | +| `@All(route: string\|RegExp)` | `@All("/users/me") rewrite()` | Methods marked with this decorator will register a request made with any HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.all("/users/me", rewrite)` | +| `@Method(methodName: string, route: string\|RegExp)` | `@Method("move", "/users/:id") move()` | Methods marked with this decorator will register a request made with given `methodName` HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.move("/users/:id", move)` | #### Method Parameter Decorators From 694901abf12e9f6313ad3bf0d6d8b813cba14b63 Mon Sep 17 00:00:00 2001 From: Arthur Melin Date: Fri, 29 May 2020 18:36:13 +0200 Subject: [PATCH 3/3] Add tests for All decorator --- test/functional/controller-methods.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/functional/controller-methods.spec.ts b/test/functional/controller-methods.spec.ts index da682f7f..1be1404d 100644 --- a/test/functional/controller-methods.spec.ts +++ b/test/functional/controller-methods.spec.ts @@ -7,6 +7,7 @@ import {Head} from "../../src/decorator/Head"; import {Delete} from "../../src/decorator/Delete"; import {Patch} from "../../src/decorator/Patch"; import {Put} from "../../src/decorator/Put"; +import {All} from "../../src/decorator/All"; import {ContentType} from "../../src/decorator/ContentType"; import {JsonController} from "../../src/decorator/JsonController"; import {UnauthorizedError} from "../../src/http-error/UnauthorizedError"; @@ -52,6 +53,10 @@ describe("controller methods", () => { head() { return "Removing user"; } + @All("/users/me") + all() { + return "Current user"; + } @Method("post", "/categories") postCategories() { return "Posting categories"; @@ -196,6 +201,19 @@ describe("controller methods", () => { }); }); + describe("all respond with proper status code, headers and body content", () => { + const callback = (response: any) => { + expect(response).to.have.status(200); + expect(response).to.have.header("content-type", "text/html; charset=utf-8"); + expect(response.body).to.be.equal("Current user"); + }; + + assertRequest([3001, 3002], "get", "users/me", callback); + assertRequest([3001, 3002], "put", "users/me", callback); + assertRequest([3001, 3002], "patch", "users/me", callback); + assertRequest([3001, 3002], "delete", "users/me", callback); + }); + describe("custom method (post) respond with proper status code, headers and body content", () => { assertRequest([3001, 3002], "post", "categories", response => { expect(response).to.have.status(200);