mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
Update to final version
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Controller } from '../../../src/common/utils/controller.decorator';
|
import { Controller } from '../../../src/common/utils/decorators/controller.decorator';
|
||||||
import { MessagePattern } from '../../../src/microservices/utils/pattern.decorator';
|
import { MessagePattern } from '../../../src/microservices/utils/pattern.decorator';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Module } from '../../../src/common/utils/module.decorator';
|
import { Module } from '../../../src/common/utils/decorators/module.decorator';
|
||||||
import { MathController } from './math.controller';
|
import { MathController } from './math.controller';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ import { ClientController } from './client/client.controller';
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
modules: [ UsersModule ],
|
modules: [ UsersModule ],
|
||||||
controllers: [ ClientController ]
|
controllers: [ ClientController ],
|
||||||
})
|
})
|
||||||
export class ApplicationModule {}
|
export class ApplicationModule {}
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
import { Controller } from '../../../src/common/utils/controller.decorator';
|
import { Controller } from '../../../src/common/utils/decorators/controller.decorator';
|
||||||
import { Client } from '../../../src/microservices/utils/client.decorator';
|
import { Client } from '../../../src/microservices/utils/client.decorator';
|
||||||
import { RequestMapping } from '../../../src/common/utils/request-mapping.decorator';
|
import { RequestMapping } from '../../../src/common/utils/decorators/request-mapping.decorator';
|
||||||
import { ClientProxy } from '../../../src/microservices/client/client-proxy';
|
import { ClientProxy } from '../../../src/microservices/client/client-proxy';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { Transport } from '../../../src/common/enums/transport.enum';
|
import { Transport } from '../../../src/common/enums/transport.enum';
|
||||||
import 'rxjs/add/operator/catch';
|
import 'rxjs/add/operator/catch';
|
||||||
|
|
||||||
|
const MicroserviceClient = { transport: Transport.TCP, port: 5667 };
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class ClientController {
|
export class ClientController {
|
||||||
|
@Client(MicroserviceClient)
|
||||||
@Client({ transport: Transport.TCP, port: 5667 })
|
public client: ClientProxy;
|
||||||
client: ClientProxy;
|
|
||||||
|
|
||||||
@RequestMapping({ path: 'client' })
|
@RequestMapping({ path: 'client' })
|
||||||
sendMessage(req, res) {
|
public sendMessage(req, res) {
|
||||||
const pattern = { command: 'add' };
|
const pattern = { command: 'add' };
|
||||||
const data = [ 1, 2, 3, 4, 5 ];
|
const data = [ 1, 2, 3, 4, 5 ];
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Module } from '../../../src/common/utils/module.decorator';
|
import { Module } from '../../../src/common/utils/decorators/module.decorator';
|
||||||
import { ChatGateway } from '../users/chat.gateway';
|
import { ChatGateway } from '../users/chat.gateway';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
components: [ ChatGateway ],
|
components: [ ChatGateway ],
|
||||||
exports: [ ChatGateway ]
|
exports: [ ChatGateway ],
|
||||||
})
|
})
|
||||||
export class SharedModule {}
|
export class SharedModule {}
|
||||||
@@ -7,18 +7,17 @@ import { NestMiddleware } from '../../../src/core/middlewares/interfaces/nest-mi
|
|||||||
export class AuthMiddleware implements NestMiddleware {
|
export class AuthMiddleware implements NestMiddleware {
|
||||||
constructor(private usersService: UsersService) {}
|
constructor(private usersService: UsersService) {}
|
||||||
|
|
||||||
resolve() {
|
public resolve() {
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
const userName = req.headers['x-access-token'];
|
const username = req.headers['x-access-token'];
|
||||||
const users = this.usersService.getUsers();
|
const users = this.usersService.getUsers();
|
||||||
|
const user = users.find(({ name }) => name === username);
|
||||||
const user = users.find((user) => user.name === userName);
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new HttpException('User not found.', 401);
|
throw new HttpException('User not found.', 401);
|
||||||
}
|
}
|
||||||
req.user = user;
|
req.user = user;
|
||||||
next();
|
next();
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { WebSocketGateway } from '../../../src/websockets/utils/socket-gateway.decorator';
|
import { WebSocketGateway } from '../../../src/websockets/utils/socket-gateway.decorator';
|
||||||
import { SubscribeMessage } from '../../../src/websockets/utils/subscribe-message.decorator';
|
import { SubscribeMessage } from '../../../src/websockets/utils/subscribe-message.decorator';
|
||||||
import { Subject } from 'rxjs/Subject';
|
|
||||||
import { WebSocketServer } from '../../../src/websockets/utils/gateway-server.decorator';
|
import { WebSocketServer } from '../../../src/websockets/utils/gateway-server.decorator';
|
||||||
|
import { OnGatewayInit, OnGatewayConnection } from '../../../src/websockets/index';
|
||||||
|
import { ChatMiddleware } from './chat.middleware';
|
||||||
|
|
||||||
@WebSocketGateway({ port: 2000 })
|
@WebSocketGateway({
|
||||||
export class ChatGateway {
|
port: 2000,
|
||||||
private msg$ = new Subject<any>();
|
middlewares: [ ChatMiddleware ],
|
||||||
|
})
|
||||||
|
export class ChatGateway implements OnGatewayInit, OnGatewayConnection {
|
||||||
|
@WebSocketServer() private server;
|
||||||
|
|
||||||
@WebSocketServer() server;
|
public afterInit(server) {}
|
||||||
|
public handleConnection(client) {}
|
||||||
|
|
||||||
get msgStream() {
|
@SubscribeMessage('event')
|
||||||
return this.msg$.asObservable();
|
public onMessage(client, data) {
|
||||||
|
client.emit('event', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeMessage({ value: 'message' })
|
|
||||||
onMessage(client, data) {
|
|
||||||
this.msg$.next({ client, data });
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
13
example/modules/users/chat.middleware.ts
Normal file
13
example/modules/users/chat.middleware.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { GatewayMiddleware } from '../../../src/websockets/interfaces/gateway-middleware.interface';
|
||||||
|
import { UsersService } from './users.service';
|
||||||
|
import { Middleware } from '../../../src/index';
|
||||||
|
|
||||||
|
@Middleware()
|
||||||
|
export class ChatMiddleware implements GatewayMiddleware {
|
||||||
|
public resolve(): (socket, next) => void {
|
||||||
|
return (socket, next) => {
|
||||||
|
console.log('Authorization...');
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { Component } from '../../../src/common/utils/component.decorator';
|
|
||||||
import { ChatGateway } from './chat.gateway';
|
|
||||||
|
|
||||||
@Component()
|
|
||||||
export class ChatService {
|
|
||||||
|
|
||||||
constructor(private chatGateway: ChatGateway) {
|
|
||||||
const stream$ = this.chatGateway.msgStream;
|
|
||||||
stream$.subscribe(this.storeMessage.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
storeMessage(data) {
|
|
||||||
// store data
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
14
example/modules/users/exception.filter.ts
Normal file
14
example/modules/users/exception.filter.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { ExceptionFilter } from '../../../src/common/interfaces/exception-filter.interface';
|
||||||
|
import { Catch } from '../../../src/common/utils/decorators/catch.decorator';
|
||||||
|
import { UsersService } from './users.service';
|
||||||
|
|
||||||
|
export class CustomException {}
|
||||||
|
|
||||||
|
@Catch(CustomException)
|
||||||
|
export class CustomExceptionFilter implements ExceptionFilter {
|
||||||
|
public catch(exception, response) {
|
||||||
|
response.status(500).json({
|
||||||
|
message: 'Custom exception message.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Component } from '../../../src/common/utils/component.decorator';
|
|
||||||
|
|
||||||
@Component()
|
|
||||||
export class NotificationService {
|
|
||||||
storeNotification(data) {
|
|
||||||
const notification = this.mapDataToNotification(data);
|
|
||||||
// store notification
|
|
||||||
}
|
|
||||||
|
|
||||||
private mapDataToNotification(msg) {}
|
|
||||||
}
|
|
||||||
@@ -1,34 +1,34 @@
|
|||||||
import { UsersService } from "./users.service";
|
import * as express from 'express';
|
||||||
import { RequestMethod, Controller, RequestMapping } from "./../../../src/";
|
import { UsersService } from './users.service';
|
||||||
import { ModuleRef } from '../../../src/core/injector/module-ref';
|
import { Controller, Response, Body, Param } from './../../../src/';
|
||||||
import { UsersModule } from './users.module';
|
import { Get, Post } from '../../../src/common/utils/decorators/request-mapping.decorator';
|
||||||
import { Get, Post } from '../../../src/common/utils/request-mapping.decorator';
|
import { HttpStatus } from '../../../src/common/index';
|
||||||
|
import { ExceptionFilters } from '../../../src/common/utils/decorators/exception-filters.decorator';
|
||||||
|
import { CustomExceptionFilter } from './exception.filter';
|
||||||
|
|
||||||
@Controller({ path: 'users' })
|
@Controller('users')
|
||||||
|
@ExceptionFilters(CustomExceptionFilter)
|
||||||
export class UsersController {
|
export class UsersController {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private usersService: UsersService,
|
private usersService: UsersService) {}
|
||||||
private moduleRef: ModuleRef) {}
|
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
async getAllUsers(req, res) {
|
public async getAllUsers(@Response() res: express.Response) {
|
||||||
const users = await this.usersService.getAllUsers();
|
const users = await this.usersService.getAllUsers();
|
||||||
res.status(200).json(users);
|
res.status(HttpStatus.OK).json(users);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('/:id')
|
@Get('/:id')
|
||||||
async getUser(req, res) {
|
public async getUser(@Response() res: express.Response, @Param('id') id) {
|
||||||
const user = await this.usersService.getUser(req.params.id);
|
const user = await this.usersService.getUser(id);
|
||||||
res.status(200).json(user);
|
res.status(HttpStatus.OK).json(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
async addUser(req, res) {
|
public async addUser(@Response() res: express.Response, @Body('user') user) {
|
||||||
const msg = await this.usersService.getUser(req.body.user);
|
const msg = await this.usersService.getUser(user);
|
||||||
res.status(201).json(msg);
|
res.status(HttpStatus.CREATED).json(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,16 @@
|
|||||||
import { Module } from './../../../src/';
|
import { Module } from './../../../src/';
|
||||||
import { UsersController } from './users.controller';
|
import { UsersController } from './users.controller';
|
||||||
import { UsersService } from './users.service';
|
import { UsersService } from './users.service';
|
||||||
import { MiddlewareBuilder } from '../../../src/core/middlewares/builder';
|
|
||||||
import { AuthMiddleware } from './auth.middleware';
|
import { AuthMiddleware } from './auth.middleware';
|
||||||
|
import { NestModule, MiddlewaresConsumer } from '../../../src/common/index';
|
||||||
|
import { ChatGateway } from './chat.gateway';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [ UsersController ],
|
controllers: [ UsersController ],
|
||||||
components: [
|
components: [ UsersService, ChatGateway ],
|
||||||
UsersService
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class UsersModule {
|
export class UsersModule implements NestModule {
|
||||||
getContext() {
|
public configure(consumer: MiddlewaresConsumer) {
|
||||||
return 'Test';
|
consumer.apply(AuthMiddleware).forRoutes(UsersController);
|
||||||
}
|
|
||||||
|
|
||||||
configure(builder: MiddlewareBuilder) {
|
|
||||||
builder.apply(AuthMiddleware)
|
|
||||||
.with('admin')
|
|
||||||
.forRoutes(UsersController);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,32 @@
|
|||||||
import { Component } from "./../../../src/";
|
import { Component } from './../../../src/';
|
||||||
import { HttpException } from '../../../src/core/exceptions/http-exception';
|
import { HttpException } from '../../../src/core/exceptions/http-exception';
|
||||||
|
|
||||||
@Component()
|
@Component()
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
|
|
||||||
private users = [
|
private users = [
|
||||||
{ id: 1, name: "John Doe" },
|
{ id: 1, name: 'John Doe' },
|
||||||
{ id: 2, name: "Alice Caeiro" },
|
{ id: 2, name: 'Alice Caeiro' },
|
||||||
{ id: 3, name: "Who Knows" },
|
{ id: 3, name: 'Who Knows' },
|
||||||
];
|
];
|
||||||
|
|
||||||
getUsers() {
|
public getUsers() {
|
||||||
return this.users;
|
return this.users;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllUsers() {
|
public getAllUsers() {
|
||||||
return Promise.resolve(this.users);
|
return Promise.resolve(this.users);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUser(id: string) {
|
public getUser(id: string) {
|
||||||
const user = this.users.find((user) => user.id === +id);
|
const user = this.users.find((user) => user.id === +id);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new HttpException("User not found", 404);
|
throw new HttpException('User not found', 404);
|
||||||
}
|
}
|
||||||
return Promise.resolve(user);
|
return Promise.resolve(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
addUser(user) {
|
public addUser(user) {
|
||||||
this.users.push(user);
|
this.users.push(user);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nest.js",
|
"name": "nest.js",
|
||||||
"version": "1.0.0-rc4",
|
"version": "1.0.0",
|
||||||
"description": "Modern, fast, powerful node.js web framework",
|
"description": "Modern, fast, powerful node.js web framework",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -18,7 +18,13 @@
|
|||||||
"src/**/*.ts"
|
"src/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules/"
|
"node_modules/",
|
||||||
|
"src/*.ts",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/common/services/logger.service.ts",
|
||||||
|
"src/microservices/exceptions/",
|
||||||
|
"src/core/adapters/*.ts",
|
||||||
|
"src/websockets/adapters/*.ts"
|
||||||
],
|
],
|
||||||
"extension": [
|
"extension": [
|
||||||
".ts"
|
".ts"
|
||||||
@@ -47,10 +53,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^3.4.34",
|
"@types/chai": "^3.4.34",
|
||||||
|
"@types/express": "^4.0.35",
|
||||||
"@types/mocha": "^2.2.38",
|
"@types/mocha": "^2.2.38",
|
||||||
"@types/node": "^7.0.5",
|
"@types/node": "^7.0.5",
|
||||||
"@types/redis": "^0.12.36",
|
"@types/redis": "^0.12.36",
|
||||||
"@types/sinon": "^1.16.36",
|
"@types/sinon": "^1.16.36",
|
||||||
|
"@types/socket.io": "^1.4.29",
|
||||||
"awesome-typescript-loader": "^3.0.0-beta.18",
|
"awesome-typescript-loader": "^3.0.0-beta.18",
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"concurrently": "^3.4.0",
|
"concurrently": "^3.4.0",
|
||||||
|
|||||||
@@ -7,5 +7,9 @@ export const metadata = {
|
|||||||
|
|
||||||
export const PATH_METADATA = 'path';
|
export const PATH_METADATA = 'path';
|
||||||
export const PARAMTYPES_METADATA = 'design:paramtypes';
|
export const PARAMTYPES_METADATA = 'design:paramtypes';
|
||||||
export const SELF_PARAMS_METADATA = 'self:paramtypes';
|
export const SELF_DECLARED_DEPS_METADATA = 'self:paramtypes';
|
||||||
export const METHOD_METADATA = 'method';
|
export const METHOD_METADATA = 'method';
|
||||||
|
|
||||||
|
export const ROUTE_ARGS_METADATA = '__routeArguments__';
|
||||||
|
export const EXCEPTION_FILTERS_METADATA = '__exceptionFilters__';
|
||||||
|
export const FILTER_CATCH_EXCEPTIONS = '__filterCatchExceptions__';
|
||||||
45
src/common/enums/http-status.enum.ts
Normal file
45
src/common/enums/http-status.enum.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
export enum HttpStatus {
|
||||||
|
CONTINUE = 100,
|
||||||
|
SWITCHING_PROTOCOLS = 101,
|
||||||
|
PROCESSING = 102,
|
||||||
|
OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
ACCEPTED = 202,
|
||||||
|
NON_AUTHORITATIVE_INFORMATION = 203,
|
||||||
|
NO_CONTENT = 204,
|
||||||
|
RESET_CONTENT = 205,
|
||||||
|
PARTIAL_CONTENT = 206,
|
||||||
|
AMBIGUOUS = 300,
|
||||||
|
MOVED_PERMANENTLY = 301,
|
||||||
|
FOUND = 302,
|
||||||
|
SEE_OTHER = 303,
|
||||||
|
NOT_MODIFIED = 304,
|
||||||
|
TEMPORARY_REDIRECT = 307,
|
||||||
|
PERMANENT_REDIRECT = 308,
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
PAYMENT_REQUIRED = 402,
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
METHOD_NOT_ALLOWED = 405,
|
||||||
|
NOT_ACCEPTABLE = 406,
|
||||||
|
PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||||
|
REQUEST_TIMEOUT = 408,
|
||||||
|
CONFLICT = 409,
|
||||||
|
GONE = 410,
|
||||||
|
LENGTH_REQUIRED = 411,
|
||||||
|
PRECONDITION_FAILED = 412,
|
||||||
|
PAYLOAD_TOO_LARGE = 413,
|
||||||
|
URI_TOO_LONG = 414,
|
||||||
|
UNSUPPORTED_MEDIA_TYPE = 415,
|
||||||
|
REQUESTED_RANGE_NOT_SATISFIABLE = 416,
|
||||||
|
EXPECTATION_FAILED = 417,
|
||||||
|
UNPROCESSABLE_ENTITY = 422,
|
||||||
|
TOO_MANY_REQUESTS = 429,
|
||||||
|
INTERNAL_SERVER_ERROR = 500,
|
||||||
|
NOT_IMPLEMENTED = 501,
|
||||||
|
BAD_GATEWAY = 502,
|
||||||
|
SERVICE_UNAVAILABLE = 503,
|
||||||
|
GATEWAY_TIMEOUT = 504,
|
||||||
|
HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './request-method.enum';
|
export * from './request-method.enum';
|
||||||
export * from './transport.enum';
|
export * from './transport.enum';
|
||||||
|
export * from './http-status.enum';
|
||||||
4
src/common/enums/nest-environment.enum.ts
Normal file
4
src/common/enums/nest-environment.enum.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum NestEnvironment {
|
||||||
|
RUN,
|
||||||
|
TEST,
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum NestMode {
|
|
||||||
RUN,
|
|
||||||
TEST
|
|
||||||
}
|
|
||||||
@@ -3,5 +3,5 @@ export enum RequestMethod {
|
|||||||
POST,
|
POST,
|
||||||
PUT,
|
PUT,
|
||||||
DELETE,
|
DELETE,
|
||||||
ALL
|
ALL,
|
||||||
}
|
}
|
||||||
10
src/common/enums/route-paramtypes.enum.ts
Normal file
10
src/common/enums/route-paramtypes.enum.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export enum RouteParamtypes {
|
||||||
|
REQUEST,
|
||||||
|
RESPONSE,
|
||||||
|
NEXT,
|
||||||
|
BODY,
|
||||||
|
QUERY,
|
||||||
|
PARAM,
|
||||||
|
HEADERS,
|
||||||
|
SESSION,
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export enum Transport {
|
export enum Transport {
|
||||||
TCP,
|
TCP,
|
||||||
REDIS
|
REDIS,
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
export * from './utils';
|
export * from './utils';
|
||||||
export * from './enums';
|
export * from './enums';
|
||||||
export { NestModule } from './interfaces';
|
export {
|
||||||
|
NestModule,
|
||||||
|
INestApplication,
|
||||||
|
INestMicroservice,
|
||||||
|
MiddlewareConfigProxy,
|
||||||
|
MiddlewaresConsumer,
|
||||||
|
OnModuleInit,
|
||||||
|
} from './interfaces';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { ExceptionFilter } from './exception-filter.interface';
|
||||||
|
import { Metatype } from './metatype.interface';
|
||||||
|
|
||||||
|
export interface ExceptionFilterMetadata {
|
||||||
|
func: ExceptionFilter['catch'];
|
||||||
|
exceptionMetatypes: Metatype<any>[];
|
||||||
|
}
|
||||||
3
src/common/interfaces/exception-filter.interface.ts
Normal file
3
src/common/interfaces/exception-filter.interface.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface ExceptionFilter {
|
||||||
|
catch(exception, response);
|
||||||
|
}
|
||||||
@@ -5,4 +5,9 @@ export * from './controller.interface';
|
|||||||
export * from './injectable.interface';
|
export * from './injectable.interface';
|
||||||
export * from './controller-metadata.interface';
|
export * from './controller-metadata.interface';
|
||||||
export * from './module-metadata.interface';
|
export * from './module-metadata.interface';
|
||||||
export * from './metatype.interface';
|
export * from './metatype.interface';
|
||||||
|
export * from './nest-application.interface';
|
||||||
|
export * from './nest-microservice.interface';
|
||||||
|
export * from './middlewares-consumer.interface';
|
||||||
|
export * from './on-init.interface';
|
||||||
|
export * from './middleware-config-proxy.interface';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { MiddlewaresConsumer } from './middlewares-consumer.interface';
|
||||||
|
import { RequestMappingMetadata } from './request-mapping-metadata.interface';
|
||||||
|
|
||||||
|
export interface MiddlewareConfigProxy {
|
||||||
|
with: (...data) => MiddlewareConfigProxy;
|
||||||
|
forRoutes: (...routes) => MiddlewaresConsumer;
|
||||||
|
}
|
||||||
6
src/common/interfaces/middlewares-consumer.interface.ts
Normal file
6
src/common/interfaces/middlewares-consumer.interface.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { Metatype } from './metatype.interface';
|
||||||
|
import { MiddlewareConfigProxy } from './middleware-config-proxy.interface';
|
||||||
|
|
||||||
|
export interface MiddlewaresConsumer {
|
||||||
|
apply(metatypes: Metatype<any> | Array<Metatype<any>>): MiddlewareConfigProxy;
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@ import { NestModule } from './nest-module.interface';
|
|||||||
import { Controller } from './controller.interface';
|
import { Controller } from './controller.interface';
|
||||||
|
|
||||||
export interface ModuleMetadata {
|
export interface ModuleMetadata {
|
||||||
modules?: NestModule[],
|
modules?: NestModule[];
|
||||||
components?: any[],
|
components?: any[];
|
||||||
controllers?: Controller[] | any[],
|
controllers?: Controller[] | any[];
|
||||||
exports?: any[],
|
exports?: any[];
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/common/interfaces/nest-application.interface.ts
Normal file
3
src/common/interfaces/nest-application.interface.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface INestApplication {
|
||||||
|
listen(port: number, callback?: () => void);
|
||||||
|
}
|
||||||
3
src/common/interfaces/nest-microservice.interface.ts
Normal file
3
src/common/interfaces/nest-microservice.interface.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface INestMicroservice {
|
||||||
|
listen(callback: () => void);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { MiddlewareBuilder } from '../../core/middlewares/builder';
|
import { MiddlewareBuilder } from '../../core/middlewares/builder';
|
||||||
|
import { MiddlewaresConsumer } from './middlewares-consumer.interface';
|
||||||
|
|
||||||
export interface NestModule {
|
export interface NestModule {
|
||||||
configure?: (router: MiddlewareBuilder) => MiddlewareBuilder;
|
configure?: (consumer: MiddlewaresConsumer) => MiddlewaresConsumer | void;
|
||||||
}
|
}
|
||||||
3
src/common/interfaces/on-init.interface.ts
Normal file
3
src/common/interfaces/on-init.interface.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface OnModuleInit {
|
||||||
|
onModuleInit();
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { RequestMethod } from '../enums/request-method.enum';
|
import { RequestMethod } from '../enums/request-method.enum';
|
||||||
|
|
||||||
export interface RequestMappingMetadata {
|
export interface RequestMappingMetadata {
|
||||||
path?: string,
|
path?: string;
|
||||||
method?: RequestMethod
|
method?: RequestMethod;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { NestMode } from '../enums/nest-mode.enum';
|
import { NestEnvironment } from '../enums/nest-environment.enum';
|
||||||
|
|
||||||
declare var process;
|
declare const process;
|
||||||
import * as clc from 'cli-color';
|
import * as clc from 'cli-color';
|
||||||
|
|
||||||
export class Logger {
|
export class Logger {
|
||||||
private static mode = NestMode.RUN;
|
private static mode = NestEnvironment.RUN;
|
||||||
private readonly yellow = clc.xterm(3);
|
private readonly yellow = clc.xterm(3);
|
||||||
|
|
||||||
constructor(private context: string) {}
|
constructor(private context: string) {}
|
||||||
|
|
||||||
static setMode(mode: NestMode) {
|
public static setMode(mode: NestEnvironment) {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(message: string) {
|
public log(message: string) {
|
||||||
this.logMessage(message, clc.green);
|
this.logMessage(message, clc.green);
|
||||||
}
|
}
|
||||||
|
|
||||||
error(message: string, trace = '') {
|
public error(message: string, trace = '') {
|
||||||
this.logMessage(message, clc.red);
|
this.logMessage(message, clc.red);
|
||||||
this.printStackTrace(trace);
|
this.printStackTrace(trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
warn(message: string) {
|
public warn(message: string) {
|
||||||
this.logMessage(message, clc.yellow);
|
this.logMessage(message, clc.yellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
private logMessage(message: string, color: Function) {
|
private logMessage(message: string, color: (msg: string) => string) {
|
||||||
if (Logger.mode === NestMode.TEST) { return; }
|
if (Logger.mode === NestEnvironment.TEST) return;
|
||||||
|
|
||||||
process.stdout.write(color(`[Nest] ${process.pid} - `));
|
process.stdout.write(color(`[Nest] ${process.pid} - `));
|
||||||
process.stdout.write(`${new Date(Date.now()).toLocaleString()} `);
|
process.stdout.write(`${new Date(Date.now()).toLocaleString()} `);
|
||||||
@@ -37,7 +37,7 @@ export class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private printStackTrace(trace: string) {
|
private printStackTrace(trace: string) {
|
||||||
if (Logger.mode === NestMode.TEST) { return; }
|
if (Logger.mode === NestEnvironment.TEST) return;
|
||||||
|
|
||||||
process.stdout.write(trace);
|
process.stdout.write(trace);
|
||||||
process.stdout.write(`\n`);
|
process.stdout.write(`\n`);
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import { BindResolveMiddlewareValues } from '../../utils/bind-resolve-values.uti
|
|||||||
import { NestMiddleware } from '../../../core/middlewares/interfaces/nest-middleware.interface';
|
import { NestMiddleware } from '../../../core/middlewares/interfaces/nest-middleware.interface';
|
||||||
|
|
||||||
describe('BindResolveMiddlewareValues', () => {
|
describe('BindResolveMiddlewareValues', () => {
|
||||||
let type,
|
let type;
|
||||||
arg1 = 3,
|
const arg1 = 3,
|
||||||
arg2 = 4;
|
arg2 = 4;
|
||||||
|
|
||||||
class Test implements NestMiddleware {
|
class Test implements NestMiddleware {
|
||||||
resolve(a, b) {
|
public resolve(a, b) {
|
||||||
return () => [a, b];
|
return () => [a, b];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,6 @@ describe('BindResolveMiddlewareValues', () => {
|
|||||||
expect(hof()).to.deep.equal([arg1, arg2]);
|
expect(hof()).to.deep.equal([arg1, arg2]);
|
||||||
});
|
});
|
||||||
it('should set name of metatype', () => {
|
it('should set name of metatype', () => {
|
||||||
expect(type.name).to.eq((<any>Test).name + JSON.stringify([ arg1, arg2 ]));
|
expect(type.name).to.eq((Test as any).name + JSON.stringify([ arg1, arg2 ]));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
16
src/common/test/utils/catch.decorator.spec.ts
Normal file
16
src/common/test/utils/catch.decorator.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { Catch } from '../../utils/decorators/catch.decorator';
|
||||||
|
import { FILTER_CATCH_EXCEPTIONS } from '../../constants';
|
||||||
|
|
||||||
|
describe('@Catch', () => {
|
||||||
|
const exceptions = [ 'exception', 'exception2' ];
|
||||||
|
|
||||||
|
@Catch(...exceptions) class Test {}
|
||||||
|
|
||||||
|
it('should enhance class with expected exceptions array', () => {
|
||||||
|
const metadata = Reflect.getMetadata(FILTER_CATCH_EXCEPTIONS, Test);
|
||||||
|
expect(metadata).to.be.eql(exceptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { Component } from '../../utils/component.decorator';
|
import { Component } from '../../utils/decorators/component.decorator';
|
||||||
|
|
||||||
describe('@Component', () => {
|
describe('@Component', () => {
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ describe('@Component', () => {
|
|||||||
test: string) {}
|
test: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should enhance transport with "design:paramtypes" metadata', () => {
|
it('should enhance component with "design:paramtypes" metadata', () => {
|
||||||
const constructorParams = Reflect.getMetadata('design:paramtypes', TestComponent);
|
const constructorParams = Reflect.getMetadata('design:paramtypes', TestComponent);
|
||||||
|
|
||||||
expect(constructorParams[0]).to.be.eql(Number);
|
expect(constructorParams[0]).to.be.eql(Number);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { Controller } from '../../utils/controller.decorator';
|
import { Controller } from '../../utils/decorators/controller.decorator';
|
||||||
|
|
||||||
describe('@Controller', () => {
|
describe('@Controller', () => {
|
||||||
const props = {
|
const props = {
|
||||||
@@ -11,7 +11,7 @@ describe('@Controller', () => {
|
|||||||
@Controller() class EmptyDecorator {}
|
@Controller() class EmptyDecorator {}
|
||||||
@Controller({}) class AnotherTest {}
|
@Controller({}) class AnotherTest {}
|
||||||
|
|
||||||
it('should enhance transport with expected path metadata', () => {
|
it('should enhance controller with expected path metadata', () => {
|
||||||
const path = Reflect.getMetadata('path', Test);
|
const path = Reflect.getMetadata('path', Test);
|
||||||
expect(path).to.be.eql(props.path);
|
expect(path).to.be.eql(props.path);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { Dependencies } from '../../utils/dependencies.decorator';
|
import { Dependencies } from '../../utils/decorators/dependencies.decorator';
|
||||||
import { PARAMTYPES_METADATA } from '../../constants';
|
import { PARAMTYPES_METADATA } from '../../constants';
|
||||||
|
|
||||||
describe('@Dependencies', () => {
|
describe('@Dependencies', () => {
|
||||||
const deps = [ 'test', 'test2' ];
|
const dep = 'test', dep2 = 'test2';
|
||||||
|
const deps = [ dep, dep2 ];
|
||||||
|
|
||||||
@Dependencies(deps) class Test {}
|
@Dependencies(deps) class Test {}
|
||||||
|
@Dependencies(dep, dep2) class Test2 {}
|
||||||
|
|
||||||
it('should enhance class with expected dependencies array', () => {
|
it('should enhance class with expected dependencies array', () => {
|
||||||
const metadata = Reflect.getMetadata(PARAMTYPES_METADATA, Test);
|
const metadata = Reflect.getMetadata(PARAMTYPES_METADATA, Test);
|
||||||
expect(metadata).to.be.eql(deps);
|
expect(metadata).to.be.eql(deps);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should makes passed array flatten', () => {
|
||||||
|
const metadata = Reflect.getMetadata(PARAMTYPES_METADATA, Test2);
|
||||||
|
expect(metadata).to.be.eql([dep, dep2]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
16
src/common/test/utils/exception-filters.decorator.spec.ts
Normal file
16
src/common/test/utils/exception-filters.decorator.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { EXCEPTION_FILTERS_METADATA } from '../../constants';
|
||||||
|
import { ExceptionFilters } from '../../utils/decorators/exception-filters.decorator';
|
||||||
|
|
||||||
|
describe('@ExceptionFilters', () => {
|
||||||
|
const filters = [ 'exception', 'exception2' ];
|
||||||
|
|
||||||
|
@ExceptionFilters(...filters) class Test {}
|
||||||
|
|
||||||
|
it('should enhance class with expected exception filters array', () => {
|
||||||
|
const metadata = Reflect.getMetadata(EXCEPTION_FILTERS_METADATA, Test);
|
||||||
|
expect(metadata).to.be.eql(filters);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
26
src/common/test/utils/inject.decorator.spec.ts
Normal file
26
src/common/test/utils/inject.decorator.spec.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { SELF_DECLARED_DEPS_METADATA } from '../../constants';
|
||||||
|
import { Inject } from '../../index';
|
||||||
|
|
||||||
|
describe('@Inject', () => {
|
||||||
|
const opaqueToken = () => ({});
|
||||||
|
class Test {
|
||||||
|
constructor(
|
||||||
|
@Inject('test') param,
|
||||||
|
@Inject('test2') param2,
|
||||||
|
@Inject(opaqueToken) param3) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should enhance class with expected constructor params metadata', () => {
|
||||||
|
const metadata = Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, Test);
|
||||||
|
|
||||||
|
const expectedMetadata = [
|
||||||
|
{ index: 2, param: opaqueToken.name },
|
||||||
|
{ index: 1, param: 'test2' },
|
||||||
|
{ index: 0, param: 'test' },
|
||||||
|
];
|
||||||
|
expect(metadata).to.be.eql(expectedMetadata);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -3,7 +3,8 @@ import { expect } from 'chai';
|
|||||||
import { MergeWithValues } from '../../utils/merge-with-values.util';
|
import { MergeWithValues } from '../../utils/merge-with-values.util';
|
||||||
|
|
||||||
describe('MergeWithValues', () => {
|
describe('MergeWithValues', () => {
|
||||||
let type, data = { test: [ 1, 2, 3 ] };
|
let type;
|
||||||
|
const data = { test: [ 1, 2, 3 ] };
|
||||||
class Test {}
|
class Test {}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { Module } from '../../utils/module.decorator';
|
import { Module } from '../../utils/decorators/module.decorator';
|
||||||
import { InvalidModuleConfigException } from '../../../errors/exceptions/invalid-module-config.exception';
|
import { InvalidModuleConfigException } from '../../../errors/exceptions/invalid-module-config.exception';
|
||||||
|
|
||||||
describe('@Module', () => {
|
describe('@Module', () => {
|
||||||
@@ -14,7 +14,7 @@ describe('@Module', () => {
|
|||||||
@Module(moduleProps)
|
@Module(moduleProps)
|
||||||
class TestModule {}
|
class TestModule {}
|
||||||
|
|
||||||
it('should enhance transport with expected module metadata', () => {
|
it('should enhance class with expected module metadata', () => {
|
||||||
const modules = Reflect.getMetadata('modules', TestModule);
|
const modules = Reflect.getMetadata('modules', TestModule);
|
||||||
const components = Reflect.getMetadata('components', TestModule);
|
const components = Reflect.getMetadata('components', TestModule);
|
||||||
const exports = Reflect.getMetadata('exports', TestModule);
|
const exports = Reflect.getMetadata('exports', TestModule);
|
||||||
@@ -29,7 +29,7 @@ describe('@Module', () => {
|
|||||||
it('should throw exception when module properties are invalid', () => {
|
it('should throw exception when module properties are invalid', () => {
|
||||||
const invalidProps = {
|
const invalidProps = {
|
||||||
...moduleProps,
|
...moduleProps,
|
||||||
test: []
|
test: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(Module.bind(null, invalidProps)).to.throw(InvalidModuleConfigException);
|
expect(Module.bind(null, invalidProps)).to.throw(InvalidModuleConfigException);
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { RequestMapping } from '../../utils/request-mapping.decorator';
|
import { RequestMapping } from '../../utils/decorators/request-mapping.decorator';
|
||||||
import { RequestMethod } from '../../enums/request-method.enum';
|
import { RequestMethod } from '../../enums/request-method.enum';
|
||||||
|
|
||||||
describe('@RequestMapping', () => {
|
describe('@RequestMapping', () => {
|
||||||
const requestProps = {
|
const requestProps = {
|
||||||
path: 'test',
|
path: 'test',
|
||||||
method: RequestMethod.ALL
|
method: RequestMethod.ALL,
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should enhance transport with expected request metadata', () => {
|
it('should enhance class with expected request metadata', () => {
|
||||||
class Test {
|
class Test {
|
||||||
@RequestMapping(requestProps)
|
@RequestMapping(requestProps)
|
||||||
static test() {}
|
public static test() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = Reflect.getMetadata('path', Test.test);
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
@@ -25,7 +25,7 @@ describe('@RequestMapping', () => {
|
|||||||
it('should set request method on GET by default', () => {
|
it('should set request method on GET by default', () => {
|
||||||
class Test {
|
class Test {
|
||||||
@RequestMapping({ path: '' })
|
@RequestMapping({ path: '' })
|
||||||
static test() {}
|
public static test() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const method = Reflect.getMetadata('method', Test.test);
|
const method = Reflect.getMetadata('method', Test.test);
|
||||||
@@ -35,7 +35,7 @@ describe('@RequestMapping', () => {
|
|||||||
it('should set path on "/" by default', () => {
|
it('should set path on "/" by default', () => {
|
||||||
class Test {
|
class Test {
|
||||||
@RequestMapping({})
|
@RequestMapping({})
|
||||||
static test() {}
|
public static test() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const method = Reflect.getMetadata('path', Test.test);
|
const method = Reflect.getMetadata('path', Test.test);
|
||||||
|
|||||||
159
src/common/test/utils/route-params.decorator.spec.ts
Normal file
159
src/common/test/utils/route-params.decorator.spec.ts
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { RequestMethod } from '../../enums/request-method.enum';
|
||||||
|
import { Get, Post, Delete, All, Put } from '../../index';
|
||||||
|
|
||||||
|
describe('@Get', () => {
|
||||||
|
const requestPath = 'test';
|
||||||
|
const requestProps = {
|
||||||
|
path: requestPath,
|
||||||
|
method: RequestMethod.GET,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should enhance class with expected request metadata', () => {
|
||||||
|
class Test {
|
||||||
|
@Get(requestPath)
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
const method = Reflect.getMetadata('method', Test.test);
|
||||||
|
|
||||||
|
expect(method).to.be.eql(requestProps.method);
|
||||||
|
expect(path).to.be.eql(requestPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set path on "/" by default', () => {
|
||||||
|
class Test {
|
||||||
|
@Get()
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
expect(path).to.be.eql('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@Post', () => {
|
||||||
|
const requestPath = 'test';
|
||||||
|
const requestProps = {
|
||||||
|
path: requestPath,
|
||||||
|
method: RequestMethod.POST,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should enhance class with expected request metadata', () => {
|
||||||
|
class Test {
|
||||||
|
@Post(requestPath)
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
const method = Reflect.getMetadata('method', Test.test);
|
||||||
|
|
||||||
|
expect(method).to.be.eql(requestProps.method);
|
||||||
|
expect(path).to.be.eql(requestPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set path on "/" by default', () => {
|
||||||
|
class Test {
|
||||||
|
@Post()
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
expect(path).to.be.eql('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@Delete', () => {
|
||||||
|
const requestPath = 'test';
|
||||||
|
const requestProps = {
|
||||||
|
path: requestPath,
|
||||||
|
method: RequestMethod.DELETE,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should enhance class with expected request metadata', () => {
|
||||||
|
class Test {
|
||||||
|
@Delete(requestPath)
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
const method = Reflect.getMetadata('method', Test.test);
|
||||||
|
|
||||||
|
expect(method).to.be.eql(requestProps.method);
|
||||||
|
expect(path).to.be.eql(requestPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set path on "/" by default', () => {
|
||||||
|
class Test {
|
||||||
|
@Delete()
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
expect(path).to.be.eql('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@All', () => {
|
||||||
|
const requestPath = 'test';
|
||||||
|
const requestProps = {
|
||||||
|
path: requestPath,
|
||||||
|
method: RequestMethod.ALL,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should enhance class with expected request metadata', () => {
|
||||||
|
class Test {
|
||||||
|
@All(requestPath)
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
const method = Reflect.getMetadata('method', Test.test);
|
||||||
|
|
||||||
|
expect(method).to.be.eql(requestProps.method);
|
||||||
|
expect(path).to.be.eql(requestPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set path on "/" by default', () => {
|
||||||
|
class Test {
|
||||||
|
@All()
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
expect(path).to.be.eql('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@Put', () => {
|
||||||
|
const requestPath = 'test';
|
||||||
|
const requestProps = {
|
||||||
|
path: requestPath,
|
||||||
|
method: RequestMethod.PUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should enhance class with expected request metadata', () => {
|
||||||
|
class Test {
|
||||||
|
@Put(requestPath)
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
const method = Reflect.getMetadata('method', Test.test);
|
||||||
|
|
||||||
|
expect(method).to.be.eql(requestProps.method);
|
||||||
|
expect(path).to.be.eql(requestPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set path on "/" by default', () => {
|
||||||
|
class Test {
|
||||||
|
@Put()
|
||||||
|
public static test() {}
|
||||||
|
}
|
||||||
|
const path = Reflect.getMetadata('path', Test.test);
|
||||||
|
expect(path).to.be.eql('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -2,14 +2,14 @@ import { Constructor } from './merge-with-values.util';
|
|||||||
import { NestMiddleware } from '../../core/middlewares/interfaces/nest-middleware.interface';
|
import { NestMiddleware } from '../../core/middlewares/interfaces/nest-middleware.interface';
|
||||||
|
|
||||||
export const BindResolveMiddlewareValues = <T extends Constructor<NestMiddleware>>(data: Array<any>) => {
|
export const BindResolveMiddlewareValues = <T extends Constructor<NestMiddleware>>(data: Array<any>) => {
|
||||||
return (metatype: T): any => {
|
return (Metatype: T): any => {
|
||||||
const type = class extends metatype {
|
const type = class extends Metatype {
|
||||||
resolve() {
|
public resolve() {
|
||||||
return super.resolve(...data);
|
return super.resolve(...data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const token = metatype.name + JSON.stringify(data);
|
const token = Metatype.name + JSON.stringify(data);
|
||||||
Object.defineProperty(type, 'name', { value: token });
|
Object.defineProperty(type, 'name', { value: token });
|
||||||
return type;
|
return type;
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import 'reflect-metadata';
|
|
||||||
import { ControllerMetadata } from '../interfaces/controller-metadata.interface';
|
|
||||||
import { isUndefined } from './shared.utils';
|
|
||||||
import { PATH_METADATA } from '../constants';
|
|
||||||
|
|
||||||
const defaultMetadata = { [PATH_METADATA]: '/' };
|
|
||||||
|
|
||||||
export const Controller = (metadata: ControllerMetadata = defaultMetadata): ClassDecorator => {
|
|
||||||
if (isUndefined(metadata[PATH_METADATA])) {
|
|
||||||
metadata[PATH_METADATA] = '/';
|
|
||||||
}
|
|
||||||
return (target: Object) => {
|
|
||||||
Reflect.defineMetadata(PATH_METADATA, metadata[PATH_METADATA], target);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
8
src/common/utils/decorators/catch.decorator.ts
Normal file
8
src/common/utils/decorators/catch.decorator.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { FILTER_CATCH_EXCEPTIONS } from '../../constants';
|
||||||
|
|
||||||
|
export const Catch = (...exceptions): ClassDecorator => {
|
||||||
|
return (target: object) => {
|
||||||
|
Reflect.defineMetadata(FILTER_CATCH_EXCEPTIONS, exceptions, target);
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export const Component = (): ClassDecorator => {
|
export const Component = (): ClassDecorator => {
|
||||||
return (target: Object) => {}
|
return (target: object) => {};
|
||||||
};
|
};
|
||||||
13
src/common/utils/decorators/controller.decorator.ts
Normal file
13
src/common/utils/decorators/controller.decorator.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { ControllerMetadata } from '../../interfaces/controller-metadata.interface';
|
||||||
|
import { isUndefined, isObject } from '../shared.utils';
|
||||||
|
import { PATH_METADATA } from '../../constants';
|
||||||
|
|
||||||
|
export const Controller = (metadata?: ControllerMetadata | string): ClassDecorator => {
|
||||||
|
let path = isObject(metadata) ? metadata[PATH_METADATA] : metadata;
|
||||||
|
path = isUndefined(path) ? '/' : path;
|
||||||
|
|
||||||
|
return (target: object) => {
|
||||||
|
Reflect.defineMetadata(PATH_METADATA, path, target);
|
||||||
|
};
|
||||||
|
};
|
||||||
14
src/common/utils/decorators/dependencies.decorator.ts
Normal file
14
src/common/utils/decorators/dependencies.decorator.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { PARAMTYPES_METADATA } from '../../constants';
|
||||||
|
|
||||||
|
const flatten = (arr) => {
|
||||||
|
const flat = [].concat(...arr);
|
||||||
|
return flat.some(Array.isArray) ? flatten(flat) : flat;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Dependencies = (...metadata): ClassDecorator => {
|
||||||
|
const flattenDeps = flatten(metadata);
|
||||||
|
return (target: object) => {
|
||||||
|
Reflect.defineMetadata(PARAMTYPES_METADATA, flattenDeps, target);
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { EXCEPTION_FILTERS_METADATA } from '../../constants';
|
||||||
|
|
||||||
|
export const ExceptionFilters = (...filters): ClassDecorator => {
|
||||||
|
return (target: object) => {
|
||||||
|
Reflect.defineMetadata(EXCEPTION_FILTERS_METADATA, filters, target);
|
||||||
|
};
|
||||||
|
};
|
||||||
13
src/common/utils/decorators/inject.decorator.ts
Normal file
13
src/common/utils/decorators/inject.decorator.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { SELF_DECLARED_DEPS_METADATA } from '../../constants';
|
||||||
|
import { isFunction } from '../shared.utils';
|
||||||
|
|
||||||
|
export const Inject = (param): ParameterDecorator => {
|
||||||
|
return (target, key, index) => {
|
||||||
|
const args = Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, target) || [];
|
||||||
|
const type = isFunction(param) ? param.name : param;
|
||||||
|
|
||||||
|
args.push({ index, param: type });
|
||||||
|
Reflect.defineMetadata(SELF_DECLARED_DEPS_METADATA, args, target);
|
||||||
|
};
|
||||||
|
};
|
||||||
34
src/common/utils/decorators/module.decorator.ts
Normal file
34
src/common/utils/decorators/module.decorator.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { ModuleMetadata } from '../../interfaces/module-metadata.interface';
|
||||||
|
import { InvalidModuleConfigException } from '../../../errors/exceptions/invalid-module-config.exception';
|
||||||
|
import { metadata } from '../../constants';
|
||||||
|
|
||||||
|
const metadataKeys = [
|
||||||
|
metadata.MODULES,
|
||||||
|
metadata.EXPORTS,
|
||||||
|
metadata.COMPONENTS,
|
||||||
|
metadata.CONTROLLERS,
|
||||||
|
];
|
||||||
|
|
||||||
|
const validateKeys = (keys: string[]) => {
|
||||||
|
const isKeyValid = (key) => metadataKeys.findIndex(k => k === key) < 0;
|
||||||
|
const validateKey = (key) => {
|
||||||
|
if (isKeyValid(key)) {
|
||||||
|
throw new InvalidModuleConfigException(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
keys.forEach(validateKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Module = (props: ModuleMetadata): ClassDecorator => {
|
||||||
|
const propsKeys = Object.keys(props);
|
||||||
|
validateKeys(propsKeys);
|
||||||
|
|
||||||
|
return (target: object) => {
|
||||||
|
for (const property in props) {
|
||||||
|
if (props.hasOwnProperty(property)) {
|
||||||
|
Reflect.defineMetadata(property, props[property], target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { RequestMappingMetadata } from '../interfaces/request-mapping-metadata.interface';
|
import { RequestMappingMetadata } from '../../interfaces/request-mapping-metadata.interface';
|
||||||
import { RequestMethod } from '../enums/request-method.enum';
|
import { RequestMethod } from '../../enums/request-method.enum';
|
||||||
import { PATH_METADATA, METHOD_METADATA } from '../constants';
|
import { PATH_METADATA, METHOD_METADATA } from '../../constants';
|
||||||
|
|
||||||
const defaultMetadata = {
|
const defaultMetadata = {
|
||||||
[PATH_METADATA]: '/',
|
[PATH_METADATA]: '/',
|
||||||
[METHOD_METADATA]: RequestMethod.GET
|
[METHOD_METADATA]: RequestMethod.GET,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RequestMapping = (metadata: RequestMappingMetadata = defaultMetadata): MethodDecorator => {
|
export const RequestMapping = (metadata: RequestMappingMetadata = defaultMetadata): MethodDecorator => {
|
||||||
@@ -16,13 +16,13 @@ export const RequestMapping = (metadata: RequestMappingMetadata = defaultMetadat
|
|||||||
Reflect.defineMetadata(PATH_METADATA, path, descriptor.value);
|
Reflect.defineMetadata(PATH_METADATA, path, descriptor.value);
|
||||||
Reflect.defineMetadata(METHOD_METADATA, requestMethod, descriptor.value);
|
Reflect.defineMetadata(METHOD_METADATA, requestMethod, descriptor.value);
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const createMappingDecorator = (method: RequestMethod) => (path?: string): MethodDecorator => {
|
const createMappingDecorator = (method: RequestMethod) => (path?: string): MethodDecorator => {
|
||||||
return RequestMapping({
|
return RequestMapping({
|
||||||
[PATH_METADATA]: path,
|
[PATH_METADATA]: path,
|
||||||
[METHOD_METADATA]: method
|
[METHOD_METADATA]: method,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
41
src/common/utils/decorators/route-params.decorator.ts
Normal file
41
src/common/utils/decorators/route-params.decorator.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { ROUTE_ARGS_METADATA } from '../../constants';
|
||||||
|
import { RouteParamtypes } from '../../enums/route-paramtypes.enum';
|
||||||
|
|
||||||
|
export type ParamData = object | string | number;
|
||||||
|
export interface RouteParamsMetadata {
|
||||||
|
[prop: number]: {
|
||||||
|
index: number;
|
||||||
|
data?: ParamData;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignMetadata = (
|
||||||
|
args: RouteParamsMetadata,
|
||||||
|
paramtype: RouteParamtypes,
|
||||||
|
index: number,
|
||||||
|
data?: ParamData) => ({
|
||||||
|
...args,
|
||||||
|
[paramtype]: { index, data },
|
||||||
|
});
|
||||||
|
|
||||||
|
const createRouteParamDecorator = (paramtype: RouteParamtypes) => {
|
||||||
|
return (data?: ParamData): ParameterDecorator => (target, key, index) => {
|
||||||
|
const args = Reflect.getMetadata(ROUTE_ARGS_METADATA, target, key) || {};
|
||||||
|
Reflect.defineMetadata(
|
||||||
|
ROUTE_ARGS_METADATA,
|
||||||
|
assignMetadata(args, paramtype, index, data),
|
||||||
|
target,
|
||||||
|
key,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Request: () => ParameterDecorator = createRouteParamDecorator(RouteParamtypes.REQUEST);
|
||||||
|
export const Response: () => ParameterDecorator = createRouteParamDecorator(RouteParamtypes.RESPONSE);
|
||||||
|
export const Next: () => ParameterDecorator = createRouteParamDecorator(RouteParamtypes.NEXT);
|
||||||
|
export const Query: (property?: string) => ParameterDecorator = createRouteParamDecorator(RouteParamtypes.QUERY);
|
||||||
|
export const Body: (property?: string) => ParameterDecorator = createRouteParamDecorator(RouteParamtypes.BODY);
|
||||||
|
export const Param: (property?: string) => ParameterDecorator = createRouteParamDecorator(RouteParamtypes.PARAM);
|
||||||
|
export const Session: () => ParameterDecorator = createRouteParamDecorator(RouteParamtypes.SESSION);
|
||||||
|
export const Headers: (property?: string) => ParameterDecorator = createRouteParamDecorator(RouteParamtypes.HEADERS);
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import 'reflect-metadata';
|
|
||||||
import { PARAMTYPES_METADATA } from '../constants';
|
|
||||||
|
|
||||||
export const Dependencies = (metadata: any[]): ClassDecorator => {
|
|
||||||
return (target: Object) => {
|
|
||||||
Reflect.defineMetadata(PARAMTYPES_METADATA, metadata, target);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
export * from './request-mapping.decorator';
|
export * from './decorators/request-mapping.decorator';
|
||||||
export * from './controller.decorator';
|
export * from './decorators/controller.decorator';
|
||||||
export * from './component.decorator';
|
export * from './decorators/component.decorator';
|
||||||
export * from './module.decorator';
|
export * from './decorators/module.decorator';
|
||||||
export * from './dependencies.decorator';
|
export * from './decorators/dependencies.decorator';
|
||||||
export * from './inject.decorator';
|
export * from './decorators/inject.decorator';
|
||||||
export { Component as Middleware } from './component.decorator';
|
export { Component as Middleware } from './decorators/component.decorator';
|
||||||
export * from './merge-with-values.util';
|
export * from './decorators/route-params.decorator';
|
||||||
|
export * from './decorators/catch.decorator';
|
||||||
|
export * from './decorators/exception-filters.decorator';
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import 'reflect-metadata';
|
|
||||||
import { SELF_PARAMS_METADATA } from '../constants';
|
|
||||||
import { isFunction } from './shared.utils';
|
|
||||||
|
|
||||||
export const Inject = (param): ParameterDecorator => {
|
|
||||||
return (target, key, index) => {
|
|
||||||
const selfArgs = Reflect.getMetadata(SELF_PARAMS_METADATA, target) || [];
|
|
||||||
const type = isFunction(param) ? param.name : param;
|
|
||||||
|
|
||||||
selfArgs.push({ index, param: type });
|
|
||||||
Reflect.defineMetadata(SELF_PARAMS_METADATA, selfArgs, target);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
export interface Constructor<T> {
|
export interface Constructor<T> {
|
||||||
new(...args: any[]): T
|
new(...args: any[]): T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MergeWithValues = <T extends Constructor<{}>>(data: { [param: string]: any }) => {
|
export const MergeWithValues = <T extends Constructor<{}>>(data: { [param: string]: any }) => {
|
||||||
return (metatype: T): any => {
|
return (Metatype: T): any => {
|
||||||
const type = class extends metatype {
|
const Type = class extends Metatype {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const token = metatype.name + JSON.stringify(data);
|
const token = Metatype.name + JSON.stringify(data);
|
||||||
Object.defineProperty(type, 'name', { value: token });
|
Object.defineProperty(Type, 'name', { value: token });
|
||||||
Object.assign(type.prototype, data);
|
Object.assign(Type.prototype, data);
|
||||||
return type;
|
return Type;
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import 'reflect-metadata';
|
|
||||||
import { ModuleMetadata } from '../interfaces/module-metadata.interface';
|
|
||||||
import { InvalidModuleConfigException } from '../../errors/exceptions/invalid-module-config.exception';
|
|
||||||
import { metadata } from '../constants';
|
|
||||||
|
|
||||||
export const Module = (props: ModuleMetadata): ClassDecorator => {
|
|
||||||
const propsKeys = Object.keys(props);
|
|
||||||
const acceptableParams = [ metadata.MODULES, metadata.EXPORTS, metadata.COMPONENTS, metadata.CONTROLLERS ];
|
|
||||||
|
|
||||||
propsKeys.forEach((prop) => {
|
|
||||||
if (acceptableParams.findIndex((param) => param === prop) < 0) {
|
|
||||||
throw new InvalidModuleConfigException(prop);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return (target: Object) => {
|
|
||||||
for (let property in props) {
|
|
||||||
if (props.hasOwnProperty(property)) {
|
|
||||||
Reflect.defineMetadata(property, props[property], target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
export const isUndefined = (obj): boolean => typeof obj === 'undefined';
|
export const isUndefined = (obj): obj is undefined => typeof obj === 'undefined';
|
||||||
export const isFunction = (fn): boolean => typeof fn === 'function';
|
export const isFunction = (fn): boolean => typeof fn === 'function';
|
||||||
|
export const isObject = (fn): fn is object => typeof fn === 'object';
|
||||||
|
export const isString = (fn): fn is string => typeof fn === 'string';
|
||||||
export const isConstructor = (fn): boolean => fn === 'constructor';
|
export const isConstructor = (fn): boolean => fn === 'constructor';
|
||||||
export const validatePath = (path): string => (path.charAt(0) !== '/') ? '/' + path : path;
|
export const validatePath = (path): string => (path.charAt(0) !== '/') ? '/' + path : path;
|
||||||
export const isNil = (obj): boolean => isUndefined(obj) || obj === null;
|
export const isNil = (obj): boolean => isUndefined(obj) || obj === null;
|
||||||
|
export const isEmpty = (array): boolean => !(array && array.length > 0);
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
|
|
||||||
export class ExpressAdapter {
|
export class ExpressAdapter {
|
||||||
|
public static create(): any {
|
||||||
static create(): any {
|
|
||||||
return express();
|
return express();
|
||||||
}
|
}
|
||||||
|
|
||||||
static createRouter(): any {
|
public static createRouter(): any {
|
||||||
return express.Router();
|
return express.Router();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export const messages = {
|
export const messages = {
|
||||||
APPLICATION_START: `Starting Nest application...`,
|
APPLICATION_START: `Starting Nest application...`,
|
||||||
APPLICATION_READY: `Nest application is ready!`,
|
APPLICATION_READY: `Nest application is ready!`,
|
||||||
UNKOWN_EXCEPTION_MESSAGE: 'Unkown exception'
|
UNKOWN_EXCEPTION_MESSAGE: 'Unkown exception',
|
||||||
};
|
};
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
import { HttpException } from './http-exception';
|
import { HttpException } from './http-exception';
|
||||||
import { messages } from '../constants';
|
import { messages } from '../constants';
|
||||||
import { Logger } from '../../common/services/logger.service';
|
import { Logger } from '../../common/services/logger.service';
|
||||||
|
import { ExceptionFilterMetadata } from '../../common/interfaces/exception-filter-metadata.interface';
|
||||||
|
import { isEmpty, isObject } from '../../common/utils/shared.utils';
|
||||||
|
import { InvalidExceptionFilterException } from '../../errors/exceptions/invalid-exception-filter.exception';
|
||||||
|
|
||||||
export class ExceptionsHandler {
|
export class ExceptionsHandler {
|
||||||
private readonly logger = new Logger(ExceptionsHandler.name);
|
private readonly logger = new Logger(ExceptionsHandler.name);
|
||||||
|
private filters: ExceptionFilterMetadata[] = [];
|
||||||
|
|
||||||
|
public next(exception: Error | HttpException | any, response) {
|
||||||
|
if (this.invokeCustomFilters(exception, response)) return;
|
||||||
|
|
||||||
next(exception: Error | HttpException, response) {
|
|
||||||
if (!(exception instanceof HttpException)) {
|
if (!(exception instanceof HttpException)) {
|
||||||
response.status(500).json({ message: messages.UNKOWN_EXCEPTION_MESSAGE });
|
response.status(500).json({ message: messages.UNKOWN_EXCEPTION_MESSAGE });
|
||||||
|
|
||||||
@@ -13,9 +19,28 @@ export class ExceptionsHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.status(exception.getStatus()).json({
|
const res = exception.getResponse();
|
||||||
message: exception.getMessage()
|
const message = isObject(res) ? res : ({ message: res });
|
||||||
});
|
response.status(exception.getStatus()).json(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setCustomFilters(filters: ExceptionFilterMetadata[]) {
|
||||||
|
if (!Array.isArray(filters)) {
|
||||||
|
throw new InvalidExceptionFilterException();
|
||||||
|
}
|
||||||
|
this.filters = filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public invokeCustomFilters(exception, response): boolean {
|
||||||
|
if (isEmpty(this.filters)) return false;
|
||||||
|
|
||||||
|
const filter = this.filters.find(({ exceptionMetatypes, func }) => {
|
||||||
|
const hasMetatype = !!exceptionMetatypes.find(
|
||||||
|
ExceptionMetatype => exception instanceof ExceptionMetatype,
|
||||||
|
);
|
||||||
|
return hasMetatype;
|
||||||
|
});
|
||||||
|
filter && filter.func(exception, response);
|
||||||
|
return !!filter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
export class HttpException {
|
export class HttpException {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly message: string,
|
private readonly response: string | object,
|
||||||
private readonly status: number) {}
|
private readonly status: number) {}
|
||||||
|
|
||||||
getMessage() {
|
public getResponse(): string | object {
|
||||||
return this.message;
|
return this.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatus() {
|
public getStatus(): number {
|
||||||
return this.status;
|
return this.status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import { RequestMethod } from '../../common/enums/request-method.enum';
|
import { RequestMethod } from '../../common/enums/request-method.enum';
|
||||||
|
|
||||||
export const getModuleInitMessage =
|
export const ModuleInitMessage = (module: string) => `${module} dependencies initialized`;
|
||||||
(module: string) => `${module} dependencies initialized`;
|
export const RouteMappedMessage = (path: string, method) => `Mapped {${path}, ${RequestMethod[method]}} route`;
|
||||||
|
export const ControllerMappingMessage = (name: string) => `${name}:`;
|
||||||
export const getRouteMappedMessage =
|
|
||||||
(path: string, method) => `Mapped {${path}, ${RequestMethod[method]}} route`;
|
|
||||||
|
|
||||||
export const getControllerMappingMessage =
|
|
||||||
(name: string) => `${name}:`;
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { RequestMethod } from '../../common/enums/request-method.enum';
|
import { RequestMethod } from '../../common/enums/request-method.enum';
|
||||||
|
|
||||||
export class RouterMethodFactory {
|
export class RouterMethodFactory {
|
||||||
get(target, requestMethod: RequestMethod) {
|
public get(target, requestMethod: RequestMethod) {
|
||||||
switch(requestMethod) {
|
switch (requestMethod) {
|
||||||
case RequestMethod.POST: return target.post;
|
case RequestMethod.POST: return target.post;
|
||||||
case RequestMethod.ALL: return target.all;
|
case RequestMethod.ALL: return target.all;
|
||||||
case RequestMethod.DELETE: return target.delete;
|
case RequestMethod.DELETE: return target.delete;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export { HttpException } from './exceptions/http-exception';
|
export { HttpException } from './exceptions/http-exception';
|
||||||
export { NestMiddleware, MiddlewareConfiguration } from './middlewares/interfaces';
|
export { NestMiddleware, MiddlewareConfiguration } from './middlewares/interfaces';
|
||||||
export { MiddlewareBuilder } from './middlewares/builder';
|
export { MiddlewareBuilder } from './middlewares/builder';
|
||||||
|
export { ModuleRef } from './injector/module-ref';
|
||||||
@@ -7,18 +7,18 @@ import { Metatype } from '../../common/interfaces/metatype.interface';
|
|||||||
export class NestContainer {
|
export class NestContainer {
|
||||||
private readonly modules = new Map<string, Module>();
|
private readonly modules = new Map<string, Module>();
|
||||||
|
|
||||||
addModule(metatype: NestModuleMetatype) {
|
public addModule(metatype: NestModuleMetatype) {
|
||||||
if (this.modules.has(metatype.name)) { return; }
|
if (this.modules.has(metatype.name)) return;
|
||||||
|
|
||||||
this.modules.set(metatype.name, new Module(metatype));
|
this.modules.set(metatype.name, new Module(metatype));
|
||||||
}
|
}
|
||||||
|
|
||||||
getModules(): Map<string, Module> {
|
public getModules(): Map<string, Module> {
|
||||||
return this.modules;
|
return this.modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
addRelatedModule(relatedModule: NestModuleMetatype, target: NestModuleMetatype) {
|
public addRelatedModule(relatedModule: NestModuleMetatype, target: NestModuleMetatype) {
|
||||||
if (!this.modules.has(target.name)) { return; }
|
if (!this.modules.has(target.name)) return;
|
||||||
|
|
||||||
const module = this.modules.get(target.name);
|
const module = this.modules.get(target.name);
|
||||||
const related = this.modules.get(relatedModule.name);
|
const related = this.modules.get(relatedModule.name);
|
||||||
@@ -26,34 +26,31 @@ export class NestContainer {
|
|||||||
module.addRelatedModule(related);
|
module.addRelatedModule(related);
|
||||||
}
|
}
|
||||||
|
|
||||||
addComponent(component: Metatype<Injectable>, metatype: NestModuleMetatype) {
|
public addComponent(component: Metatype<Injectable>, metatype: NestModuleMetatype) {
|
||||||
if (!this.modules.has(metatype.name)) {
|
if (!this.modules.has(metatype.name)) {
|
||||||
throw new UnkownModuleException();
|
throw new UnkownModuleException();
|
||||||
}
|
}
|
||||||
|
|
||||||
const module = this.modules.get(metatype.name);
|
const module = this.modules.get(metatype.name);
|
||||||
module.addComponent(component);
|
module.addComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
addExportedComponent(exportedComponent: Metatype<Injectable>, metatype: NestModuleMetatype) {
|
public addExportedComponent(exportedComponent: Metatype<Injectable>, metatype: NestModuleMetatype) {
|
||||||
if (!this.modules.has(metatype.name)) {
|
if (!this.modules.has(metatype.name)) {
|
||||||
throw new UnkownModuleException();
|
throw new UnkownModuleException();
|
||||||
}
|
}
|
||||||
|
|
||||||
const module = this.modules.get(metatype.name);
|
const module = this.modules.get(metatype.name);
|
||||||
module.addExportedComponent(exportedComponent);
|
module.addExportedComponent(exportedComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
addController(controller: Metatype<Controller>, metatype: NestModuleMetatype) {
|
public addController(controller: Metatype<Controller>, metatype: NestModuleMetatype) {
|
||||||
if(!this.modules.has(metatype.name)) {
|
if (!this.modules.has(metatype.name)) {
|
||||||
throw new UnkownModuleException();
|
throw new UnkownModuleException();
|
||||||
}
|
}
|
||||||
|
|
||||||
const module = this.modules.get(metatype.name);
|
const module = this.modules.get(metatype.name);
|
||||||
module.addRoute(controller);
|
module.addRoute(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
public clear() {
|
||||||
this.modules.clear();
|
this.modules.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,37 +8,36 @@ import { Controller } from '../../common/interfaces/controller.interface';
|
|||||||
import { Injectable } from '../../common/interfaces/injectable.interface';
|
import { Injectable } from '../../common/interfaces/injectable.interface';
|
||||||
import { MiddlewareWrapper } from '../middlewares/container';
|
import { MiddlewareWrapper } from '../middlewares/container';
|
||||||
import { isUndefined, isNil, isFunction } from '../../common/utils/shared.utils';
|
import { isUndefined, isNil, isFunction } from '../../common/utils/shared.utils';
|
||||||
import { PARAMTYPES_METADATA, SELF_PARAMS_METADATA } from '../../common/constants';
|
import { PARAMTYPES_METADATA, SELF_DECLARED_DEPS_METADATA } from '../../common/constants';
|
||||||
|
|
||||||
export class Injector {
|
export class Injector {
|
||||||
|
public loadInstanceOfMiddleware(
|
||||||
loadInstanceOfMiddleware(
|
|
||||||
wrapper: MiddlewareWrapper,
|
wrapper: MiddlewareWrapper,
|
||||||
collection: Map<string, MiddlewareWrapper>,
|
collection: Map<string, MiddlewareWrapper>,
|
||||||
module: Module) {
|
module: Module) {
|
||||||
|
|
||||||
const { metatype } = wrapper;
|
const { metatype } = wrapper;
|
||||||
const currentFetchedMiddleware = collection.get(metatype.name);
|
const currentMetatype = collection.get(metatype.name);
|
||||||
if (currentFetchedMiddleware.instance !== null) return;
|
if (currentMetatype.instance !== null) return;
|
||||||
|
|
||||||
this.resolveConstructorParams(<any>wrapper, module, null, (argsInstances) => {
|
this.resolveConstructorParams(wrapper as any, module, null, argsInstances => {
|
||||||
collection.set(metatype.name, {
|
collection.set(metatype.name, {
|
||||||
instance: new metatype(...argsInstances),
|
instance: new metatype(...argsInstances),
|
||||||
metatype
|
metatype,
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadInstanceOfRoute(wrapper: InstanceWrapper<Controller>, module: Module) {
|
public loadInstanceOfRoute(wrapper: InstanceWrapper<Controller>, module: Module) {
|
||||||
const routes = module.routes;
|
const routes = module.routes;
|
||||||
this.loadInstance<Controller>(wrapper, routes, module);
|
this.loadInstance<Controller>(wrapper, routes, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPrototypeOfInstance<T>({ metatype, name }: InstanceWrapper<T>, collection: Map<string, InstanceWrapper<T>>) {
|
public loadPrototypeOfInstance<T>({ metatype, name }: InstanceWrapper<T>, collection: Map<string, InstanceWrapper<T>>) {
|
||||||
if (!collection) { return; }
|
if (!collection) return;
|
||||||
|
|
||||||
const target = collection.get(name);
|
const target = collection.get(name);
|
||||||
if (target.isResolved || !isNil(target.inject)) { return; }
|
if (target.isResolved || !isNil(target.inject)) return;
|
||||||
|
|
||||||
collection.set(name, {
|
collection.set(name, {
|
||||||
...collection.get(name),
|
...collection.get(name),
|
||||||
@@ -46,40 +45,44 @@ export class Injector {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadInstanceOfComponent(wrapper: InstanceWrapper<Injectable>, module: Module) {
|
public loadInstanceOfComponent(wrapper: InstanceWrapper<Injectable>, module: Module) {
|
||||||
const components = module.components;
|
const components = module.components;
|
||||||
this.loadInstance<Injectable>(wrapper, components, module);
|
this.loadInstance<Injectable>(wrapper, components, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadInstance<T>(wrapper: InstanceWrapper<T>, collection, module: Module) {
|
public loadInstance<T>(wrapper: InstanceWrapper<T>, collection, module: Module) {
|
||||||
const { metatype, name, inject } = wrapper;
|
const { metatype, name, inject } = wrapper;
|
||||||
const currentFetchedInstance = collection.get(name);
|
const currentMetatype = collection.get(name);
|
||||||
if (isUndefined(currentFetchedInstance)) {
|
if (isUndefined(currentMetatype)) {
|
||||||
throw new RuntimeException('');
|
throw new RuntimeException('');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentFetchedInstance.isResolved) return;
|
if (currentMetatype.isResolved) return;
|
||||||
this.resolveConstructorParams<T>(wrapper, module, inject, (argsInstances) => {
|
this.resolveConstructorParams<T>(wrapper, module, inject, argsInstances => {
|
||||||
if (isNil(inject)) {
|
if (isNil(inject)) {
|
||||||
currentFetchedInstance.instance = Object.assign(
|
currentMetatype.instance = Object.assign(
|
||||||
currentFetchedInstance.instance,
|
currentMetatype.instance,
|
||||||
new metatype(...argsInstances),
|
new metatype(...argsInstances),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
currentMetatype.instance = currentMetatype.metatype(...argsInstances);
|
||||||
}
|
}
|
||||||
else {
|
currentMetatype.isResolved = true;
|
||||||
currentFetchedInstance.instance = currentFetchedInstance.metatype(...argsInstances);
|
|
||||||
}
|
|
||||||
currentFetchedInstance.isResolved = true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveConstructorParams<T>(wrapper: InstanceWrapper<T>, module: Module, inject: any[], callback: Function) {
|
public resolveConstructorParams<T>(
|
||||||
|
wrapper: InstanceWrapper<T>,
|
||||||
|
module: Module,
|
||||||
|
inject: any[],
|
||||||
|
callback: (args) => void) {
|
||||||
|
|
||||||
const args = isNil(inject) ? this.reflectConstructorParams(wrapper.metatype) : inject;
|
const args = isNil(inject) ? this.reflectConstructorParams(wrapper.metatype) : inject;
|
||||||
const instances = args.map(param => this.resolveSingleParam<T>(wrapper, param, module));
|
const instances = args.map(param => this.resolveSingleParam<T>(wrapper, param, module));
|
||||||
callback(instances);
|
callback(instances);
|
||||||
}
|
}
|
||||||
|
|
||||||
reflectConstructorParams<T>(type: Metatype<T>): any[] {
|
public reflectConstructorParams<T>(type: Metatype<T>): any[] {
|
||||||
const paramtypes = Reflect.getMetadata(PARAMTYPES_METADATA, type) || [];
|
const paramtypes = Reflect.getMetadata(PARAMTYPES_METADATA, type) || [];
|
||||||
const selfParams = this.reflectSelfParams<T>(type);
|
const selfParams = this.reflectSelfParams<T>(type);
|
||||||
|
|
||||||
@@ -87,37 +90,32 @@ export class Injector {
|
|||||||
return paramtypes;
|
return paramtypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
reflectSelfParams<T>(type: Metatype<T>): any[] {
|
public reflectSelfParams<T>(type: Metatype<T>): any[] {
|
||||||
return Reflect.getMetadata(SELF_PARAMS_METADATA, type) || [];
|
return Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, type) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveSingleParam<T>(wrapper: InstanceWrapper<T>, param: Metatype<any> | string | symbol, module: Module) {
|
public resolveSingleParam<T>(wrapper: InstanceWrapper<T>, param: Metatype<any> | string | symbol, module: Module) {
|
||||||
if (isUndefined(param)) {
|
if (isUndefined(param)) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
return this.resolveComponentInstance<T>(
|
return this.resolveComponentInstance<T>(
|
||||||
module,
|
module,
|
||||||
isFunction(param) ? (<Metatype<any>>param).name : param,
|
isFunction(param) ? (param as Metatype<any>).name : param,
|
||||||
wrapper
|
wrapper,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveComponentInstance<T>(module: Module, name: any, wrapper: InstanceWrapper<T>) {
|
public resolveComponentInstance<T>(module: Module, name: any, wrapper: InstanceWrapper<T>) {
|
||||||
const components = module.components;
|
const components = module.components;
|
||||||
const instanceWrapper = this.scanForComponent<T>(components, name, module, wrapper);
|
const instanceWrapper = this.scanForComponent(components, name, module, wrapper);
|
||||||
|
|
||||||
if (isNil(instanceWrapper.instance)) {
|
if (!instanceWrapper.isResolved) {
|
||||||
this.loadInstanceOfComponent(components.get(name), module);
|
this.loadInstanceOfComponent(components.get(name), module);
|
||||||
}
|
}
|
||||||
return instanceWrapper.instance;
|
return instanceWrapper.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
scanForComponent<T>(
|
public scanForComponent(components: Map<string, any>, name: any, module: Module, { metatype }) {
|
||||||
components: Map<string, any>,
|
|
||||||
name: any,
|
|
||||||
module: Module,
|
|
||||||
{ metatype }) {
|
|
||||||
|
|
||||||
if (components.has(name)) {
|
if (components.has(name)) {
|
||||||
return components.get(name);
|
return components.get(name);
|
||||||
}
|
}
|
||||||
@@ -129,13 +127,13 @@ export class Injector {
|
|||||||
return instanceWrapper;
|
return instanceWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
scanForComponentInRelatedModules(module: Module, name: any) {
|
public scanForComponentInRelatedModules(module: Module, name: any) {
|
||||||
const relatedModules = module.relatedModules || [];
|
const relatedModules = module.relatedModules || [];
|
||||||
let component = null;
|
let component = null;
|
||||||
|
|
||||||
(<Array<any>>relatedModules).forEach((relatedModule) => {
|
(relatedModules as any[]).forEach((relatedModule) => {
|
||||||
const { components, exports } = relatedModule;
|
const { components, exports } = relatedModule;
|
||||||
if (!exports.has(name) || !components.has(name)) { return; }
|
if (!exports.has(name) || !components.has(name)) return;
|
||||||
|
|
||||||
component = components.get(name);
|
component = components.get(name);
|
||||||
if (!component.isResolved) {
|
if (!component.isResolved) {
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import { Injectable } from '../../common/interfaces/injectable.interface';
|
|||||||
import { Controller } from '../../common/interfaces/controller.interface';
|
import { Controller } from '../../common/interfaces/controller.interface';
|
||||||
import { Module } from './module';
|
import { Module } from './module';
|
||||||
import { Logger } from '../../common/services/logger.service';
|
import { Logger } from '../../common/services/logger.service';
|
||||||
import { getModuleInitMessage } from '../helpers/messages';
|
import { ModuleInitMessage } from '../helpers/messages';
|
||||||
|
import { isUndefined, isNil } from '../../common/utils/shared.utils';
|
||||||
|
import { OnModuleInit } from '../../common/interfaces/index';
|
||||||
|
|
||||||
export class InstanceLoader {
|
export class InstanceLoader {
|
||||||
private injector = new Injector();
|
private injector = new Injector();
|
||||||
@@ -12,7 +14,7 @@ export class InstanceLoader {
|
|||||||
|
|
||||||
constructor(private container: NestContainer) {}
|
constructor(private container: NestContainer) {}
|
||||||
|
|
||||||
createInstancesOfDependencies() {
|
public createInstancesOfDependencies() {
|
||||||
const modules = this.container.getModules();
|
const modules = this.container.getModules();
|
||||||
|
|
||||||
this.createPrototypes(modules);
|
this.createPrototypes(modules);
|
||||||
@@ -30,8 +32,10 @@ export class InstanceLoader {
|
|||||||
modules.forEach((module, name) => {
|
modules.forEach((module, name) => {
|
||||||
this.createInstancesOfComponents(module);
|
this.createInstancesOfComponents(module);
|
||||||
this.createInstancesOfRoutes(module);
|
this.createInstancesOfRoutes(module);
|
||||||
this.logger.log(getModuleInitMessage(name));
|
this.callModuleInitHook(module);
|
||||||
})
|
|
||||||
|
this.logger.log(ModuleInitMessage(name));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createPrototypesOfComponents(module: Module) {
|
private createPrototypesOfComponents(module: Module) {
|
||||||
@@ -58,4 +62,15 @@ export class InstanceLoader {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private callModuleInitHook(module: Module) {
|
||||||
|
const components = [...module.routes, ...module.components];
|
||||||
|
components.map(([key, {instance}]) => instance)
|
||||||
|
.filter((instance) => !isNil(instance))
|
||||||
|
.filter(this.hasOnModuleInitHook)
|
||||||
|
.forEach((instance) => (instance as OnModuleInit).onModuleInit());
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasOnModuleInitHook(instance: Controller | Injectable): instance is OnModuleInit {
|
||||||
|
return !isUndefined((instance as OnModuleInit).onModuleInit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1 +1,5 @@
|
|||||||
export class ModuleRef {}
|
import { OpaqueToken } from './module';
|
||||||
|
|
||||||
|
export abstract class ModuleRef {
|
||||||
|
public abstract get<T>(type: OpaqueToken): T;
|
||||||
|
}
|
||||||
@@ -4,84 +4,112 @@ import { UnkownExportException } from '../../errors/exceptions/unkown-export.exc
|
|||||||
import { NestModuleMetatype } from '../../common/interfaces/module-metatype.interface';
|
import { NestModuleMetatype } from '../../common/interfaces/module-metatype.interface';
|
||||||
import { Metatype } from '../../common/interfaces/metatype.interface';
|
import { Metatype } from '../../common/interfaces/metatype.interface';
|
||||||
import { ModuleRef } from './module-ref';
|
import { ModuleRef } from './module-ref';
|
||||||
import { isFunction, isNil } from '../../common/utils/shared.utils';
|
import { isFunction, isNil, isUndefined } from '../../common/utils/shared.utils';
|
||||||
|
import { RuntimeException } from '../../errors/exceptions/runtime.exception';
|
||||||
|
|
||||||
export type CustomComponent = { provide: any };
|
export interface CustomComponent {
|
||||||
|
provide: any;
|
||||||
|
}
|
||||||
|
export type OpaqueToken = string | symbol | object | Metatype<any>;
|
||||||
export type CustomClass = CustomComponent & { useClass: Metatype<any> };
|
export type CustomClass = CustomComponent & { useClass: Metatype<any> };
|
||||||
export type CustomFactory = CustomComponent & { useFactory: Function, inject?: Metatype<any>[] };
|
export type CustomFactory = CustomComponent & { useFactory: (...args) => any, inject?: Metatype<any>[] };
|
||||||
export type CustomValue = CustomComponent & { useValue: any };
|
export type CustomValue = CustomComponent & { useValue: any };
|
||||||
export type ComponentMetatype = Metatype<Injectable> | CustomFactory | CustomValue | CustomClass;
|
export type ComponentMetatype = Metatype<Injectable> | CustomFactory | CustomValue | CustomClass;
|
||||||
|
|
||||||
export class Module {
|
export class Module {
|
||||||
private _instance: NestModule;
|
|
||||||
private _relatedModules = new Set<Module>();
|
private _relatedModules = new Set<Module>();
|
||||||
private _components = new Map<any, InstanceWrapper<Injectable>>();
|
private _components = new Map<any, InstanceWrapper<Injectable>>();
|
||||||
private _routes = new Map<string, InstanceWrapper<Controller>>();
|
private _routes = new Map<string, InstanceWrapper<Controller>>();
|
||||||
private _exports = new Set<string>();
|
private _exports = new Set<string>();
|
||||||
|
|
||||||
constructor(private _metatype: NestModuleMetatype) {
|
constructor(private _metatype: NestModuleMetatype) {
|
||||||
this._instance = new _metatype();
|
|
||||||
this.addModuleRef();
|
this.addModuleRef();
|
||||||
|
this.addModuleAsComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
get relatedModules() : Set<Module> {
|
get relatedModules(): Set<Module> {
|
||||||
return this._relatedModules;
|
return this._relatedModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
get components() : Map<string, InstanceWrapper<Injectable>> {
|
get components(): Map<string, InstanceWrapper<Injectable>> {
|
||||||
return this._components;
|
return this._components;
|
||||||
}
|
}
|
||||||
|
|
||||||
get routes() : Map<string, InstanceWrapper<Controller>> {
|
get routes(): Map<string, InstanceWrapper<Controller>> {
|
||||||
return this._routes;
|
return this._routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
get exports() : Set<string> {
|
get exports(): Set<string> {
|
||||||
return this._exports;
|
return this._exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
get instance() : NestModule {
|
get instance(): NestModule {
|
||||||
return this._instance;
|
if (!this._components.has(this._metatype.name)) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
const module = this._components.get(this._metatype.name);
|
||||||
|
return module.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
set instance(value: NestModule) {
|
get metatype(): NestModuleMetatype {
|
||||||
this._instance = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get metatype() : NestModuleMetatype {
|
|
||||||
return this._metatype;
|
return this._metatype;
|
||||||
}
|
}
|
||||||
|
|
||||||
addModuleRef() {
|
public addModuleRef() {
|
||||||
const moduleRef = this.getModuleRefMetatype(this._components);
|
const moduleRef = this.getModuleRefMetatype(this._components);
|
||||||
this._components.set(ModuleRef.name, {
|
this._components.set(ModuleRef.name, {
|
||||||
name: ModuleRef.name,
|
name: ModuleRef.name,
|
||||||
metatype: ModuleRef,
|
metatype: ModuleRef as any,
|
||||||
isResolved: true,
|
isResolved: true,
|
||||||
instance: new moduleRef,
|
instance: new moduleRef(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addComponent(component: ComponentMetatype) {
|
public addModuleAsComponent() {
|
||||||
if (!isNil((<CustomComponent>component).provide)) {
|
this._components.set(this._metatype.name, {
|
||||||
|
name: this._metatype.name,
|
||||||
|
metatype: this._metatype,
|
||||||
|
isResolved: false,
|
||||||
|
instance: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public addComponent(component: ComponentMetatype) {
|
||||||
|
if (this.isCustomComponent(component)) {
|
||||||
this.addCustomComponent(component);
|
this.addCustomComponent(component);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._components.set((<Metatype<Injectable>>component).name, {
|
this._components.set((component as Metatype<Injectable>).name, {
|
||||||
name: (<Metatype<Injectable>>component).name,
|
name: (component as Metatype<Injectable>).name,
|
||||||
metatype: <Metatype<Injectable>>component,
|
metatype: component as Metatype<Injectable>,
|
||||||
instance: null,
|
instance: null,
|
||||||
isResolved: false,
|
isResolved: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addCustomComponent(component: ComponentMetatype) {
|
public isCustomComponent(component: ComponentMetatype): component is CustomClass | CustomFactory | CustomValue {
|
||||||
if ((<CustomClass>component).useClass) this.addCustomClass(<CustomClass>component);
|
return !isNil((component as CustomComponent).provide);
|
||||||
else if((<CustomValue>component).useValue) this.addCustomValue(<CustomValue>component);
|
|
||||||
else if((<CustomFactory>component).useFactory) this.addCustomFactory(<CustomFactory>component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addCustomClass(component: CustomClass) {
|
public addCustomComponent(component: ComponentMetatype) {
|
||||||
|
if (this.isCustomClass(component)) this.addCustomClass(component);
|
||||||
|
else if (this.isCustomValue(component)) this.addCustomValue(component);
|
||||||
|
else if (this.isCustomFactory(component)) this.addCustomFactory(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isCustomClass(component): component is CustomClass {
|
||||||
|
return !isUndefined((component as CustomClass).useClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isCustomValue(component): component is CustomValue {
|
||||||
|
return !isUndefined((component as CustomValue).useValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isCustomFactory(component): component is CustomFactory {
|
||||||
|
return !isUndefined((component as CustomFactory).useFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addCustomClass(component: CustomClass) {
|
||||||
const { provide: metatype, useClass } = component;
|
const { provide: metatype, useClass } = component;
|
||||||
this._components.set(metatype.name, {
|
this._components.set(metatype.name, {
|
||||||
name: metatype.name,
|
name: metatype.name,
|
||||||
@@ -91,12 +119,12 @@ export class Module {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addCustomValue(component: CustomValue) {
|
public addCustomValue(component: CustomValue) {
|
||||||
const { provide, useValue: value } = component;
|
const { provide, useValue: value } = component;
|
||||||
const name = isFunction(provide) ? provide.name : provide;
|
const name = isFunction(provide) ? provide.name : provide;
|
||||||
|
|
||||||
this._components.set(name, {
|
this._components.set(name, {
|
||||||
name: name,
|
name,
|
||||||
metatype: null,
|
metatype: null,
|
||||||
instance: value,
|
instance: value,
|
||||||
isResolved: true,
|
isResolved: true,
|
||||||
@@ -104,11 +132,11 @@ export class Module {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addCustomFactory(component: CustomFactory){
|
public addCustomFactory(component: CustomFactory){
|
||||||
const { provide: name, useFactory: factory, inject } = component;
|
const { provide: name, useFactory: factory, inject } = component;
|
||||||
this._components.set(name, {
|
this._components.set(name, {
|
||||||
name: name,
|
name,
|
||||||
metatype: <any>factory,
|
metatype: factory as any,
|
||||||
instance: null,
|
instance: null,
|
||||||
isResolved: false,
|
isResolved: false,
|
||||||
inject: inject || [],
|
inject: inject || [],
|
||||||
@@ -116,35 +144,35 @@ export class Module {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addExportedComponent(exportedComponent: Metatype<Injectable>) {
|
public addExportedComponent(exportedComponent: Metatype<Injectable>) {
|
||||||
if (!this._components.get(exportedComponent.name)) {
|
if (!this._components.get(exportedComponent.name)) {
|
||||||
throw new UnkownExportException(exportedComponent.name);
|
throw new UnkownExportException(exportedComponent.name);
|
||||||
}
|
}
|
||||||
this._exports.add(exportedComponent.name);
|
this._exports.add(exportedComponent.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
addRoute(route: Metatype<Controller>) {
|
public addRoute(route: Metatype<Controller>) {
|
||||||
this._routes.set(route.name, {
|
this._routes.set(route.name, {
|
||||||
name: (<Metatype<Controller>>route).name,
|
name: route.name,
|
||||||
metatype: route,
|
metatype: route,
|
||||||
instance: null,
|
instance: null,
|
||||||
isResolved: false,
|
isResolved: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addRelatedModule(relatedModule) {
|
public addRelatedModule(relatedModule) {
|
||||||
this._relatedModules.add(relatedModule);
|
this._relatedModules.add(relatedModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getModuleRefMetatype(components) {
|
private getModuleRefMetatype(components) {
|
||||||
return class extends this._metatype {
|
return class {
|
||||||
private readonly components = components;
|
private readonly components = components;
|
||||||
|
|
||||||
get<T>(type: string | symbol | object | Metatype<any>): T {
|
public get<T>(type: OpaqueToken): T {
|
||||||
const name = isFunction(type) ? (<Metatype<any>>type).name : type;
|
const name = isFunction(type) ? (type as Metatype<any>).name : type;
|
||||||
const exists = this.components.has(name);
|
const exists = this.components.has(name);
|
||||||
|
|
||||||
return exists ? <T>this.components.get(name).instance : null;
|
return exists ? this.components.get(name).instance as T : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/core/metadata-scanner.ts
Normal file
17
src/core/metadata-scanner.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Injectable } from '../common/interfaces/injectable.interface';
|
||||||
|
import { isConstructor, isFunction, isNil } from '../common/utils/shared.utils';
|
||||||
|
|
||||||
|
export class MetadataScanner {
|
||||||
|
public scanFromPrototype<T extends Injectable, R>(instance: T, prototype, callback: (name: string) => R): R[] {
|
||||||
|
return Object.getOwnPropertyNames(prototype)
|
||||||
|
.filter((method) => {
|
||||||
|
const descriptor = Object.getOwnPropertyDescriptor(prototype, method);
|
||||||
|
if (descriptor.set || descriptor.get) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !isConstructor(method) && isFunction(prototype[method]);
|
||||||
|
})
|
||||||
|
.map(callback)
|
||||||
|
.filter((metadata) => !isNil(metadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,36 +3,25 @@ import { InvalidMiddlewareConfigurationException } from '../../errors/exceptions
|
|||||||
import { isUndefined, isNil } from '../../common/utils/shared.utils';
|
import { isUndefined, isNil } from '../../common/utils/shared.utils';
|
||||||
import { BindResolveMiddlewareValues } from '../../common/utils/bind-resolve-values.util';
|
import { BindResolveMiddlewareValues } from '../../common/utils/bind-resolve-values.util';
|
||||||
import { Logger } from '../../common/services/logger.service';
|
import { Logger } from '../../common/services/logger.service';
|
||||||
import { Metatype } from '../../common/interfaces/metatype.interface';
|
import { Metatype, MiddlewaresConsumer } from '../../common/interfaces';
|
||||||
|
import { MiddlewareConfigProxy } from '../../index';
|
||||||
|
import { RoutesMapper } from './routes-mapper';
|
||||||
|
|
||||||
export class MiddlewareBuilder {
|
export class MiddlewareBuilder implements MiddlewaresConsumer {
|
||||||
private middlewaresCollection = new Set<MiddlewareConfiguration>();
|
private readonly middlewaresCollection = new Set<MiddlewareConfiguration>();
|
||||||
private logger = new Logger(MiddlewareBuilder.name);
|
private readonly logger = new Logger(MiddlewareBuilder.name);
|
||||||
|
|
||||||
apply(metatypes: Metatype<any> | Array<Metatype<any>>): MiddlewareConfigProxy {
|
constructor(private readonly routesMapper: RoutesMapper) {}
|
||||||
let resolveParams = null;
|
|
||||||
const configProxy = {
|
public apply(metatypes: Metatype<any> | Metatype<any>[]): MiddlewareConfigProxy {
|
||||||
with: (...data) => {
|
return new MiddlewareBuilder.ConfigProxy(this, metatypes);
|
||||||
resolveParams = data;
|
|
||||||
return configProxy as MiddlewareConfigProxy;
|
|
||||||
},
|
|
||||||
forRoutes: (...routes) => {
|
|
||||||
const configuration = {
|
|
||||||
middlewares: this.bindValuesToResolve(metatypes, resolveParams),
|
|
||||||
forRoutes: routes
|
|
||||||
};
|
|
||||||
this.middlewaresCollection.add(<MiddlewareConfiguration>configuration);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return configProxy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* Since version RC.6 this method is deprecated. Use apply() instead.
|
* Since version RC.6 this method is deprecated. Use apply() instead.
|
||||||
*/
|
*/
|
||||||
use(configuration: MiddlewareConfiguration) {
|
public use(configuration: MiddlewareConfiguration) {
|
||||||
this.logger.warn('DEPRECATED! Since version RC.6 `use()` method is deprecated. Use `apply()` instead.');
|
this.logger.warn('DEPRECATED! Since version RC.6 `use()` method is deprecated. Use `apply()` instead.');
|
||||||
|
|
||||||
const { middlewares, forRoutes } = configuration;
|
const { middlewares, forRoutes } = configuration;
|
||||||
@@ -44,20 +33,48 @@ export class MiddlewareBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
public build() {
|
||||||
return [ ...this.middlewaresCollection ];
|
return [ ...this.middlewaresCollection ];
|
||||||
}
|
}
|
||||||
|
|
||||||
private bindValuesToResolve(middlewares: Metatype<any> | Array<Metatype<any>>, resolveParams: Array<any>) {
|
private bindValuesToResolve(middlewares: Metatype<any> | Metatype<any>[], resolveParams: any[]) {
|
||||||
if (isNil(resolveParams)) {
|
if (isNil(resolveParams)) {
|
||||||
return middlewares;
|
return middlewares;
|
||||||
}
|
}
|
||||||
const bindArgs = BindResolveMiddlewareValues(resolveParams);
|
const bindArgs = BindResolveMiddlewareValues(resolveParams);
|
||||||
return [].concat(middlewares).map(bindArgs);
|
return [].concat(middlewares).map(bindArgs);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export interface MiddlewareConfigProxy {
|
private static ConfigProxy = class implements MiddlewareConfigProxy {
|
||||||
with: (...data) => MiddlewareConfigProxy;
|
private contextArgs = null;
|
||||||
forRoutes: (...routes) => MiddlewareBuilder;
|
|
||||||
|
constructor(
|
||||||
|
private readonly builder: MiddlewareBuilder,
|
||||||
|
private readonly includedRoutes: Metatype<any> | Metatype<any>[]) {}
|
||||||
|
|
||||||
|
public with(...args): this {
|
||||||
|
this.contextArgs = args;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public forRoutes(...routes): MiddlewaresConsumer {
|
||||||
|
const { middlewaresCollection, bindValuesToResolve, routesMapper } = this.builder;
|
||||||
|
|
||||||
|
const forRoutes = this.mapRoutesToFlatList(
|
||||||
|
routes.map((route) => routesMapper.mapRouteToRouteProps(route),
|
||||||
|
));
|
||||||
|
const configuration = {
|
||||||
|
middlewares: bindValuesToResolve(
|
||||||
|
this.includedRoutes, this.contextArgs,
|
||||||
|
),
|
||||||
|
forRoutes,
|
||||||
|
};
|
||||||
|
middlewaresCollection.add(configuration);
|
||||||
|
return this.builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapRoutesToFlatList(forRoutes) {
|
||||||
|
return forRoutes.reduce((a, b) => a.concat(b));
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -1,46 +1,36 @@
|
|||||||
import { MiddlewareConfiguration } from './interfaces/middleware-configuration.interface';
|
import { MiddlewareConfiguration } from './interfaces/middleware-configuration.interface';
|
||||||
import { NestMiddleware } from './interfaces/nest-middleware.interface';
|
import { NestMiddleware } from './interfaces/nest-middleware.interface';
|
||||||
import { RoutesMapper } from './routes-mapper';
|
|
||||||
import { Metatype } from '../../common/interfaces/metatype.interface';
|
import { Metatype } from '../../common/interfaces/metatype.interface';
|
||||||
|
|
||||||
export class MiddlewaresContainer {
|
export class MiddlewaresContainer {
|
||||||
private readonly middlewares = new Map<string, Map<string, MiddlewareWrapper>>();
|
private readonly middlewares = new Map<string, Map<string, MiddlewareWrapper>>();
|
||||||
private readonly configs = new Map<string, Set<MiddlewareConfiguration>>();
|
private readonly configs = new Map<string, Set<MiddlewareConfiguration>>();
|
||||||
|
|
||||||
constructor(private routesMapper: RoutesMapper) {}
|
public getMiddlewares(module: string): Map<string, MiddlewareWrapper> {
|
||||||
|
|
||||||
getMiddlewares(module: string): Map<string, MiddlewareWrapper> {
|
|
||||||
return this.middlewares.get(module) || new Map();
|
return this.middlewares.get(module) || new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigs(): Map<string, Set<MiddlewareConfiguration>> {
|
public getConfigs(): Map<string, Set<MiddlewareConfiguration>> {
|
||||||
return this.configs;
|
return this.configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
addConfig(configList: MiddlewareConfiguration[], module: string) {
|
public addConfig(configList: MiddlewareConfiguration[], module: string) {
|
||||||
const middlewares = this.getCurrentMiddlewares(module);
|
const middlewares = this.getCurrentMiddlewares(module);
|
||||||
const currentConfig = this.getCurrentConfig(module);
|
const currentConfig = this.getCurrentConfig(module);
|
||||||
|
|
||||||
(configList || []).map((config) => {
|
const configurations = configList || [];
|
||||||
|
configurations.map((config) => {
|
||||||
[].concat(config.middlewares).map((metatype) => {
|
[].concat(config.middlewares).map((metatype) => {
|
||||||
const token = metatype.name;
|
const token = metatype.name;
|
||||||
middlewares.set(token, {
|
middlewares.set(token, {
|
||||||
instance: null,
|
instance: null,
|
||||||
metatype
|
metatype,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
config.forRoutes = this.mapRoutesToFlatList(config.forRoutes);
|
|
||||||
currentConfig.add(config);
|
currentConfig.add(config);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapRoutesToFlatList(forRoutes) {
|
|
||||||
return forRoutes.map((route) => (
|
|
||||||
this.routesMapper.mapRouteToRouteProps(route)
|
|
||||||
)).reduce((a, b) => a.concat(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCurrentMiddlewares(module: string) {
|
private getCurrentMiddlewares(module: string) {
|
||||||
if (!this.middlewares.has(module)) {
|
if (!this.middlewares.has(module)) {
|
||||||
this.middlewares.set(module, new Map<string, MiddlewareWrapper>());
|
this.middlewares.set(module, new Map<string, MiddlewareWrapper>());
|
||||||
@@ -57,6 +47,6 @@ export class MiddlewaresContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MiddlewareWrapper {
|
export interface MiddlewareWrapper {
|
||||||
instance: NestMiddleware,
|
instance: NestMiddleware;
|
||||||
metatype: Metatype<NestMiddleware>
|
metatype: Metatype<NestMiddleware>;
|
||||||
}
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import { NestMiddleware } from './nest-middleware.interface';
|
|
||||||
import { Metatype } from '../../../common/interfaces/metatype.interface';
|
|
||||||
|
|
||||||
export interface MiddlewareMetatype extends Metatype<NestMiddleware> {}
|
|
||||||
@@ -12,30 +12,32 @@ import { RoutesMapper } from './routes-mapper';
|
|||||||
import { RouterProxy } from '../router/router-proxy';
|
import { RouterProxy } from '../router/router-proxy';
|
||||||
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
||||||
import { Module } from '../injector/module';
|
import { Module } from '../injector/module';
|
||||||
import { isUndefined } from 'util';
|
|
||||||
import { RouterMethodFactory } from '../helpers/router-method-factory';
|
import { RouterMethodFactory } from '../helpers/router-method-factory';
|
||||||
import { NestMiddleware } from './interfaces/nest-middleware.interface';
|
import { NestMiddleware } from './interfaces/nest-middleware.interface';
|
||||||
import { Metatype } from '../../common/interfaces/metatype.interface';
|
import { Metatype } from '../../common/interfaces/metatype.interface';
|
||||||
import { RuntimeException } from '../../errors/exceptions/runtime.exception';
|
import { RuntimeException } from '../../errors/exceptions/runtime.exception';
|
||||||
|
import { isUndefined } from '../../common/utils/shared.utils';
|
||||||
|
|
||||||
export class MiddlewaresModule {
|
export class MiddlewaresModule {
|
||||||
private static container = new MiddlewaresContainer(new RoutesMapper());
|
private static routesMapper = new RoutesMapper();
|
||||||
|
private static container = new MiddlewaresContainer();
|
||||||
private static resolver: MiddlewaresResolver;
|
private static resolver: MiddlewaresResolver;
|
||||||
private static routerProxy = new RouterProxy(new ExceptionsHandler());
|
private static exceptionHandler = new ExceptionsHandler();
|
||||||
|
private static routerProxy = new RouterProxy();
|
||||||
private static routerMethodFactory = new RouterMethodFactory();
|
private static routerMethodFactory = new RouterMethodFactory();
|
||||||
|
|
||||||
static getContainer(): MiddlewaresContainer {
|
public static getContainer(): MiddlewaresContainer {
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
|
|
||||||
static setup(container: NestContainer) {
|
public static setup(container: NestContainer) {
|
||||||
this.resolver = new MiddlewaresResolver(this.container);
|
this.resolver = new MiddlewaresResolver(this.container);
|
||||||
|
|
||||||
const modules = container.getModules();
|
const modules = container.getModules();
|
||||||
this.resolveMiddlewares(modules);
|
this.resolveMiddlewares(modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
static resolveMiddlewares(modules: Map<string, Module>) {
|
public static resolveMiddlewares(modules: Map<string, Module>) {
|
||||||
modules.forEach((module, name) => {
|
modules.forEach((module, name) => {
|
||||||
const instance = module.instance;
|
const instance = module.instance;
|
||||||
|
|
||||||
@@ -44,10 +46,10 @@ export class MiddlewaresModule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static loadConfiguration(instance: NestModule, module: string) {
|
public static loadConfiguration(instance: NestModule, module: string) {
|
||||||
if (!instance.configure) { return; }
|
if (!instance.configure) return;
|
||||||
|
|
||||||
const middlewaresBuilder = new MiddlewareBuilder();
|
const middlewaresBuilder = new MiddlewareBuilder(this.routesMapper);
|
||||||
instance.configure(middlewaresBuilder);
|
instance.configure(middlewaresBuilder);
|
||||||
if (!(middlewaresBuilder instanceof MiddlewareBuilder))
|
if (!(middlewaresBuilder instanceof MiddlewareBuilder))
|
||||||
return;
|
return;
|
||||||
@@ -56,7 +58,7 @@ export class MiddlewaresModule {
|
|||||||
this.container.addConfig(config, module);
|
this.container.addConfig(config, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
static setupMiddlewares(app: Application) {
|
public static setupMiddlewares(app: Application) {
|
||||||
const configs = this.container.getConfigs();
|
const configs = this.container.getConfigs();
|
||||||
|
|
||||||
configs.forEach((moduleConfigs, module: string) => {
|
configs.forEach((moduleConfigs, module: string) => {
|
||||||
@@ -69,7 +71,7 @@ export class MiddlewaresModule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static setupRouteMiddleware(
|
public static setupRouteMiddleware(
|
||||||
route: ControllerMetadata & { method: RequestMethod },
|
route: ControllerMetadata & { method: RequestMethod },
|
||||||
config: MiddlewareConfiguration,
|
config: MiddlewareConfiguration,
|
||||||
module: string,
|
module: string,
|
||||||
@@ -77,15 +79,16 @@ export class MiddlewaresModule {
|
|||||||
|
|
||||||
const { path, method } = route;
|
const { path, method } = route;
|
||||||
|
|
||||||
[].concat(config.middlewares).map((middlewareMetatype) => {
|
const middlewares = [].concat(config.middlewares);
|
||||||
|
middlewares.map((metatype: Metatype<NestMiddleware>) => {
|
||||||
const collection = this.container.getMiddlewares(module);
|
const collection = this.container.getMiddlewares(module);
|
||||||
const middleware: MiddlewareWrapper = collection.get(middlewareMetatype.name);
|
const middleware = collection.get(metatype.name);
|
||||||
if (isUndefined(middleware)) {
|
if (isUndefined(middleware)) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = middleware.instance;
|
const { instance } = (middleware as MiddlewareWrapper);
|
||||||
this.setupHandler(instance, middlewareMetatype, app, method, path);
|
this.setupHandler(instance, metatype, app, method, path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +103,7 @@ export class MiddlewaresModule {
|
|||||||
throw new InvalidMiddlewareException(metatype.name);
|
throw new InvalidMiddlewareException(metatype.name);
|
||||||
}
|
}
|
||||||
const router = this.routerMethodFactory.get(app, method).bind(app);
|
const router = this.routerMethodFactory.get(app, method).bind(app);
|
||||||
const proxy = this.routerProxy.createProxy(instance.resolve());
|
const proxy = this.routerProxy.createProxy(instance.resolve(), this.exceptionHandler);
|
||||||
|
|
||||||
router(path, proxy);
|
router(path, proxy);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,9 @@ export class MiddlewaresResolver {
|
|||||||
|
|
||||||
constructor(private middlewaresContainer: MiddlewaresContainer) {}
|
constructor(private middlewaresContainer: MiddlewaresContainer) {}
|
||||||
|
|
||||||
resolveInstances(module: Module, moduleName: string) {
|
public resolveInstances(module: Module, moduleName: string) {
|
||||||
const middlewares = this.middlewaresContainer.getMiddlewares(moduleName);
|
const middlewares = this.middlewaresContainer.getMiddlewares(moduleName);
|
||||||
|
middlewares.forEach((wrapper) => this.resolveMiddlewareInstance(wrapper, middlewares, module));
|
||||||
middlewares.forEach((wrapper) => {
|
|
||||||
this.resolveMiddlewareInstance(wrapper, middlewares, module);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveMiddlewareInstance(
|
private resolveMiddlewareInstance(
|
||||||
@@ -23,7 +20,7 @@ export class MiddlewaresResolver {
|
|||||||
this.instanceLoader.loadInstanceOfMiddleware(
|
this.instanceLoader.loadInstanceOfMiddleware(
|
||||||
wrapper,
|
wrapper,
|
||||||
middlewares,
|
middlewares,
|
||||||
module
|
module,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,23 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { RouterBuilder } from '../router/router-builder';
|
import { ExpressRouterExplorer } from '../router/router-explorer';
|
||||||
import { UnkownRequestMappingException } from '../../errors/exceptions/unkown-request-mapping.exception';
|
import { UnkownRequestMappingException } from '../../errors/exceptions/unkown-request-mapping.exception';
|
||||||
import { RequestMethod } from '../../common/enums/request-method.enum';
|
import { RequestMethod } from '../../common/enums/request-method.enum';
|
||||||
import { isUndefined, validatePath } from '../../common/utils/shared.utils';
|
import { isUndefined, validatePath } from '../../common/utils/shared.utils';
|
||||||
import { PATH_METADATA } from '../../common/constants';
|
import { PATH_METADATA } from '../../common/constants';
|
||||||
|
import { MetadataScanner } from '../metadata-scanner';
|
||||||
|
|
||||||
export class RoutesMapper {
|
export class RoutesMapper {
|
||||||
private readonly routerBuilder = new RouterBuilder();
|
private readonly routerExplorer = new ExpressRouterExplorer(new MetadataScanner());
|
||||||
|
|
||||||
mapRouteToRouteProps(routeMetatype) {
|
public mapRouteToRouteProps(routeMetatype) {
|
||||||
const routePath: string = Reflect.getMetadata(PATH_METADATA, routeMetatype);
|
const routePath: string = Reflect.getMetadata(PATH_METADATA, routeMetatype);
|
||||||
if (isUndefined(routePath)) {
|
if (isUndefined(routePath)) {
|
||||||
return [ this.mapObjectToRouteProps(routeMetatype) ];
|
return [this.mapObjectToRouteProps(routeMetatype)];
|
||||||
}
|
}
|
||||||
|
const paths = this.routerExplorer.scanForPaths(Object.create(routeMetatype), routeMetatype.prototype);
|
||||||
const paths = this.routerBuilder.scanForPathsFromPrototype(
|
return paths.map((route) => ({
|
||||||
Object.create(routeMetatype),
|
path: this.validateRoutePath(routePath) + this.validateRoutePath(route.path),
|
||||||
routeMetatype.prototype
|
method: route.requestMethod,
|
||||||
);
|
|
||||||
|
|
||||||
return paths.map((singlePath) => ({
|
|
||||||
path: this.validateRoutePath(routePath) + this.validateRoutePath(singlePath.path),
|
|
||||||
method: singlePath.requestMethod
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,15 +26,13 @@ export class RoutesMapper {
|
|||||||
if (isUndefined(path)) {
|
if (isUndefined(path)) {
|
||||||
throw new UnkownRequestMappingException();
|
throw new UnkownRequestMappingException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: this.validateRoutePath(path),
|
path: this.validateRoutePath(path),
|
||||||
method: (isUndefined(method)) ? RequestMethod.ALL : method
|
method: isUndefined(method) ? RequestMethod.ALL : method,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private validateRoutePath(path: string): string {
|
private validateRoutePath(path: string): string {
|
||||||
return validatePath(path);
|
return validatePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { Controller } from '../../../common/interfaces/controller.interface';
|
||||||
|
import { ExceptionsHandler } from '../../exceptions/exceptions-handler';
|
||||||
|
|
||||||
|
export interface ExceptionsFilter {
|
||||||
|
create(instance: Controller, moduleName: string): ExceptionsHandler;
|
||||||
|
}
|
||||||
6
src/core/router/interfaces/explorer.inteface.ts
Normal file
6
src/core/router/interfaces/explorer.inteface.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { Controller } from '../../../common/interfaces/index';
|
||||||
|
import { Metatype } from '../../../common/interfaces/metatype.interface';
|
||||||
|
|
||||||
|
export interface RouterExplorer {
|
||||||
|
explore(instance: Controller, metatype: Metatype<Controller>, moduleName: string);
|
||||||
|
}
|
||||||
5
src/core/router/interfaces/resolver.interface.ts
Normal file
5
src/core/router/interfaces/resolver.interface.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Application } from 'express';
|
||||||
|
|
||||||
|
export interface Resolver {
|
||||||
|
resolve(express: Application);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum';
|
||||||
|
|
||||||
|
export interface IRouteParamsFactory {
|
||||||
|
exchangeKeyForValue(key: RouteParamtypes, data, { req, res, next });
|
||||||
|
}
|
||||||
18
src/core/router/route-params-factory.ts
Normal file
18
src/core/router/route-params-factory.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { RouteParamtypes } from '../../common/enums/route-paramtypes.enum';
|
||||||
|
import { IRouteParamsFactory } from './interfaces/route-params-factory.interface';
|
||||||
|
|
||||||
|
export class RouteParamsFactory implements IRouteParamsFactory {
|
||||||
|
public exchangeKeyForValue(key: RouteParamtypes, data, { req, res, next }) {
|
||||||
|
switch (key) {
|
||||||
|
case RouteParamtypes.NEXT: return next;
|
||||||
|
case RouteParamtypes.REQUEST: return req;
|
||||||
|
case RouteParamtypes.RESPONSE: return res;
|
||||||
|
case RouteParamtypes.BODY: return data ? req.body[data] : req.body;
|
||||||
|
case RouteParamtypes.PARAM: return data ? req.params[data] : req.params;
|
||||||
|
case RouteParamtypes.QUERY: return data ? req.query[data] : req.query;
|
||||||
|
case RouteParamtypes.HEADERS: return data ? req.headers[data] : req.headers;
|
||||||
|
case RouteParamtypes.SESSION: return req.session;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
import 'reflect-metadata';
|
|
||||||
import { Controller } from '../../common/interfaces/controller.interface';
|
|
||||||
import { RequestMethod } from '../../common/enums/request-method.enum';
|
|
||||||
import { RouterProxy, RouterProxyCallback } from './router-proxy';
|
|
||||||
import { UnkownRequestMappingException } from '../../errors/exceptions/unkown-request-mapping.exception';
|
|
||||||
import { ExpressAdapter } from '../adapters/express-adapter';
|
|
||||||
import { Metatype } from '../../common/interfaces/metatype.interface';
|
|
||||||
import { isUndefined, isConstructor, validatePath, isFunction } from '../../common/utils/shared.utils';
|
|
||||||
import { RouterMethodFactory } from '../helpers/router-method-factory';
|
|
||||||
import { PATH_METADATA, METHOD_METADATA } from '../../common/constants';
|
|
||||||
import { Logger } from '../../common/services/logger.service';
|
|
||||||
import { getRouteMappedMessage } from '../helpers/messages';
|
|
||||||
|
|
||||||
export class RouterBuilder {
|
|
||||||
private readonly routerMethodFactory = new RouterMethodFactory();
|
|
||||||
private readonly logger = new Logger(RouterBuilder.name);
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private routerProxy?: RouterProxy,
|
|
||||||
private expressAdapter?: ExpressAdapter) {}
|
|
||||||
|
|
||||||
public build(instance: Controller, metatype: Metatype<Controller>) {
|
|
||||||
const router = (<any>this.expressAdapter).createRouter();
|
|
||||||
const path = this.fetchRouterPath(metatype);
|
|
||||||
const routerPaths = this.scanForPaths(instance);
|
|
||||||
|
|
||||||
this.applyPathsToRouterProxy(router, routerPaths);
|
|
||||||
|
|
||||||
return { path, router };
|
|
||||||
}
|
|
||||||
|
|
||||||
scanForPaths(instance: Controller): RoutePathProperties[] {
|
|
||||||
const instancePrototype = Object.getPrototypeOf(instance);
|
|
||||||
return this.scanForPathsFromPrototype(instance, instancePrototype);
|
|
||||||
}
|
|
||||||
|
|
||||||
scanForPathsFromPrototype(instance: Controller, instancePrototype) {
|
|
||||||
return Object.getOwnPropertyNames(instancePrototype)
|
|
||||||
.filter((method) => {
|
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(instancePrototype, method);
|
|
||||||
if (descriptor.set || descriptor.get) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !isConstructor(method) && isFunction(instancePrototype[method]);
|
|
||||||
})
|
|
||||||
.map((methodName) => this.exploreMethodMetadata(instance, instancePrototype, methodName))
|
|
||||||
.filter((path) => path !== null);
|
|
||||||
}
|
|
||||||
|
|
||||||
exploreMethodMetadata(instance: Controller, instancePrototype, methodName: string): RoutePathProperties {
|
|
||||||
const callbackMethod = instancePrototype[methodName];
|
|
||||||
|
|
||||||
const routePath = Reflect.getMetadata(PATH_METADATA, callbackMethod);
|
|
||||||
if (isUndefined(routePath)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestMethod: RequestMethod = Reflect.getMetadata(METHOD_METADATA, callbackMethod);
|
|
||||||
return {
|
|
||||||
targetCallback: (<Function>callbackMethod).bind(instance),
|
|
||||||
path: this.validateRoutePath(routePath),
|
|
||||||
requestMethod,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
applyPathsToRouterProxy(router, routePaths: RoutePathProperties[]) {
|
|
||||||
(routePaths || []).map((pathProperties) => {
|
|
||||||
this.bindMethodToRouterProxy(router, pathProperties);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private bindMethodToRouterProxy(router, pathProperties: RoutePathProperties) {
|
|
||||||
const { path, requestMethod, targetCallback } = pathProperties;
|
|
||||||
|
|
||||||
const routerMethod = this.routerMethodFactory.get(router, requestMethod).bind(router);
|
|
||||||
const proxy = this.routerProxy.createProxy(targetCallback);
|
|
||||||
routerMethod(path, proxy);
|
|
||||||
|
|
||||||
this.logger.log(getRouteMappedMessage(path, requestMethod));
|
|
||||||
}
|
|
||||||
|
|
||||||
private fetchRouterPath(metatype: Metatype<Controller>) {
|
|
||||||
const path = Reflect.getMetadata(PATH_METADATA, metatype);
|
|
||||||
return this.validateRoutePath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private validateRoutePath(path: string): string {
|
|
||||||
if (isUndefined(path)) {
|
|
||||||
throw new UnkownRequestMappingException();
|
|
||||||
}
|
|
||||||
return validatePath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RoutePathProperties {
|
|
||||||
path: string,
|
|
||||||
requestMethod: RequestMethod,
|
|
||||||
targetCallback: RouterProxyCallback,
|
|
||||||
}
|
|
||||||
58
src/core/router/router-exception-filters.ts
Normal file
58
src/core/router/router-exception-filters.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { Controller } from '../../common/interfaces/controller.interface';
|
||||||
|
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
||||||
|
import { EXCEPTION_FILTERS_METADATA, FILTER_CATCH_EXCEPTIONS } from '../../common/constants';
|
||||||
|
import { isEmpty, isFunction } from '../../common/utils/shared.utils';
|
||||||
|
import { Metatype } from '../../common/interfaces/index';
|
||||||
|
import { ExceptionFilterMetadata } from '../../common/interfaces/exception-filter-metadata.interface';
|
||||||
|
import { NestContainer } from '../injector/container';
|
||||||
|
import { UnkownModuleException } from '../../errors/exceptions/unkown-module.exception';
|
||||||
|
import { ExceptionFilter } from '../../common/interfaces/exception-filter.interface';
|
||||||
|
|
||||||
|
export class RouterExceptionFilters {
|
||||||
|
constructor(private container: NestContainer) {}
|
||||||
|
|
||||||
|
public create(instance: Controller, moduleName: string): ExceptionsHandler {
|
||||||
|
const exceptionHandler = new ExceptionsHandler();
|
||||||
|
const filters = this.reflectExceptionFilters(instance);
|
||||||
|
if (isEmpty(filters)) {
|
||||||
|
return exceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filtersHooks = this.resolveFiltersMetatypes(filters, moduleName);
|
||||||
|
exceptionHandler.setCustomFilters(filtersHooks);
|
||||||
|
return exceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reflectExceptionFilters(instance: Controller): Metatype<any>[] {
|
||||||
|
const prototype = Object.getPrototypeOf(instance);
|
||||||
|
return Reflect.getMetadata(EXCEPTION_FILTERS_METADATA, prototype.constructor) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public resolveFiltersMetatypes(filters: Metatype<any>[], moduleName: string): ExceptionFilterMetadata[] {
|
||||||
|
return filters.filter(metatype => isFunction(metatype))
|
||||||
|
.map(metatype => ({
|
||||||
|
instance: this.findExceptionsFilterInstance(metatype, moduleName),
|
||||||
|
metatype,
|
||||||
|
}))
|
||||||
|
.filter(({ instance }) => instance.catch && isFunction(instance.catch))
|
||||||
|
.map(({ instance, metatype }) => ({
|
||||||
|
func: instance.catch.bind(instance),
|
||||||
|
exceptionMetatypes: this.reflectCatchExceptions(metatype),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public findExceptionsFilterInstance(metatype: Metatype<any>, moduleName: string): ExceptionFilter {
|
||||||
|
const modules = this.container.getModules();
|
||||||
|
if (!modules.has(moduleName)) {
|
||||||
|
throw new UnkownModuleException();
|
||||||
|
}
|
||||||
|
const { components } = modules.get(moduleName);
|
||||||
|
const { instance } = components.get(metatype.name);
|
||||||
|
return instance as ExceptionFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reflectCatchExceptions(metatype: Metatype<Controller>): Metatype<any>[] {
|
||||||
|
return Reflect.getMetadata(FILTER_CATCH_EXCEPTIONS, metatype) || [];
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/core/router/router-execution-context.ts
Normal file
51
src/core/router/router-execution-context.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { ROUTE_ARGS_METADATA } from '../../common/constants';
|
||||||
|
import { isUndefined } from '../../common/utils/shared.utils';
|
||||||
|
import { RouteParamtypes } from '../../common/enums/route-paramtypes.enum';
|
||||||
|
import { Controller } from '../../common/interfaces';
|
||||||
|
import { RouteParamsMetadata } from '../../index';
|
||||||
|
import { IRouteParamsFactory } from './interfaces/route-params-factory.interface';
|
||||||
|
|
||||||
|
export interface IndexValuePair {
|
||||||
|
index: number;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RouterExecutionContext {
|
||||||
|
constructor(private paramsFactory: IRouteParamsFactory) {}
|
||||||
|
|
||||||
|
public create(instance: Controller, callback: (...args) => any) {
|
||||||
|
const metadata = this.reflectCallbackMetadata(instance, callback);
|
||||||
|
if (isUndefined(metadata)) {
|
||||||
|
return callback.bind(instance);
|
||||||
|
}
|
||||||
|
const keys = Object.keys(metadata).map(Number);
|
||||||
|
const argsLength = this.getArgumentsLength(keys, metadata);
|
||||||
|
const args = this.createNullArray(argsLength);
|
||||||
|
|
||||||
|
return (req, res, next) => {
|
||||||
|
const indexValuePairs = this.exchangeKeysForValues(keys, metadata, { req, res, next });
|
||||||
|
indexValuePairs.forEach(pair => args[pair.index] = pair.value);
|
||||||
|
return callback.apply(instance, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public reflectCallbackMetadata(instance: Controller, callback: (...args) => any): RouteParamsMetadata {
|
||||||
|
return Reflect.getMetadata(ROUTE_ARGS_METADATA, instance, callback.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getArgumentsLength(keys: RouteParamtypes[], metadata: RouteParamsMetadata): number {
|
||||||
|
return Math.max(...keys.map(key => metadata[key].index)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createNullArray(length: number): any[] {
|
||||||
|
return Array.apply(null, { length }).fill(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public exchangeKeysForValues(keys: RouteParamtypes[], metadata: RouteParamsMetadata, { req, res, next }): IndexValuePair[] {
|
||||||
|
return keys.map(key => ({
|
||||||
|
index: metadata[key].index,
|
||||||
|
value: this.paramsFactory.exchangeKeyForValue(key, metadata[key].data, { req, res, next }),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/core/router/router-explorer.ts
Normal file
103
src/core/router/router-explorer.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { Controller } from '../../common/interfaces/controller.interface';
|
||||||
|
import { RequestMethod } from '../../common/enums/request-method.enum';
|
||||||
|
import { RouterProxy, RouterProxyCallback } from './router-proxy';
|
||||||
|
import { UnkownRequestMappingException } from '../../errors/exceptions/unkown-request-mapping.exception';
|
||||||
|
import { ExpressAdapter } from '../adapters/express-adapter';
|
||||||
|
import { Metatype } from '../../common/interfaces/metatype.interface';
|
||||||
|
import { isUndefined, validatePath } from '../../common/utils/shared.utils';
|
||||||
|
import { RouterMethodFactory } from '../helpers/router-method-factory';
|
||||||
|
import { PATH_METADATA, METHOD_METADATA } from '../../common/constants';
|
||||||
|
import { Logger } from '../../common/services/logger.service';
|
||||||
|
import { RouteMappedMessage } from '../helpers/messages';
|
||||||
|
import { RouterExecutionContext } from './router-execution-context';
|
||||||
|
import { ExceptionsFilter } from './interfaces/exceptions-filter.interface';
|
||||||
|
import { RouteParamsFactory } from './route-params-factory';
|
||||||
|
import { RouterExplorer } from './interfaces/explorer.inteface';
|
||||||
|
import { MetadataScanner } from '../metadata-scanner';
|
||||||
|
|
||||||
|
export class ExpressRouterExplorer implements RouterExplorer {
|
||||||
|
private readonly executionContextCreator = new RouterExecutionContext(new RouteParamsFactory());
|
||||||
|
private readonly routerMethodFactory = new RouterMethodFactory();
|
||||||
|
private readonly logger = new Logger('RouterExplorer');
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private metadataScanner?: MetadataScanner,
|
||||||
|
private routerProxy?: RouterProxy,
|
||||||
|
private expressAdapter?: ExpressAdapter,
|
||||||
|
private exceptionsFilter?: ExceptionsFilter) {}
|
||||||
|
|
||||||
|
public explore(instance: Controller, metatype: Metatype<Controller>, moduleName: string) {
|
||||||
|
const router = (this.expressAdapter as any).createRouter();
|
||||||
|
const path = this.fetchRouterPath(metatype);
|
||||||
|
const routerPaths = this.scanForPaths(instance);
|
||||||
|
|
||||||
|
this.applyPathsToRouterProxy(router, routerPaths, instance, moduleName);
|
||||||
|
return { path, router };
|
||||||
|
}
|
||||||
|
|
||||||
|
public scanForPaths(instance: Controller, prototype?): RoutePathProperties[] {
|
||||||
|
const instancePrototype = isUndefined(prototype) ? Object.getPrototypeOf(instance) : prototype;
|
||||||
|
return this.metadataScanner.scanFromPrototype<Controller, RoutePathProperties>(
|
||||||
|
instance,
|
||||||
|
instancePrototype,
|
||||||
|
(method) => this.exploreMethodMetadata(instance, instancePrototype, method),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public exploreMethodMetadata(instance: Controller, instancePrototype, methodName: string): RoutePathProperties {
|
||||||
|
const targetCallback = instancePrototype[methodName];
|
||||||
|
const routePath = Reflect.getMetadata(PATH_METADATA, targetCallback);
|
||||||
|
if (isUndefined(routePath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestMethod: RequestMethod = Reflect.getMetadata(METHOD_METADATA, targetCallback);
|
||||||
|
return {
|
||||||
|
targetCallback,
|
||||||
|
requestMethod,
|
||||||
|
path: this.validateRoutePath(routePath),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public applyPathsToRouterProxy(router, routePaths: RoutePathProperties[], instance: Controller, moduleName: string) {
|
||||||
|
(routePaths || []).map((pathProperties) => {
|
||||||
|
const { path, requestMethod } = pathProperties;
|
||||||
|
this.applyCallbackToRouter(router, pathProperties, instance, moduleName);
|
||||||
|
this.logger.log(RouteMappedMessage(path, requestMethod));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyCallbackToRouter(router, pathProperties: RoutePathProperties, instance: Controller, moduleName: string) {
|
||||||
|
const { path, requestMethod, targetCallback } = pathProperties;
|
||||||
|
|
||||||
|
const routerMethod = this.routerMethodFactory.get(router, requestMethod).bind(router);
|
||||||
|
const proxy = this.createCallbackProxy(instance, targetCallback, moduleName);
|
||||||
|
routerMethod(path, proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createCallbackProxy(instance: Controller, callback: RoutePathProperties['targetCallback'], moduleName: string) {
|
||||||
|
const executionContext = this.executionContextCreator.create(instance, callback);
|
||||||
|
const exceptionFilter = this.exceptionsFilter.create(instance, moduleName);
|
||||||
|
|
||||||
|
return this.routerProxy.createProxy(executionContext, exceptionFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchRouterPath(metatype: Metatype<Controller>) {
|
||||||
|
const path = Reflect.getMetadata(PATH_METADATA, metatype);
|
||||||
|
return this.validateRoutePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateRoutePath(path: string): string {
|
||||||
|
if (isUndefined(path)) {
|
||||||
|
throw new UnkownRequestMappingException();
|
||||||
|
}
|
||||||
|
return validatePath(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoutePathProperties {
|
||||||
|
path: string;
|
||||||
|
requestMethod: RequestMethod;
|
||||||
|
targetCallback: RouterProxyCallback;
|
||||||
|
}
|
||||||
@@ -1,27 +1,23 @@
|
|||||||
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
||||||
|
|
||||||
|
export type RouterProxyCallback = (req?, res?, next?) => void;
|
||||||
|
|
||||||
export class RouterProxy {
|
export class RouterProxy {
|
||||||
|
public createProxy(
|
||||||
constructor(private exceptionsHandler: ExceptionsHandler) {}
|
targetCallback: RouterProxyCallback,
|
||||||
|
exceptionsHandler: ExceptionsHandler,
|
||||||
createProxy(targetCallback: RouterProxyCallback) {
|
) {
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
Promise
|
Promise.resolve(targetCallback(req, res, next))
|
||||||
.resolve(targetCallback(req, res, next))
|
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
exceptionsHandler.next(e, res);
|
||||||
this.exceptionsHandler.next(e, res);
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch(e) {
|
catch (e) {
|
||||||
this.exceptionsHandler.next(e, res);
|
exceptionsHandler.next(e, res);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RouterProxyCallback {
|
|
||||||
(req?, res?, next?): void;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,34 +1,45 @@
|
|||||||
import { Application } from 'express';
|
import { Application } from 'express';
|
||||||
import { NestContainer, InstanceWrapper } from '../injector/container';
|
import { NestContainer, InstanceWrapper } from '../injector/container';
|
||||||
import { RouterBuilder } from './router-builder';
|
|
||||||
import { RouterProxy } from './router-proxy';
|
import { RouterProxy } from './router-proxy';
|
||||||
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
|
||||||
import { Controller } from '../../common/interfaces/controller.interface';
|
import { Controller } from '../../common/interfaces/controller.interface';
|
||||||
import { Logger } from '../../common/services/logger.service';
|
import { Logger } from '../../common/services/logger.service';
|
||||||
import { getControllerMappingMessage } from '../helpers/messages';
|
import { ControllerMappingMessage } from '../helpers/messages';
|
||||||
|
import { Resolver } from './interfaces/resolver.interface';
|
||||||
|
import { RouterExceptionFilters } from './router-exception-filters';
|
||||||
|
import { MetadataScanner } from '../metadata-scanner';
|
||||||
|
import { RouterExplorer } from './interfaces/explorer.inteface';
|
||||||
|
import { ExpressRouterExplorer } from './router-explorer';
|
||||||
|
|
||||||
export class RoutesResolver {
|
export class RoutesResolver implements Resolver {
|
||||||
private readonly logger = new Logger(RoutesResolver.name);
|
private readonly logger = new Logger(RoutesResolver.name);
|
||||||
private readonly routerProxy = new RouterProxy(new ExceptionsHandler());
|
private readonly routerProxy = new RouterProxy();
|
||||||
private routerBuilder: RouterBuilder;
|
private readonly routerExceptionsFilter: RouterExceptionFilters;
|
||||||
|
private readonly routerBuilder: RouterExplorer;
|
||||||
|
|
||||||
constructor(private container: NestContainer, expressAdapter) {
|
constructor(private container: NestContainer, expressAdapter) {
|
||||||
this.routerBuilder = new RouterBuilder(this.routerProxy, expressAdapter);
|
this.routerExceptionsFilter = new RouterExceptionFilters(container);
|
||||||
|
this.routerBuilder = new ExpressRouterExplorer(
|
||||||
|
new MetadataScanner(),
|
||||||
|
this.routerProxy,
|
||||||
|
expressAdapter,
|
||||||
|
this.routerExceptionsFilter,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(express: Application) {
|
public resolve(express: Application) {
|
||||||
const modules = this.container.getModules();
|
const modules = this.container.getModules();
|
||||||
modules.forEach(({ routes }) => this.setupRouters(routes, express));
|
modules.forEach(({ routes }, moduleName) => this.setupRouters(routes, moduleName, express));
|
||||||
}
|
}
|
||||||
|
|
||||||
setupRouters(
|
public setupRouters(
|
||||||
routes: Map<string, InstanceWrapper<Controller>>,
|
routes: Map<string, InstanceWrapper<Controller>>,
|
||||||
|
moduleName: string,
|
||||||
express: Application) {
|
express: Application) {
|
||||||
|
|
||||||
routes.forEach(({ instance, metatype }) => {
|
routes.forEach(({ instance, metatype }) => {
|
||||||
this.logger.log(getControllerMappingMessage(metatype.name));
|
this.logger.log(ControllerMappingMessage(metatype.name));
|
||||||
|
|
||||||
const { path, router } = this.routerBuilder.build(instance, metatype);
|
const { path, router } = this.routerBuilder.explore(instance, metatype, moduleName);
|
||||||
express.use(path, router);
|
express.use(path, router);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ import 'reflect-metadata';
|
|||||||
import { NestContainer } from './injector/container';
|
import { NestContainer } from './injector/container';
|
||||||
import { Controller } from '../common/interfaces/controller.interface';
|
import { Controller } from '../common/interfaces/controller.interface';
|
||||||
import { Injectable } from '../common/interfaces/injectable.interface';
|
import { Injectable } from '../common/interfaces/injectable.interface';
|
||||||
import { metadata } from '../common/constants';
|
import { metadata, EXCEPTION_FILTERS_METADATA } from '../common/constants';
|
||||||
import { NestModuleMetatype } from '../common/interfaces/module-metatype.interface';
|
import { NestModuleMetatype } from '../common/interfaces/module-metatype.interface';
|
||||||
import { Metatype } from '../common/interfaces/metatype.interface';
|
import { Metatype } from '../common/interfaces/metatype.interface';
|
||||||
|
import { GATEWAY_MIDDLEWARES } from '../websockets/constants';
|
||||||
|
|
||||||
export class DependenciesScanner {
|
export class DependenciesScanner {
|
||||||
|
|
||||||
constructor(private container: NestContainer) {}
|
constructor(private container: NestContainer) {}
|
||||||
|
|
||||||
scan(module: NestModuleMetatype) {
|
public scan(module: NestModuleMetatype) {
|
||||||
this.scanForModules(module);
|
this.scanForModules(module);
|
||||||
this.scanModulesForDependencies();
|
this.scanModulesForDependencies();
|
||||||
}
|
}
|
||||||
@@ -18,8 +18,8 @@ export class DependenciesScanner {
|
|||||||
private scanForModules(module: NestModuleMetatype) {
|
private scanForModules(module: NestModuleMetatype) {
|
||||||
this.storeModule(module);
|
this.storeModule(module);
|
||||||
|
|
||||||
const innerModules = this.reflectMetadata(module, metadata.MODULES);
|
const importedModules = this.reflectMetadata(module, metadata.MODULES);
|
||||||
innerModules.map((module) => this.scanForModules(module));
|
importedModules.map((imported) => this.scanForModules(imported));
|
||||||
}
|
}
|
||||||
|
|
||||||
private storeModule(module: NestModuleMetatype) {
|
private storeModule(module: NestModuleMetatype) {
|
||||||
@@ -44,12 +44,18 @@ export class DependenciesScanner {
|
|||||||
|
|
||||||
private reflectComponents(module: NestModuleMetatype) {
|
private reflectComponents(module: NestModuleMetatype) {
|
||||||
const components = this.reflectMetadata(module, metadata.COMPONENTS);
|
const components = this.reflectMetadata(module, metadata.COMPONENTS);
|
||||||
components.map((component) => this.storeComponent(component, module));
|
components.map((component) => {
|
||||||
|
this.storeComponent(component, module);
|
||||||
|
this.reflectGatewaysMiddlewares(component, module);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private reflectControllers(module: NestModuleMetatype) {
|
private reflectControllers(module: NestModuleMetatype) {
|
||||||
const routes = this.reflectMetadata(module, metadata.CONTROLLERS);
|
const routes = this.reflectMetadata(module, metadata.CONTROLLERS);
|
||||||
routes.map((route) => this.storeRoute(route, module));
|
routes.map((route) => {
|
||||||
|
this.storeRoute(route, module);
|
||||||
|
this.reflectExceptionFilters(route, module);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private reflectExports(module: NestModuleMetatype) {
|
private reflectExports(module: NestModuleMetatype) {
|
||||||
@@ -57,6 +63,16 @@ export class DependenciesScanner {
|
|||||||
exports.map((exportedComponent) => this.storeExportedComponent(exportedComponent, module));
|
exports.map((exportedComponent) => this.storeExportedComponent(exportedComponent, module));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private reflectExceptionFilters(component: Metatype<Injectable>, module: NestModuleMetatype) {
|
||||||
|
const filters = this.reflectMetadata(component, EXCEPTION_FILTERS_METADATA);
|
||||||
|
filters.map((filter) => this.storeComponent(filter, module));
|
||||||
|
}
|
||||||
|
|
||||||
|
private reflectGatewaysMiddlewares(component: Metatype<Injectable>, module: NestModuleMetatype) {
|
||||||
|
const middlewares = this.reflectMetadata(component, GATEWAY_MIDDLEWARES);
|
||||||
|
middlewares.map((middleware) => this.storeComponent(middleware, module));
|
||||||
|
}
|
||||||
|
|
||||||
private storeRelatedModule(related: NestModuleMetatype, module: NestModuleMetatype) {
|
private storeRelatedModule(related: NestModuleMetatype, module: NestModuleMetatype) {
|
||||||
this.container.addRelatedModule(related, module);
|
this.container.addRelatedModule(related, module);
|
||||||
}
|
}
|
||||||
@@ -72,7 +88,7 @@ export class DependenciesScanner {
|
|||||||
private storeRoute(route: Metatype<Controller>, module: NestModuleMetatype) {
|
private storeRoute(route: Metatype<Controller>, module: NestModuleMetatype) {
|
||||||
this.container.addController(route, module);
|
this.container.addController(route, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
private reflectMetadata(module: NestModuleMetatype, metadata: string) {
|
private reflectMetadata(module: NestModuleMetatype, metadata: string) {
|
||||||
return Reflect.getMetadata(metadata, module) || [];
|
return Reflect.getMetadata(metadata, module) || [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { expect } from 'chai';
|
|||||||
import { ExceptionsHandler } from '../../exceptions/exceptions-handler';
|
import { ExceptionsHandler } from '../../exceptions/exceptions-handler';
|
||||||
import { HttpException } from '../../exceptions/http-exception';
|
import { HttpException } from '../../exceptions/http-exception';
|
||||||
import { Logger } from '../../../common/services/logger.service';
|
import { Logger } from '../../../common/services/logger.service';
|
||||||
import { NestMode } from '../../../common/enums/nest-mode.enum';
|
import { NestEnvironment } from '../../../common/enums/nest-environment.enum';
|
||||||
|
import { InvalidExceptionFilterException } from '../../../errors/exceptions/invalid-exception-filter.exception';
|
||||||
|
|
||||||
describe('ExceptionsHandler', () => {
|
describe('ExceptionsHandler', () => {
|
||||||
let handler: ExceptionsHandler;
|
let handler: ExceptionsHandler;
|
||||||
@@ -11,7 +12,7 @@ describe('ExceptionsHandler', () => {
|
|||||||
let jsonStub: sinon.SinonStub;
|
let jsonStub: sinon.SinonStub;
|
||||||
let response;
|
let response;
|
||||||
|
|
||||||
before(() => Logger.setMode(NestMode.TEST));
|
before(() => Logger.setMode(NestEnvironment.TEST));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
handler = new ExceptionsHandler();
|
handler = new ExceptionsHandler();
|
||||||
@@ -20,31 +21,106 @@ describe('ExceptionsHandler', () => {
|
|||||||
|
|
||||||
response = {
|
response = {
|
||||||
status: statusStub,
|
status: statusStub,
|
||||||
json: jsonStub
|
json: jsonStub,
|
||||||
};
|
};
|
||||||
response.status.returns(response);
|
response.status.returns(response);
|
||||||
response.json.returns(response);
|
response.json.returns(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('next', () => {
|
describe('next', () => {
|
||||||
|
|
||||||
it('should method send expected response status code and message when exception is unknown', () => {
|
it('should method send expected response status code and message when exception is unknown', () => {
|
||||||
handler.next(new Error(), response);
|
handler.next(new Error(), response);
|
||||||
|
|
||||||
expect(statusStub.calledWith(500)).to.be.true;
|
expect(statusStub.calledWith(500)).to.be.true;
|
||||||
expect(jsonStub.calledWith({ message: 'Unkown exception' })).to.be.true;
|
expect(jsonStub.calledWith({ message: 'Unkown exception' })).to.be.true;
|
||||||
});
|
});
|
||||||
|
describe('when exception is instance of HttpException', () => {
|
||||||
|
it('should method send expected response status code and json object', () => {
|
||||||
|
const status = 401;
|
||||||
|
const message = {
|
||||||
|
custom: 'Unauthorized',
|
||||||
|
};
|
||||||
|
handler.next(new HttpException(message, status), response);
|
||||||
|
|
||||||
it('should method send expected response status code and message when exception is instance of HttpException', () => {
|
expect(statusStub.calledWith(status)).to.be.true;
|
||||||
const status = 401;
|
expect(jsonStub.calledWith(message)).to.be.true;
|
||||||
const message = 'Unauthorized';
|
});
|
||||||
|
it('should method send expected response status code and transform message to json', () => {
|
||||||
|
const status = 401;
|
||||||
|
const message = 'Unauthorized';
|
||||||
|
|
||||||
handler.next(new HttpException(message, status), response);
|
handler.next(new HttpException(message, status), response);
|
||||||
|
|
||||||
expect(statusStub.calledWith(status)).to.be.true;
|
expect(statusStub.calledWith(status)).to.be.true;
|
||||||
expect(jsonStub.calledWith({ message })).to.be.true;
|
expect(jsonStub.calledWith({ message })).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when "invokeCustomFilters" returns true', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
sinon.stub(handler, 'invokeCustomFilters').returns(true);
|
||||||
|
});
|
||||||
|
it('should not call status and json stubs', () => {
|
||||||
|
expect(statusStub.notCalled).to.be.true;
|
||||||
|
expect(jsonStub.notCalled).to.be.true;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
describe('setCustomFilters', () => {
|
||||||
|
const filters = [ 'test', 'test2' ];
|
||||||
|
it('should set custom filters', () => {
|
||||||
|
handler.setCustomFilters(filters as any);
|
||||||
|
expect((handler as any).filters).to.be.eql(filters);
|
||||||
|
});
|
||||||
|
it('should throws exception when passed argument is not an array', () => {
|
||||||
|
expect(
|
||||||
|
() => handler.setCustomFilters(null),
|
||||||
|
).to.throws(InvalidExceptionFilterException);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('invokeCustomFilters', () => {
|
||||||
|
describe('when filters array is empty', () => {
|
||||||
|
it('should returns false', () => {
|
||||||
|
expect(handler.invokeCustomFilters(null, null)).to.be.false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when filters array is not empty', () => {
|
||||||
|
let filters, funcSpy;
|
||||||
|
class TestException {}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
funcSpy = sinon.spy();
|
||||||
|
});
|
||||||
|
describe('when filter exists in filters array', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
filters = [
|
||||||
|
{ exceptionMetatypes: [ TestException ], func: funcSpy },
|
||||||
|
];
|
||||||
|
(handler as any).filters = filters;
|
||||||
|
});
|
||||||
|
it('should call funcSpy', () => {
|
||||||
|
handler.invokeCustomFilters(new TestException(), null);
|
||||||
|
expect(funcSpy.notCalled).to.be.false;
|
||||||
|
});
|
||||||
|
it('should call funcSpy with exception and response passed as an arguments', () => {
|
||||||
|
const exception = new TestException();
|
||||||
|
const res = { foo: 'bar' };
|
||||||
|
|
||||||
|
handler.invokeCustomFilters(exception, res);
|
||||||
|
expect(funcSpy.calledWith(exception, res)).to.be.true;
|
||||||
|
});
|
||||||
|
it('should returns true', () => {
|
||||||
|
expect(handler.invokeCustomFilters(new TestException(), null)).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when filter does not exists in filters array', () => {
|
||||||
|
it('should not call funcSpy', () => {
|
||||||
|
handler.invokeCustomFilters(new TestException(), null);
|
||||||
|
expect(funcSpy.notCalled).to.be.true;
|
||||||
|
});
|
||||||
|
it('should returns false', () => {
|
||||||
|
expect(handler.invokeCustomFilters(new TestException(), null)).to.be.false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -4,7 +4,7 @@ import { RequestMethod } from '../../../common/enums/request-method.enum';
|
|||||||
|
|
||||||
describe('RouterMethodFactory', () => {
|
describe('RouterMethodFactory', () => {
|
||||||
let factory: RouterMethodFactory;
|
let factory: RouterMethodFactory;
|
||||||
let target = {
|
const target = {
|
||||||
get: () => {},
|
get: () => {},
|
||||||
post: () => {},
|
post: () => {},
|
||||||
all: () => {},
|
all: () => {},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import * as sinon from 'sinon';
|
import * as sinon from 'sinon';
|
||||||
import { NestContainer } from '../../injector/container';
|
import { NestContainer } from '../../injector/container';
|
||||||
import { Module } from '../../../common/utils/module.decorator';
|
import { Module } from '../../../common/utils/decorators/module.decorator';
|
||||||
import { UnkownModuleException } from '../../../errors/exceptions/unkown-module.exception';
|
import { UnkownModuleException } from '../../../errors/exceptions/unkown-module.exception';
|
||||||
|
|
||||||
describe('NestContainer', () => {
|
describe('NestContainer', () => {
|
||||||
@@ -17,7 +17,7 @@ describe('NestContainer', () => {
|
|||||||
it('should not add module if already exists in collection', () => {
|
it('should not add module if already exists in collection', () => {
|
||||||
const modules = new Map();
|
const modules = new Map();
|
||||||
const setSpy = sinon.spy(modules, 'set');
|
const setSpy = sinon.spy(modules, 'set');
|
||||||
(container as any)['modules'] = modules;
|
(container as any).modules = modules;
|
||||||
|
|
||||||
container.addModule(TestModule);
|
container.addModule(TestModule);
|
||||||
container.addModule(TestModule);
|
container.addModule(TestModule);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as sinon from 'sinon';
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { InstanceWrapper } from '../../injector/container';
|
import { InstanceWrapper } from '../../injector/container';
|
||||||
import { Injector } from '../../injector/injector';
|
import { Injector } from '../../injector/injector';
|
||||||
import { Component } from '../../../common/utils/component.decorator';
|
import { Component } from '../../../common/utils/decorators/component.decorator';
|
||||||
import { RuntimeException } from '../../../errors/exceptions/runtime.exception';
|
import { RuntimeException } from '../../../errors/exceptions/runtime.exception';
|
||||||
import { Module } from '../../injector/module';
|
import { Module } from '../../injector/module';
|
||||||
import { UnkownDependenciesException } from '../../../errors/exceptions/unkown-dependencies.exception';
|
import { UnkownDependenciesException } from '../../../errors/exceptions/unkown-dependencies.exception';
|
||||||
@@ -38,19 +38,19 @@ describe('Injector', () => {
|
|||||||
name: 'MainTest',
|
name: 'MainTest',
|
||||||
metatype: MainTest,
|
metatype: MainTest,
|
||||||
instance: Object.create(MainTest.prototype),
|
instance: Object.create(MainTest.prototype),
|
||||||
isResolved: false
|
isResolved: false,
|
||||||
};
|
};
|
||||||
depOne = {
|
depOne = {
|
||||||
name: 'DependencyOne',
|
name: 'DependencyOne',
|
||||||
metatype: DependencyOne,
|
metatype: DependencyOne,
|
||||||
instance: Object.create(DependencyOne.prototype),
|
instance: Object.create(DependencyOne.prototype),
|
||||||
isResolved: false
|
isResolved: false,
|
||||||
};
|
};
|
||||||
depTwo = {
|
depTwo = {
|
||||||
name: 'DependencyTwo',
|
name: 'DependencyTwo',
|
||||||
metatype: DependencyTwo,
|
metatype: DependencyTwo,
|
||||||
instance: Object.create(DependencyOne.prototype),
|
instance: Object.create(DependencyOne.prototype),
|
||||||
isResolved: false
|
isResolved: false,
|
||||||
};
|
};
|
||||||
moduleDeps.components.set('MainTest', mainTest);
|
moduleDeps.components.set('MainTest', mainTest);
|
||||||
moduleDeps.components.set('DependencyOne', depOne);
|
moduleDeps.components.set('DependencyOne', depOne);
|
||||||
@@ -59,7 +59,7 @@ describe('Injector', () => {
|
|||||||
|
|
||||||
it('should create an instance of component with proper dependencies', () => {
|
it('should create an instance of component with proper dependencies', () => {
|
||||||
injector.loadInstance(mainTest, moduleDeps.components, moduleDeps);
|
injector.loadInstance(mainTest, moduleDeps.components, moduleDeps);
|
||||||
const { instance } = <InstanceWrapper<MainTest>>(moduleDeps.components.get('MainTest'));
|
const { instance } = moduleDeps.components.get('MainTest') as InstanceWrapper<MainTest>;
|
||||||
|
|
||||||
expect(instance.depOne instanceof DependencyOne).to.be.true;
|
expect(instance.depOne instanceof DependencyOne).to.be.true;
|
||||||
expect(instance.depTwo instanceof DependencyOne).to.be.true;
|
expect(instance.depTwo instanceof DependencyOne).to.be.true;
|
||||||
@@ -68,13 +68,13 @@ describe('Injector', () => {
|
|||||||
|
|
||||||
it('should set "isResolved" property to true after instance initialization', () => {
|
it('should set "isResolved" property to true after instance initialization', () => {
|
||||||
injector.loadInstance(mainTest, moduleDeps.components, moduleDeps);
|
injector.loadInstance(mainTest, moduleDeps.components, moduleDeps);
|
||||||
const { isResolved } = <InstanceWrapper<MainTest>>(moduleDeps.components.get('MainTest'));
|
const { isResolved } = moduleDeps.components.get('MainTest') as InstanceWrapper<MainTest>;
|
||||||
expect(isResolved).to.be.true;
|
expect(isResolved).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw RuntimeException when type is not stored in collection', () => {
|
it('should throw RuntimeException when type is not stored in collection', () => {
|
||||||
expect(
|
expect(
|
||||||
injector.loadInstance.bind(injector, 'Test', moduleDeps.components, moduleDeps)
|
injector.loadInstance.bind(injector, 'Test', moduleDeps.components, moduleDeps),
|
||||||
).to.throw(RuntimeException);
|
).to.throw(RuntimeException);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ describe('Injector', () => {
|
|||||||
name: 'Test',
|
name: 'Test',
|
||||||
metatype: Test,
|
metatype: Test,
|
||||||
instance: Object.create(Test.prototype),
|
instance: Object.create(Test.prototype),
|
||||||
isResolved: false
|
isResolved: false,
|
||||||
};
|
};
|
||||||
moduleDeps.components.set('Test', test);
|
moduleDeps.components.set('Test', test);
|
||||||
});
|
});
|
||||||
@@ -104,7 +104,7 @@ describe('Injector', () => {
|
|||||||
instance: Object.create(Test.prototype),
|
instance: Object.create(Test.prototype),
|
||||||
isResolved: false,
|
isResolved: false,
|
||||||
metatype: Test,
|
metatype: Test,
|
||||||
name: 'Test'
|
name: 'Test',
|
||||||
};
|
};
|
||||||
injector.loadPrototypeOfInstance(test, moduleDeps.components);
|
injector.loadPrototypeOfInstance(test, moduleDeps.components);
|
||||||
expect(moduleDeps.components.get('Test')).to.deep.equal(expectedResult);
|
expect(moduleDeps.components.get('Test')).to.deep.equal(expectedResult);
|
||||||
@@ -128,24 +128,24 @@ describe('Injector', () => {
|
|||||||
it('should call "resolveConstructorParams" when instance is not resolved', () => {
|
it('should call "resolveConstructorParams" when instance is not resolved', () => {
|
||||||
const collection = {
|
const collection = {
|
||||||
get: (...args) => ({
|
get: (...args) => ({
|
||||||
instance: null
|
instance: null,
|
||||||
}),
|
}),
|
||||||
set: (...args) => {}
|
set: (...args) => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
injector.loadInstanceOfMiddleware(<any>{ metatype: { name: '' }}, <any>collection, null);
|
injector.loadInstanceOfMiddleware({ metatype: { name: '' }} as any, collection as any, null);
|
||||||
expect(resolveConstructorParams.called).to.be.true;
|
expect(resolveConstructorParams.called).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not call "resolveConstructorParams" when instance is not resolved', () => {
|
it('should not call "resolveConstructorParams" when instance is not resolved', () => {
|
||||||
const collection = {
|
const collection = {
|
||||||
get: (...args) => ({
|
get: (...args) => ({
|
||||||
instance: {}
|
instance: {},
|
||||||
}),
|
}),
|
||||||
set: (...args) => {}
|
set: (...args) => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
injector.loadInstanceOfMiddleware(<any>{ metatype: { name: '' }}, <any>collection, null);
|
injector.loadInstanceOfMiddleware({ metatype: { name: '' }} as any, collection as any, null);
|
||||||
expect(resolveConstructorParams.called).to.be.false;
|
expect(resolveConstructorParams.called).to.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -156,46 +156,46 @@ describe('Injector', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
scanForComponentInRelatedModules = sinon.stub();
|
scanForComponentInRelatedModules = sinon.stub();
|
||||||
injector['scanForComponentInRelatedModules'] = scanForComponentInRelatedModules;
|
(injector as any).scanForComponentInRelatedModules = scanForComponentInRelatedModules;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return object from collection if exists', () => {
|
it('should return object from collection if exists', () => {
|
||||||
const instance = { test: 3 };
|
const instance = { test: 3 };
|
||||||
const collection = {
|
const collection = {
|
||||||
has: () => true,
|
has: () => true,
|
||||||
get: () => instance
|
get: () => instance,
|
||||||
};
|
};
|
||||||
const result = injector.scanForComponent(<any>collection, metatype.name, null, metatype);
|
const result = injector.scanForComponent(collection as any, metatype.name, null, metatype);
|
||||||
expect(result).to.be.equal(instance);
|
expect(result).to.be.equal(instance);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call "scanForComponentInRelatedModules" when object is not in collection', () => {
|
it('should call "scanForComponentInRelatedModules" when object is not in collection', () => {
|
||||||
scanForComponentInRelatedModules.returns({});
|
scanForComponentInRelatedModules.returns({});
|
||||||
const collection = {
|
const collection = {
|
||||||
has: () => false
|
has: () => false,
|
||||||
};
|
};
|
||||||
injector.scanForComponent(<any>collection, metatype.name, null, metatype);
|
injector.scanForComponent(collection as any, metatype.name, null, metatype);
|
||||||
expect(scanForComponentInRelatedModules.called).to.be.true;
|
expect(scanForComponentInRelatedModules.called).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw "UnkownDependenciesException" instanceWrapper is null', () => {
|
it('should throw "UnkownDependenciesException" instanceWrapper is null', () => {
|
||||||
scanForComponentInRelatedModules.returns(null);
|
scanForComponentInRelatedModules.returns(null);
|
||||||
const collection = {
|
const collection = {
|
||||||
has: () => false
|
has: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => injector.scanForComponent(<any>collection, metatype.name, null, metatype)
|
() => injector.scanForComponent(collection as any, metatype.name, null, metatype)
|
||||||
).throws(UnkownDependenciesException);
|
).throws(UnkownDependenciesException);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw "UnkownDependenciesException" instanceWrapper is not null', () => {
|
it('should not throw "UnkownDependenciesException" instanceWrapper is not null', () => {
|
||||||
scanForComponentInRelatedModules.returns({});
|
scanForComponentInRelatedModules.returns({});
|
||||||
const collection = {
|
const collection = {
|
||||||
has: () => false
|
has: () => false,
|
||||||
};
|
};
|
||||||
expect(
|
expect(
|
||||||
() => injector.scanForComponent(<any>collection, metatype.name, null, metatype)
|
() => injector.scanForComponent(collection as any, metatype.name, null, metatype)
|
||||||
).not.throws(UnkownDependenciesException);
|
).not.throws(UnkownDependenciesException);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -205,16 +205,16 @@ describe('Injector', () => {
|
|||||||
let loadInstanceOfComponent: sinon.SinonSpy;
|
let loadInstanceOfComponent: sinon.SinonSpy;
|
||||||
const metatype = { name: 'test' };
|
const metatype = { name: 'test' };
|
||||||
const module = {
|
const module = {
|
||||||
relatedModules: []
|
relatedModules: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
loadInstanceOfComponent = sinon.spy();
|
loadInstanceOfComponent = sinon.spy();
|
||||||
injector['loadInstanceOfComponent'] = loadInstanceOfComponent;
|
(injector as any).loadInstanceOfComponent = loadInstanceOfComponent;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return null when there is no related modules', () => {
|
it('should return null when there is no related modules', () => {
|
||||||
const result = injector.scanForComponentInRelatedModules(<any>module, null);
|
const result = injector.scanForComponentInRelatedModules(module as any, null);
|
||||||
expect(result).to.be.eq(null);
|
expect(result).to.be.eq(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -226,10 +226,10 @@ describe('Injector', () => {
|
|||||||
},
|
},
|
||||||
exports: {
|
exports: {
|
||||||
has: () => true,
|
has: () => true,
|
||||||
}
|
},
|
||||||
}]
|
}],
|
||||||
};
|
};
|
||||||
expect(injector.scanForComponentInRelatedModules(<any>module, <any>metatype)).to.be.eq(null);
|
expect(injector.scanForComponentInRelatedModules(module as any, metatype as any)).to.be.eq(null);
|
||||||
|
|
||||||
module = {
|
module = {
|
||||||
relatedModules: [{
|
relatedModules: [{
|
||||||
@@ -238,10 +238,10 @@ describe('Injector', () => {
|
|||||||
},
|
},
|
||||||
exports: {
|
exports: {
|
||||||
has: () => false,
|
has: () => false,
|
||||||
}
|
},
|
||||||
}]
|
}],
|
||||||
};
|
};
|
||||||
expect(injector.scanForComponentInRelatedModules(<any>module, <any>metatype)).to.be.eq(null);
|
expect(injector.scanForComponentInRelatedModules(module as any, metatype as any)).to.be.eq(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call "loadInstanceOfComponent" when component is not resolved', () => {
|
it('should call "loadInstanceOfComponent" when component is not resolved', () => {
|
||||||
@@ -250,15 +250,15 @@ describe('Injector', () => {
|
|||||||
components: {
|
components: {
|
||||||
has: () => true,
|
has: () => true,
|
||||||
get: () => ({
|
get: () => ({
|
||||||
isResolved: false
|
isResolved: false,
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
exports: {
|
exports: {
|
||||||
has: () => true,
|
has: () => true,
|
||||||
}
|
},
|
||||||
}]
|
}],
|
||||||
};
|
};
|
||||||
injector.scanForComponentInRelatedModules(<any>module, <any>metatype);
|
injector.scanForComponentInRelatedModules(module as any, metatype as any);
|
||||||
expect(loadInstanceOfComponent.called).to.be.true;
|
expect(loadInstanceOfComponent.called).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -268,15 +268,15 @@ describe('Injector', () => {
|
|||||||
components: {
|
components: {
|
||||||
has: () => true,
|
has: () => true,
|
||||||
get: () => ({
|
get: () => ({
|
||||||
isResolved: true
|
isResolved: true,
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
exports: {
|
exports: {
|
||||||
has: () => true,
|
has: () => true,
|
||||||
}
|
},
|
||||||
}]
|
}],
|
||||||
};
|
};
|
||||||
injector.scanForComponentInRelatedModules(<any>module, <any>metatype);
|
injector.scanForComponentInRelatedModules(module as any, metatype as any);
|
||||||
expect(loadInstanceOfComponent.called).to.be.false;
|
expect(loadInstanceOfComponent.called).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user