arrow-express
Advanced tools
Comparing version 1.2.3 to 2.0.0
@@ -24,3 +24,3 @@ import Express from "express"; | ||
*/ | ||
registerController(controller: ControllerConfiguration<any>): AppConfigurator; | ||
registerController(controller: ControllerConfiguration<any, any>): AppConfigurator; | ||
/** | ||
@@ -30,3 +30,3 @@ * Register list of controllers in application. | ||
*/ | ||
registerControllers(...controllers: ControllerConfiguration<any>[]): AppConfigurator; | ||
registerControllers(...controllers: ControllerConfiguration<any, any>[]): AppConfigurator; | ||
private printExpressConfig; | ||
@@ -33,0 +33,0 @@ private startControllers; |
@@ -103,23 +103,29 @@ "use strict"; | ||
}; | ||
AppConfigurator.prototype.startController = function (controller, prefix) { | ||
AppConfigurator.prototype.startController = function (controller, controllersChain) { | ||
var _this = this; | ||
if (prefix === void 0) { prefix = ""; } | ||
if (!controllersChain) { | ||
controllersChain = [controller]; | ||
} | ||
else { | ||
controllersChain.push(controller); | ||
} | ||
controller.getControllers().forEach(function (subController) { | ||
_this.startController(subController, AppConfigurator.getRoutePath(controller.getPrefix(), prefix)); | ||
_this.startController(subController, controllersChain); | ||
}); | ||
controller.getRoutes().forEach(function (route) { | ||
_this.registerRouteInExpress(controller, route, prefix); | ||
_this.registerRouteInExpress(controllersChain, route); | ||
}); | ||
}; | ||
AppConfigurator.prototype.registerRouteInExpress = function (controller, route, prefix) { | ||
var routePath = AppConfigurator.getRoutePath(prefix, controller.getPrefix(), route.getPath()); | ||
AppConfigurator.prototype.registerRouteInExpress = function (controllersChain, route) { | ||
var controllersPrefix = controllersChain.reduce(function (prefixAcc, controller) { return AppConfigurator.getRoutePath(prefixAcc, controller.getPrefix()); }, ""); | ||
var routePath = AppConfigurator.getRoutePath(controllersPrefix, route.getPath()); | ||
if (!route.getMethod()) { | ||
throw new configuration_error_1.ConfigurationError("Route ".concat(routePath, " has no method specified")); | ||
} | ||
this._express[route.getMethod()]("/".concat(routePath), this.createApplicationRequestHandler(route, controller)); | ||
this._express[route.getMethod()]("/".concat(routePath), this.createApplicationRequestHandler(route, controllersChain)); | ||
}; | ||
AppConfigurator.prototype.createApplicationRequestHandler = function (route, controller) { | ||
AppConfigurator.prototype.createApplicationRequestHandler = function (route, controllersChain) { | ||
var _this = this; | ||
return function (req, res) { return __awaiter(_this, void 0, void 0, function () { | ||
var context, response, error_1; | ||
var context, _i, controllersChain_1, controller, newContextValue, response, error_1; | ||
var _a; | ||
@@ -129,8 +135,19 @@ return __generator(this, function (_b) { | ||
case 0: | ||
_b.trys.push([0, 3, 4, 5]); | ||
return [4 /*yield*/, ((_a = controller.getHandler()) === null || _a === void 0 ? void 0 : _a(req, res))]; | ||
_b.trys.push([0, 6, 7, 8]); | ||
context = void 0; | ||
_i = 0, controllersChain_1 = controllersChain; | ||
_b.label = 1; | ||
case 1: | ||
context = _b.sent(); | ||
return [4 /*yield*/, route.getRequestHandler()(req, res, context)]; | ||
if (!(_i < controllersChain_1.length)) return [3 /*break*/, 4]; | ||
controller = controllersChain_1[_i]; | ||
return [4 /*yield*/, ((_a = controller.getHandler()) === null || _a === void 0 ? void 0 : _a(req, res, context))]; | ||
case 2: | ||
newContextValue = _b.sent(); | ||
context = newContextValue ? newContextValue : context; | ||
_b.label = 3; | ||
case 3: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 4: return [4 /*yield*/, route.getRequestHandler()(req, res, context)]; | ||
case 5: | ||
response = _b.sent(); | ||
@@ -143,4 +160,4 @@ if (AppConfigurator.canSendResponse(res)) { | ||
} | ||
return [3 /*break*/, 5]; | ||
case 3: | ||
return [3 /*break*/, 8]; | ||
case 6: | ||
error_1 = _b.sent(); | ||
@@ -155,7 +172,7 @@ if (AppConfigurator.canSendResponse(res)) { | ||
} | ||
return [3 /*break*/, 5]; | ||
case 4: | ||
return [3 /*break*/, 8]; | ||
case 7: | ||
this.logRequest(req, res); | ||
return [7 /*endfinally*/]; | ||
case 5: return [2 /*return*/]; | ||
case 8: return [2 /*return*/]; | ||
} | ||
@@ -162,0 +179,0 @@ }); |
import { RouteConfigurator } from "../route/route"; | ||
import Express from "express"; | ||
export type ControllerHandler<C = undefined> = (request: Express.Request, response: Express.Response) => Promise<C>; | ||
export declare class ControllerConfiguration<C = undefined> { | ||
export type ControllerHandler<C = undefined, R = undefined> = (request: Express.Request, response: Express.Response, rootContext?: R) => Promise<C>; | ||
export declare class ControllerConfiguration<C = undefined, R = undefined> { | ||
private _prefix; | ||
@@ -13,3 +13,3 @@ private _controllers; | ||
*/ | ||
registerController(controller: ControllerConfiguration): this; | ||
registerController(controller: ControllerConfiguration<any, C>): this; | ||
/** | ||
@@ -19,3 +19,3 @@ * Register array of controllers in controller | ||
*/ | ||
registerControllers(...controllers: ControllerConfiguration[]): this; | ||
registerControllers(...controllers: ControllerConfiguration<any>[]): this; | ||
/** | ||
@@ -30,3 +30,3 @@ * Register route in controller | ||
*/ | ||
registerRoutes(...routes: RouteConfigurator<C>[]): this; | ||
registerRoutes(...routes: RouteConfigurator<C, R>[]): this; | ||
/** | ||
@@ -41,8 +41,8 @@ * Register controller prefix which will be used by all routes | ||
*/ | ||
handler<NewContext>(handler: ControllerHandler<NewContext>): ControllerConfiguration<NewContext>; | ||
handler<NewContext>(handler: ControllerHandler<NewContext, R>): ControllerConfiguration<NewContext, R>; | ||
getPrefix(): string; | ||
getRoutes(): RouteConfigurator<C>[]; | ||
getControllers(): ControllerConfiguration[]; | ||
getHandler(): ControllerHandler<C> | undefined; | ||
getControllers(): ControllerConfiguration<any, C>[]; | ||
getHandler(): ControllerHandler<C, R> | undefined; | ||
} | ||
export declare function Controller<C = undefined>(): ControllerConfiguration<C>; | ||
export declare function Controller<C = undefined, R = undefined>(): ControllerConfiguration<C, R>; |
import Express from "express"; | ||
export type RouteHandler<C = undefined, R = unknown> = (request: Express.Request, response: Express.Response, context: C) => R | Promise<R>; | ||
export type RouteHandler<C = undefined, R = undefined> = (request: Express.Request, response: Express.Response, context: C) => R | Promise<R>; | ||
export type HttpMethod = "get" | "post" | "head" | "put" | "delete" | "options" | "patch"; | ||
export declare class RouteConfigurator<C = undefined, R = unknown> { | ||
export declare class RouteConfigurator<C = undefined, R = undefined> { | ||
private _method; | ||
@@ -31,2 +31,2 @@ private _path; | ||
} | ||
export declare function Route<C = undefined, R = unknown>(): RouteConfigurator<C, R>; | ||
export declare function Route<C = undefined, R = undefined>(): RouteConfigurator<C, R>; |
@@ -6,5 +6,5 @@ import { Controller, ControllerConfiguration } from "arrow-express"; | ||
import { GetMyselfRoute } from "./routes/getMyself.route"; | ||
import { AuthorizeGuard } from "../guards/authorize.guard"; | ||
import { AuthorizeGuard, UserContext } from "../guards/authorize.guard"; | ||
export function UserController(userService: UserService): ControllerConfiguration { | ||
export function UserController(userService: UserService): ControllerConfiguration<UserContext> { | ||
return Controller() | ||
@@ -11,0 +11,0 @@ .handler(AuthorizeGuard) |
import Express from "express"; | ||
import { Application } from "./application"; | ||
import { Controller } from "../controller/controller"; | ||
import { Controller, ControllerHandler } from "../controller/controller"; | ||
import { Route } from "../route/route"; | ||
@@ -179,3 +179,3 @@ import { RequestError } from "../error/request.error"; | ||
it("should pass context from controller handler to route handler", async () => { | ||
const spy = jest.fn().mockResolvedValue("context"); | ||
const spy = jest.fn().mockResolvedValue("context") as ControllerHandler<any>; | ||
const routeSpy = jest.fn(); | ||
@@ -188,4 +188,34 @@ Application({ app: ExpressAppStub, logRequests: false }) | ||
}); | ||
it("should pass context from root controller to route handler", async () => { | ||
const spy = jest.fn().mockResolvedValue("context") as ControllerHandler<any>; | ||
const routeSpy = jest.fn(); | ||
Application({ app: ExpressAppStub, logRequests: false }) | ||
.registerController( | ||
Controller() | ||
.handler(spy) | ||
.registerController(Controller().registerRoute(Route().method("get").handler(routeSpy))) | ||
) | ||
.configure(false); | ||
await mocked(ExpressAppStub.get).mock.calls[0][1]({} as never, resSpy); | ||
expect(routeSpy).toHaveBeenCalledWith(expect.anything(), expect.anything(), "context"); | ||
}); | ||
it("should pass context through controllers chain", async () => { | ||
const rootSpy = jest.fn().mockResolvedValue("root") as ControllerHandler<any>; | ||
const spy = jest.fn().mockImplementation((_, __, context) => context + "-child") as ControllerHandler<any>; | ||
const routeSpy = jest.fn(); | ||
Application({ app: ExpressAppStub, logRequests: false }) | ||
.registerController( | ||
Controller() | ||
.handler(rootSpy) | ||
.registerController(Controller().handler(spy).registerRoute(Route().method("get").handler(routeSpy))) | ||
) | ||
.configure(false); | ||
await mocked(ExpressAppStub.get).mock.calls[0][1]({} as never, resSpy); | ||
expect(routeSpy).toHaveBeenCalledWith(expect.anything(), expect.anything(), "root-child"); | ||
}); | ||
it("should call controller handler", async () => { | ||
const spy = jest.fn().mockRejectedValue(new Error()); | ||
const spy = jest.fn().mockRejectedValue(new Error()) as ControllerHandler<any>; | ||
Application({ app: ExpressAppStub, logRequests: false }) | ||
@@ -202,3 +232,3 @@ .registerController(Controller().handler(spy).registerRoute(Route().method("get"))) | ||
}; | ||
const spy = jest.fn().mockRejectedValue(new RequestError(401, response)); | ||
const spy = jest.fn().mockRejectedValue(new RequestError(401, response)) as ControllerHandler<any>; | ||
Application({ app: ExpressAppStub, logRequests: false }) | ||
@@ -205,0 +235,0 @@ .registerController(Controller().handler(spy).registerRoute(Route().method("get"))) |
@@ -46,3 +46,3 @@ import Express from "express"; | ||
*/ | ||
registerController(controller: ControllerConfiguration<any>): AppConfigurator { | ||
registerController(controller: ControllerConfiguration<any, any>): AppConfigurator { | ||
this._controllers.push(controller); | ||
@@ -56,3 +56,3 @@ return this; | ||
*/ | ||
registerControllers(...controllers: ControllerConfiguration<any>[]): AppConfigurator { | ||
registerControllers(...controllers: ControllerConfiguration<any, any>[]): AppConfigurator { | ||
controllers.forEach(controller => this.registerController(controller)); | ||
@@ -73,13 +73,22 @@ return this; | ||
private startController(controller: ControllerConfiguration, prefix = "") { | ||
private startController(controller: ControllerConfiguration, controllersChain?: ControllerConfiguration[]) { | ||
if (!controllersChain) { | ||
controllersChain = [controller]; | ||
} else { | ||
controllersChain.push(controller); | ||
} | ||
controller.getControllers().forEach(subController => { | ||
this.startController(subController, AppConfigurator.getRoutePath(controller.getPrefix(), prefix)); | ||
this.startController(subController, controllersChain); | ||
}); | ||
controller.getRoutes().forEach(route => { | ||
this.registerRouteInExpress(controller, route, prefix); | ||
this.registerRouteInExpress(controllersChain, route); | ||
}); | ||
} | ||
private registerRouteInExpress(controller: ControllerConfiguration, route: RouteConfigurator, prefix?: string) { | ||
const routePath = AppConfigurator.getRoutePath(prefix, controller.getPrefix(), route.getPath()); | ||
private registerRouteInExpress(controllersChain: ControllerConfiguration[], route: RouteConfigurator) { | ||
const controllersPrefix = controllersChain.reduce( | ||
(prefixAcc, controller) => AppConfigurator.getRoutePath(prefixAcc, controller.getPrefix()), | ||
"" | ||
); | ||
const routePath = AppConfigurator.getRoutePath(controllersPrefix, route.getPath()); | ||
@@ -90,3 +99,3 @@ if (!route.getMethod()) { | ||
this._express[route.getMethod()](`/${routePath}`, this.createApplicationRequestHandler(route, controller)); | ||
this._express[route.getMethod()](`/${routePath}`, this.createApplicationRequestHandler(route, controllersChain)); | ||
} | ||
@@ -96,7 +105,13 @@ | ||
route: RouteConfigurator, | ||
controller: ControllerConfiguration | ||
controllersChain: ControllerConfiguration[] | ||
): Express.RequestHandler { | ||
return async (req: Express.Request, res: Express.Response) => { | ||
try { | ||
const context = await controller.getHandler()?.(req, res); | ||
let context: undefined; | ||
for (const controller of controllersChain) { | ||
const newContextValue = await controller.getHandler()?.(req, res, context); | ||
context = newContextValue ? newContextValue : context; | ||
} | ||
const response = await route.getRequestHandler()(req, res, context); | ||
@@ -103,0 +118,0 @@ if (AppConfigurator.canSendResponse(res)) { |
import { RouteConfigurator } from "../route/route"; | ||
import Express from "express"; | ||
export type ControllerHandler<C = undefined> = (request: Express.Request, response: Express.Response) => Promise<C>; | ||
export class ControllerConfiguration<C = undefined> { | ||
export type ControllerHandler<C = undefined, R = undefined> = ( | ||
request: Express.Request, | ||
response: Express.Response, | ||
rootContext?: R | ||
) => Promise<C>; | ||
export class ControllerConfiguration<C = undefined, R = undefined> { | ||
private _prefix = ""; | ||
private _controllers: ControllerConfiguration[] = []; | ||
private _controllers: ControllerConfiguration<unknown, C>[] = []; | ||
private _routes: RouteConfigurator<C>[] = []; | ||
private _handler: ControllerHandler<C> | undefined; | ||
private _handler: ControllerHandler<C, R> | undefined; | ||
@@ -15,3 +20,3 @@ /** | ||
*/ | ||
registerController(controller: ControllerConfiguration): this { | ||
registerController(controller: ControllerConfiguration<any, C>): this { | ||
this._controllers.push(controller); | ||
@@ -25,3 +30,3 @@ return this; | ||
*/ | ||
registerControllers(...controllers: ControllerConfiguration[]): this { | ||
registerControllers(...controllers: ControllerConfiguration<any>[]): this { | ||
controllers.forEach(this.registerController.bind(this)); | ||
@@ -44,3 +49,3 @@ return this; | ||
*/ | ||
registerRoutes(...routes: RouteConfigurator<C>[]): this { | ||
registerRoutes(...routes: RouteConfigurator<C, R>[]): this { | ||
routes.forEach(this.registerRoute.bind(this)); | ||
@@ -62,5 +67,5 @@ return this; | ||
*/ | ||
handler<NewContext>(handler: ControllerHandler<NewContext>): ControllerConfiguration<NewContext> { | ||
this._handler = handler as unknown as ControllerHandler<C>; | ||
return this as unknown as ControllerConfiguration<NewContext>; | ||
handler<NewContext>(handler: ControllerHandler<NewContext, R>): ControllerConfiguration<NewContext, R> { | ||
this._handler = handler as unknown as ControllerHandler<C, R>; | ||
return this as unknown as ControllerConfiguration<NewContext, R>; | ||
} | ||
@@ -76,7 +81,7 @@ | ||
getControllers(): ControllerConfiguration[] { | ||
getControllers(): ControllerConfiguration<any, C>[] { | ||
return this._controllers; | ||
} | ||
getHandler(): ControllerHandler<C> | undefined { | ||
getHandler(): ControllerHandler<C, R> | undefined { | ||
return this._handler; | ||
@@ -86,4 +91,4 @@ } | ||
export function Controller<C = undefined>(): ControllerConfiguration<C> { | ||
return new ControllerConfiguration<C>(); | ||
export function Controller<C = undefined, R = undefined>(): ControllerConfiguration<C, R> { | ||
return new ControllerConfiguration<C, R>(); | ||
} |
import Express from "express"; | ||
export type RouteHandler<C = undefined, R = unknown> = ( | ||
export type RouteHandler<C = undefined, R = undefined> = ( | ||
request: Express.Request, | ||
@@ -10,3 +10,3 @@ response: Express.Response, | ||
export class RouteConfigurator<C = undefined, R = unknown> { | ||
export class RouteConfigurator<C = undefined, R = undefined> { | ||
private _method: HttpMethod; | ||
@@ -60,4 +60,4 @@ private _path: string; | ||
export function Route<C = undefined, R = unknown>(): RouteConfigurator<C, R> { | ||
export function Route<C = undefined, R = undefined>(): RouteConfigurator<C, R> { | ||
return new RouteConfigurator<C, R>(); | ||
} |
{ | ||
"name": "arrow-express", | ||
"version": "1.2.3", | ||
"version": "2.0.0", | ||
"description": "Library to bootstrap express applications with zero configuration", | ||
@@ -10,9 +10,2 @@ "main": "dist/index.js", | ||
}, | ||
"scripts": { | ||
"build": "tsc --declaration --project tsconfig.json", | ||
"test": "jest", | ||
"test:watch": "jest --watch", | ||
"test:coverage": "jest --coverage", | ||
"lint": "./node_modules/.bin/eslint ." | ||
}, | ||
"repository": { | ||
@@ -52,3 +45,10 @@ "type": "git", | ||
}, | ||
"homepage": "https://github.com/Mighty683/arrow-express#readme" | ||
} | ||
"homepage": "https://github.com/Mighty683/arrow-express#readme", | ||
"scripts": { | ||
"build": "tsc --declaration --project tsconfig.json", | ||
"test": "jest", | ||
"test:watch": "jest --watch", | ||
"test:coverage": "jest --coverage", | ||
"lint": "./node_modules/.bin/eslint ." | ||
} | ||
} |
@@ -97,2 +97,6 @@ # Arrow Express | ||
#### Controller handler | ||
Controller handler can be used to eg: authorize user and get it's context which will be passed to routes. Handlers like controllers can be chained. | ||
### Route | ||
@@ -141,2 +145,3 @@ | ||
- `response` - which is Express.Response | ||
- `context` - which is resolution of controller's handler | ||
@@ -143,0 +148,0 @@ Features of route handler: |
795905
15798
197