Skip to content

Commit

Permalink
chore(mvc): Merge EndpointBuilder to ControllerBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed May 9, 2019
1 parent 9c603c6 commit a86ac81
Show file tree
Hide file tree
Showing 23 changed files with 570 additions and 694 deletions.
6 changes: 2 additions & 4 deletions packages/common/src/filters/class/FilterBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {Type} from "@tsed/core";
import {InjectorService} from "@tsed/di";
import {ConverterService} from "../../converters/services/ConverterService";
import {ConverterService} from "../../converters";
import {ParseExpressionError} from "../errors/ParseExpressionError";
import {RequiredParamError} from "../errors/RequiredParamError";
import {UnknowFilterError} from "../errors/UnknowFilterError";
import {IFilter} from "../interfaces";
import {IFilterPreHandler} from "../interfaces/IFilterPreHandler";
import {IFilterScope} from "../interfaces/IFilterScope";
import {IFilter, IFilterPreHandler, IFilterScope} from "../interfaces";
import {FilterPreHandlers} from "../registries/FilterRegistry";
import {ValidationService} from "../services/ValidationService";
import {ParamMetadata} from "./ParamMetadata";
Expand Down
72 changes: 55 additions & 17 deletions packages/common/src/mvc/class/ControllerBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {Type} from "@tsed/core";
import {InjectorService} from "@tsed/di";
import * as Express from "express";
import {ControllerProvider} from "../class/ControllerProvider";
import {EndpointMetadata} from "../class/EndpointMetadata";
import {bindEndpointMiddleware} from "../components/bindEndpointMiddleware";
import {SendResponseMiddleware} from "../components/SendResponseMiddleware";
import {ControllerProvider} from "./ControllerProvider";
import {EndpointBuilder} from "./EndpointBuilder";
import {HandlerBuilder} from "./HandlerBuilder";

export class ControllerBuilder {
Expand All @@ -13,39 +14,74 @@ export class ControllerBuilder {
*
* @returns {any}
*/
build(injector: InjectorService): this {
public build(injector: InjectorService): Express.Router {
const {
routerOptions,
middlewares: {useBefore, useAfter}
} = this.provider;
const defaultRoutersOptions = injector.settings.routers;

// TODO Use injector create new router instance
const defaultRoutersOptions = injector.settings.routers;
this.provider.router = Express.Router(Object.assign({}, defaultRoutersOptions, routerOptions));

this.buildMiddlewares(injector, useBefore!)
.buildEndpoints(injector)
.buildMiddlewares(injector, useAfter!)
.buildSendResponse(injector)
.buildDependencies(injector);
// Controller lifecycle
this.buildMiddlewares(injector, useBefore) // Controller before-middleware
.buildEndpoints(injector) // All endpoints and his middlewares
.buildMiddlewares(injector, useAfter) // Controller after-middleware
.buildSendResponse(injector) // Final middleware to send response
.buildChildrenCtrls(injector); // Children controllers

return this;
return this.provider.router;
}

private buildEndpoints(injector: InjectorService) {
const {endpoints} = this.provider;

endpoints.forEach(endpoint => {
new EndpointBuilder(endpoint).build(injector);
});
endpoints.forEach((endpoint: EndpointMetadata) => this.buildEndpoint(injector, endpoint));

return this;
}

private buildDependencies(injector: InjectorService) {
const {dependencies, router} = this.provider;
private buildEndpoint(injector: InjectorService, endpoint: EndpointMetadata) {
const {beforeMiddlewares, middlewares: mldwrs, afterMiddlewares, pathsMethods} = endpoint;
const {
router,
middlewares: {use}
} = this.provider;

// Endpoint lifecycle
let handlers: any[] = [];

handlers = handlers
.concat(use) // Controller use-middlewares
.concat(beforeMiddlewares) // Endpoint before-middlewares
.concat(mldwrs) // Endpoint middlewares
.concat(endpoint) // Endpoint handler
.concat(afterMiddlewares) // Endpoint after-middlewares
.filter((item: any) => !!item)
.map((middleware: any) => HandlerBuilder.from(middleware).build(injector));

handlers = [bindEndpointMiddleware(endpoint)].concat(handlers);

// Add handlers to the router
pathsMethods.forEach(({path, method}) => {
if (!!method && router[method]) {
router[method](path, ...handlers);
} else {
const args: any[] = [path].concat(handlers);
router.use(...args);
}
});

if (!pathsMethods.length) {
router.use(...handlers);
}
}

private buildChildrenCtrls(injector: InjectorService) {
const {children, router} = this.provider;

dependencies.forEach((child: Type<any>) => {
children.forEach((child: Type<any>) => {
const provider = injector.getProvider(child) as ControllerProvider;

/* istanbul ignore next */
Expand All @@ -68,7 +104,9 @@ export class ControllerBuilder {

middlewares
.filter(o => typeof o === "function")
.forEach((middleware: any) => router.use(HandlerBuilder.from(middleware).build(injector)));
.forEach((middleware: any) => {
router.use(HandlerBuilder.from(middleware).build(injector));
});

return this;
}
Expand Down
60 changes: 18 additions & 42 deletions packages/common/src/mvc/class/ControllerProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {getClass, NotEnumerable, Type} from "@tsed/core";
import {Provider} from "@tsed/di";
import {Enumerable, NotEnumerable, Type} from "@tsed/core";
import {Provider, ProviderType} from "@tsed/di";
import * as Express from "express";
import {IRouterSettings} from "../../config/interfaces/IServerSettings";

Expand All @@ -13,64 +13,48 @@ export interface IChildrenController extends Type<any> {

export class ControllerProvider extends Provider<any> implements IControllerProvider {
@NotEnumerable()
public router: Express.Router;
public router: Express.Router & {[index: string]: any};
/**
* The path for the controller
*/
@NotEnumerable()
private _path: string;
@Enumerable()
public path: string;
/**
* Controllers that depend to this controller.
* @type {Array}
* @private
*/
@NotEnumerable()
private _dependencies: IChildrenController[] = [];
private _children: IChildrenController[] = [];

constructor(provide: any) {
super(provide);
this.type = "controller";
}

/**
*
* @returns {string}
*/
get path(): string {
return this._path;
}

/**
* set path
* @param value
*/
set path(value: string) {
this._path = value;
this.type = ProviderType.CONTROLLER;
}

/**
*
* @returns {Endpoint[]}
*/
get endpoints(): EndpointMetadata[] {
return EndpointRegistry.getEndpoints(getClass(this.provide));
return EndpointRegistry.getEndpoints(this.provide);
}

/**
*
* @returns {Type<any>[]}
*/
get dependencies(): IChildrenController[] {
return this._dependencies;
get children(): IChildrenController[] {
return this._children;
}

/**
*
* @param dependencies
* @param children
*/
set dependencies(dependencies: IChildrenController[]) {
this._dependencies = dependencies;
this._dependencies.forEach(d => (d.$parentCtrl = this));
set children(children: IChildrenController[]) {
this._children = children;
this._children.forEach(d => (d.$parentCtrl = this));
}

/**
Expand Down Expand Up @@ -129,7 +113,7 @@ export class ControllerProvider extends Provider<any> implements IControllerProv
/**
* Resolve final endpoint url.
*/
public getEndpointUrl = (routerPath: string): string =>
public getEndpointUrl = (routerPath?: string): string =>
(routerPath === this.path ? this.path : (routerPath || "") + this.path).replace(/\/\//gi, "/");

/**
Expand All @@ -139,14 +123,6 @@ export class ControllerProvider extends Provider<any> implements IControllerProv
return !!this.path;
}

/**
*
* @returns {boolean}
*/
public hasDependencies(): boolean {
return !!this.dependencies.length;
}

/**
*
* @returns {boolean}
Expand All @@ -157,11 +133,11 @@ export class ControllerProvider extends Provider<any> implements IControllerProv

clone(): ControllerProvider {
const provider = new ControllerProvider(this._provide);
provider._type = this._type;
provider.path = this.path;
provider.type = this.type;
provider.useClass = this._useClass;
provider._instance = this._instance;
provider._path = this._path;
provider._dependencies = this._dependencies;
provider._children = this._children;

return provider;
}
Expand Down
80 changes: 0 additions & 80 deletions packages/common/src/mvc/class/EndpointBuilder.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/common/src/mvc/class/EndpointMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class EndpointMetadata extends Storable {
@NotEnumerable()
private inheritedEndpoint: EndpointMetadata;

constructor(_provide: Type<any>, private _methodClassName: string) {
constructor(_provide: Type<any>, private _methodClassName: string | symbol) {
super(_provide, _methodClassName, Object.getOwnPropertyDescriptor(_provide, _methodClassName));

this._type = Metadata.getReturnType(this._target, this.methodClassName);
Expand Down Expand Up @@ -96,7 +96,7 @@ export class EndpointMetadata extends Storable {
*
*/
get methodClassName(): string {
return this._methodClassName;
return String(this._methodClassName);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/common/src/mvc/components/bindEndpointMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {EndpointMetadata} from "../class/EndpointMetadata";

export function bindEndpointMiddleware(endpoint: EndpointMetadata) {
return (request: any, response: any, next: any) => {
request.ctx.endpoint = endpoint;
next();
};
}
7 changes: 4 additions & 3 deletions packages/common/src/mvc/decorators/class/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,22 @@ import {IControllerProvider, PathParamsType} from "../../interfaces";
* ```
*
* @param options
* @param dependencies
* @param children
* @returns {Function}
* @decorator
*/
export function Controller(options: PathParamsType | IControllerProvider, ...dependencies: Type<any>[]): Function {
export function Controller(options: PathParamsType | IControllerProvider, ...children: Type<any>[]): Function {
return (target: any): void => {
if (typeof options === "string" || options instanceof RegExp || isArrayOrArrayClass(options)) {
registerController({
provide: target,
path: options,
dependencies
children
});
} else {
registerController({
provide: target,
children: (options as IControllerProvider).dependencies || (options as IControllerProvider).children,
...options
});
}
Expand Down
6 changes: 3 additions & 3 deletions packages/common/src/mvc/interfaces/IControllerMiddlewares.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export interface IControllerMiddlewares {
useBefore?: any[];
use?: any[];
useAfter?: any[];
useBefore: any[];
use: any[];
useAfter: any[];
}

0 comments on commit a86ac81

Please sign in to comment.