@impactor/nest 3.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -6
- package/index.d.ts +41 -40
- package/package.json +31 -33
- package/src/configs/app.d.ts +7 -4
- package/src/configs/app.js +11 -18
- package/src/configs/app.js.map +1 -1
- package/src/configs/database.js +1 -11
- package/src/configs/database.js.map +1 -1
- package/src/configs/microservice.js +6 -16
- package/src/configs/microservice.js.map +1 -1
- package/src/configs/multi-queue-rabbitmq-server.js +28 -34
- package/src/configs/multi-queue-rabbitmq-server.js.map +1 -1
- package/src/configs/redis-config.js +1 -11
- package/src/configs/redis-config.js.map +1 -1
- package/src/create-app.d.ts +1 -1
- package/src/create-app.js +47 -55
- package/src/create-app.js.map +1 -1
- package/src/create-microservice.d.ts +1 -1
- package/src/create-microservice.js +18 -22
- package/src/create-microservice.js.map +1 -1
- package/src/create-testing-microservice.d.ts +1 -1
- package/src/create-testing-microservice.js +16 -20
- package/src/create-testing-microservice.js.map +1 -1
- package/src/decorators/controller/add-decorators/add-controller-path.d.ts +1 -1
- package/src/decorators/controller/add-decorators/add-controller-path.js +7 -15
- package/src/decorators/controller/add-decorators/add-controller-path.js.map +1 -1
- package/src/decorators/controller/add-decorators/add-controller-tags.d.ts +1 -1
- package/src/decorators/controller/add-decorators/add-controller-tags.js +9 -17
- package/src/decorators/controller/add-decorators/add-controller-tags.js.map +1 -1
- package/src/decorators/controller/add-decorators/add-http-method.d.ts +2 -2
- package/src/decorators/controller/add-decorators/add-http-method.js +13 -20
- package/src/decorators/controller/add-decorators/add-http-method.js.map +1 -1
- package/src/decorators/controller/add-decorators/add-swagger-body.d.ts +2 -2
- package/src/decorators/controller/add-decorators/add-swagger-body.js +7 -17
- package/src/decorators/controller/add-decorators/add-swagger-body.js.map +1 -1
- package/src/decorators/controller/add-decorators/add-swagger-operation.d.ts +2 -2
- package/src/decorators/controller/add-decorators/add-swagger-operation.js +8 -15
- package/src/decorators/controller/add-decorators/add-swagger-operation.js.map +1 -1
- package/src/decorators/controller/add-decorators/add-swagger-params.d.ts +2 -2
- package/src/decorators/controller/add-decorators/add-swagger-params.js +9 -16
- package/src/decorators/controller/add-decorators/add-swagger-params.js.map +1 -1
- package/src/decorators/controller/add-decorators/add-swagger-response.d.ts +2 -2
- package/src/decorators/controller/add-decorators/add-swagger-response.js +10 -20
- package/src/decorators/controller/add-decorators/add-swagger-response.js.map +1 -1
- package/src/decorators/controller/controller.decorator.d.ts +1 -1
- package/src/decorators/controller/controller.decorator.js +12 -13
- package/src/decorators/controller/controller.decorator.js.map +1 -1
- package/src/decorators/controller/controller.factory.d.ts +1 -1
- package/src/decorators/controller/controller.factory.js +59 -67
- package/src/decorators/controller/controller.factory.js.map +1 -1
- package/src/decorators/controller/default-options.d.ts +1 -1
- package/src/decorators/controller/default-options.js +11 -26
- package/src/decorators/controller/default-options.js.map +1 -1
- package/src/decorators/controller/dto/empty.dto.js +2 -12
- package/src/decorators/controller/dto/empty.dto.js.map +1 -1
- package/src/decorators/controller/dto/update-response.dto.js +2 -12
- package/src/decorators/controller/dto/update-response.dto.js.map +1 -1
- package/src/decorators/controller/interceptors/query.interceptor.d.ts +1 -1
- package/src/decorators/controller/interceptors/query.interceptor.js +10 -19
- package/src/decorators/controller/interceptors/query.interceptor.js.map +1 -1
- package/src/decorators/controller/route-handler.d.ts +2 -2
- package/src/decorators/controller/route-handler.js +8 -16
- package/src/decorators/controller/route-handler.js.map +1 -1
- package/src/decorators/controller/route.decorator.d.ts +1 -1
- package/src/decorators/controller/route.decorator.js +11 -11
- package/src/decorators/controller/route.decorator.js.map +1 -1
- package/src/decorators/controller/services/crud-typeorm.service.d.ts +1 -2
- package/src/decorators/controller/services/crud-typeorm.service.js +19 -24
- package/src/decorators/controller/services/crud-typeorm.service.js.map +1 -1
- package/src/decorators/controller/types/index.d.ts +2 -2
- package/src/decorators/controller/types/index.js +1 -4
- package/src/decorators/controller/types/index.js.map +1 -1
- package/src/decorators/controller/utils/merge-options.d.ts +1 -1
- package/src/decorators/controller/utils/merge-options.js +3 -13
- package/src/decorators/controller/utils/merge-options.js.map +1 -1
- package/src/decorators/controller/utils/reflect.d.ts +2 -2
- package/src/decorators/controller/utils/reflect.js +24 -38
- package/src/decorators/controller/utils/reflect.js.map +1 -1
- package/src/decorators/entity.decorator.d.ts +6 -0
- package/src/decorators/entity.decorator.js +73 -0
- package/src/decorators/entity.decorator.js.map +1 -0
- package/src/decorators/prop.decorator.js +79 -38
- package/src/decorators/prop.decorator.js.map +1 -1
- package/src/exceptions/rpc-bad-request.exception.d.ts +1 -1
- package/src/exceptions/rpc-bad-request.exception.js +5 -15
- package/src/exceptions/rpc-bad-request.exception.js.map +1 -1
- package/src/exceptions/rpc-base.exception.js +10 -15
- package/src/exceptions/rpc-base.exception.js.map +1 -1
- package/src/exceptions/rpc-conflict.exception.d.ts +1 -1
- package/src/exceptions/rpc-conflict.exception.js +5 -15
- package/src/exceptions/rpc-conflict.exception.js.map +1 -1
- package/src/exceptions/rpc-internal-server-error.exception.d.ts +1 -1
- package/src/exceptions/rpc-internal-server-error.exception.js +5 -15
- package/src/exceptions/rpc-internal-server-error.exception.js.map +1 -1
- package/src/exceptions/rpc-method-not-allowed.exception.d.ts +1 -1
- package/src/exceptions/rpc-method-not-allowed.exception.js +5 -15
- package/src/exceptions/rpc-method-not-allowed.exception.js.map +1 -1
- package/src/exceptions/rpc-not-found.exception.d.ts +1 -1
- package/src/exceptions/rpc-not-found.exception.js +5 -15
- package/src/exceptions/rpc-not-found.exception.js.map +1 -1
- package/src/exceptions/rpc-not-implemented.exception.d.ts +1 -1
- package/src/exceptions/rpc-not-implemented.exception.js +5 -15
- package/src/exceptions/rpc-not-implemented.exception.js.map +1 -1
- package/src/exceptions/rpc-unauthorized.exception.d.ts +1 -1
- package/src/exceptions/rpc-unauthorized.exception.js +5 -15
- package/src/exceptions/rpc-unauthorized.exception.js.map +1 -1
- package/src/filters/error-to-rpc-exception.filter.js +12 -22
- package/src/filters/error-to-rpc-exception.filter.js.map +1 -1
- package/src/filters/error-to-ws-exception.filter.js +10 -20
- package/src/filters/error-to-ws-exception.filter.js.map +1 -1
- package/src/filters/typeorm-exception.filter.js +9 -19
- package/src/filters/typeorm-exception.filter.js.map +1 -1
- package/src/filters/ws-exception.filter.js +6 -16
- package/src/filters/ws-exception.filter.js.map +1 -1
- package/src/generate-metadata.d.ts +3 -3
- package/src/generate-metadata.js +18 -74
- package/src/generate-metadata.js.map +1 -1
- package/src/guards/auth.guard.js +43 -44
- package/src/guards/auth.guard.js.map +1 -1
- package/src/modules/basic/basic.controller.js +6 -16
- package/src/modules/basic/basic.controller.js.map +1 -1
- package/src/modules/basic/basic.module.js +15 -23
- package/src/modules/basic/basic.module.js.map +1 -1
- package/src/modules/basic/basic.service.js +4 -14
- package/src/modules/basic/basic.service.js.map +1 -1
- package/src/register-entities.d.ts +1 -0
- package/src/register-entities.js +135 -0
- package/src/register-entities.js.map +1 -0
- package/src/utils/logger.js +2 -12
- package/src/utils/logger.js.map +1 -1
- package/src/utils/typeorm-to-swagger.d.ts +3 -0
- package/src/utils/typeorm-to-swagger.js +308 -0
- package/src/utils/typeorm-to-swagger.js.map +1 -0
- package/nx.json +0 -190
- package/src/nest-swagger-metadata.js +0 -553
- package/src/nest-swagger-metadata.js.map +0 -1
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return addSwaggerOperation;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _reflect = require("../utils/reflect");
|
|
12
|
-
const _swagger = require("@nestjs/swagger");
|
|
13
|
-
function addSwaggerOperation(route, factory) {
|
|
14
|
-
if ((0, _reflect.has)('swagger/apiOperation', factory, route)) return;
|
|
1
|
+
import { has } from "../utils/reflect.js";
|
|
2
|
+
import { ApiOperation } from "@nestjs/swagger";
|
|
3
|
+
/**
|
|
4
|
+
* add Swagger's \@ApiOperation()
|
|
5
|
+
* @param route
|
|
6
|
+
*/ export function addSwaggerOperation(route, factory) {
|
|
7
|
+
if (has('swagger/apiOperation', factory, route)) return;
|
|
15
8
|
// remove "path" or add "prefix" metadata to it from the constructor
|
|
16
9
|
// to prevent displaying the path without the controller prefix in Swagger docs
|
|
17
10
|
let { path, ...operation } = route;
|
|
18
|
-
route.decorators.push(
|
|
11
|
+
route.decorators.push(ApiOperation(operation));
|
|
19
12
|
}
|
|
20
13
|
|
|
21
14
|
//# sourceMappingURL=add-swagger-operation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/decorators/controller/add-decorators/add-swagger-operation.ts"],"sourcesContent":["import { has } from '../utils/reflect';\nimport { ControllerFactory } from '../controller.factory';\nimport type { IRoute } from '../types';\nimport { ApiOperation } from '@nestjs/swagger';\n\n/**\n * add Swagger's \\@ApiOperation()\n * @param route\n */\nexport function addSwaggerOperation(route: IRoute, factory: ControllerFactory) {\n if (has('swagger/apiOperation', factory, route)) return;\n\n // remove \"path\" or add \"prefix\" metadata to it from the constructor\n // to prevent displaying the path without the controller prefix in Swagger docs\n let { path, ...operation } = route;\n route.decorators.push(ApiOperation(operation));\n}\n"],"names":["addSwaggerOperation","route","factory","
|
|
1
|
+
{"version":3,"sources":["../../../../../src/decorators/controller/add-decorators/add-swagger-operation.ts"],"sourcesContent":["import { has } from '../utils/reflect.js';\nimport { ControllerFactory } from '../controller.factory.js';\nimport type { IRoute } from '../types/index.js';\nimport { ApiOperation } from '@nestjs/swagger';\n\n/**\n * add Swagger's \\@ApiOperation()\n * @param route\n */\nexport function addSwaggerOperation(route: IRoute, factory: ControllerFactory) {\n if (has('swagger/apiOperation', factory, route)) return;\n\n // remove \"path\" or add \"prefix\" metadata to it from the constructor\n // to prevent displaying the path without the controller prefix in Swagger docs\n let { path, ...operation } = route;\n route.decorators.push(ApiOperation(operation));\n}\n"],"names":["has","ApiOperation","addSwaggerOperation","route","factory","path","operation","decorators","push"],"mappings":"AAAA,SAASA,GAAG,QAAQ,sBAAsB;AAG1C,SAASC,YAAY,QAAQ,kBAAkB;AAE/C;;;CAGC,GACD,OAAO,SAASC,oBAAoBC,KAAa,EAAEC,OAA0B;IAC3E,IAAIJ,IAAI,wBAAwBI,SAASD,QAAQ;IAEjD,oEAAoE;IACpE,+EAA+E;IAC/E,IAAI,EAAEE,IAAI,EAAE,GAAGC,WAAW,GAAGH;IAC7BA,MAAMI,UAAU,CAACC,IAAI,CAACP,aAAaK;AACrC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { ControllerFactory } from '../controller.factory';
|
|
2
|
-
import type { IRoute } from '../types';
|
|
1
|
+
import { ControllerFactory } from '../controller.factory.js';
|
|
2
|
+
import type { IRoute } from '../types/index.js';
|
|
3
3
|
export declare function addSwaggerParams(route: IRoute, factory: ControllerFactory): void;
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return addSwaggerParams;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _reflect = require("../utils/reflect");
|
|
12
|
-
const _swagger = require("@nestjs/swagger");
|
|
13
|
-
function addSwaggerParams(route, factory) {
|
|
14
|
-
if ((0, _reflect.has)('swagger/apiParameters', factory, route)) return;
|
|
1
|
+
import { has } from "../utils/reflect.js";
|
|
2
|
+
import { ApiParam, ApiQuery } from "@nestjs/swagger";
|
|
3
|
+
/**
|
|
4
|
+
* add Swagger's \@ApiQuery() and \ApiParam() for each param
|
|
5
|
+
* @param route
|
|
6
|
+
*/ export function addSwaggerParams(route, factory) {
|
|
7
|
+
if (has('swagger/apiParameters', factory, route)) return;
|
|
15
8
|
let queryParams = route.queryParams || factory.options?.queryParams;
|
|
16
9
|
queryParams?.map((param)=>{
|
|
17
10
|
// exclude params that doesn't match the route
|
|
@@ -42,9 +35,9 @@ function addSwaggerParams(route, factory) {
|
|
|
42
35
|
}
|
|
43
36
|
// if param.required not provided, set it to false
|
|
44
37
|
param.required = !!param.required;
|
|
45
|
-
route.decorators.push(
|
|
38
|
+
route.decorators.push(ApiQuery(param));
|
|
46
39
|
});
|
|
47
|
-
route.pathParams?.map((param)=>route.decorators.push(
|
|
40
|
+
route.pathParams?.map((param)=>route.decorators.push(ApiParam(param)));
|
|
48
41
|
}
|
|
49
42
|
|
|
50
43
|
//# sourceMappingURL=add-swagger-params.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/decorators/controller/add-decorators/add-swagger-params.ts"],"sourcesContent":["import { has } from '../utils/reflect';\nimport { ControllerFactory } from '../controller.factory';\nimport type { IRoute } from '../types';\nimport { ApiParam, ApiQuery } from '@nestjs/swagger';\n\n/**\n * add Swagger's \\@ApiQuery() and \\ApiParam() for each param\n * @param route\n */\nexport function addSwaggerParams(route: IRoute, factory: ControllerFactory) {\n if (has('swagger/apiParameters', factory, route)) return;\n\n let queryParams = route.queryParams || factory.options?.queryParams;\n queryParams?.map((param) => {\n // exclude params that doesn't match the route\n if (param.many === false && route.many) return;\n if (param.many === true && !route.many) return;\n\n if (param.httpMethods && !param.httpMethods.includes(route.httpMethod))\n return;\n\n if (!param.type && ['number', 'string'].includes(typeof param.example))\n param.type = typeof param.example;\n else if (\n param.type === 'array' ||\n (Array.isArray(param?.example) && !param.type && !param.schema)\n ) {\n param = {\n schema: {\n type: 'array',\n items: {\n type: 'string',\n },\n },\n // add items as array to prevent sending items as v1%2Cv2 instead of v1,v2\n // converts the array into ?key=v1,v2,...\n style: 'form',\n // don't generate separate params for each item\n // i.e. ?key=v1&key=v2\n explode: false,\n ...param,\n };\n\n delete param.type;\n }\n\n // if param.required not provided, set it to false\n param.required = !!param.required;\n\n route.decorators.push(ApiQuery(param));\n });\n\n route.pathParams?.map((param) => route.decorators.push(ApiParam(param)));\n}\n"],"names":["addSwaggerParams","route","factory","
|
|
1
|
+
{"version":3,"sources":["../../../../../src/decorators/controller/add-decorators/add-swagger-params.ts"],"sourcesContent":["import { has } from '../utils/reflect.js';\nimport { ControllerFactory } from '../controller.factory.js';\nimport type { IRoute } from '../types/index.js';\nimport { ApiParam, ApiQuery } from '@nestjs/swagger';\n\n/**\n * add Swagger's \\@ApiQuery() and \\ApiParam() for each param\n * @param route\n */\nexport function addSwaggerParams(route: IRoute, factory: ControllerFactory) {\n if (has('swagger/apiParameters', factory, route)) return;\n\n let queryParams = route.queryParams || factory.options?.queryParams;\n queryParams?.map((param) => {\n // exclude params that doesn't match the route\n if (param.many === false && route.many) return;\n if (param.many === true && !route.many) return;\n\n if (param.httpMethods && !param.httpMethods.includes(route.httpMethod))\n return;\n\n if (!param.type && ['number', 'string'].includes(typeof param.example))\n param.type = typeof param.example;\n else if (\n param.type === 'array' ||\n (Array.isArray(param?.example) && !param.type && !param.schema)\n ) {\n param = {\n schema: {\n type: 'array',\n items: {\n type: 'string',\n },\n },\n // add items as array to prevent sending items as v1%2Cv2 instead of v1,v2\n // converts the array into ?key=v1,v2,...\n style: 'form',\n // don't generate separate params for each item\n // i.e. ?key=v1&key=v2\n explode: false,\n ...param,\n };\n\n delete param.type;\n }\n\n // if param.required not provided, set it to false\n param.required = !!param.required;\n\n route.decorators.push(ApiQuery(param));\n });\n\n route.pathParams?.map((param) => route.decorators.push(ApiParam(param)));\n}\n"],"names":["has","ApiParam","ApiQuery","addSwaggerParams","route","factory","queryParams","options","map","param","many","httpMethods","includes","httpMethod","type","example","Array","isArray","schema","items","style","explode","required","decorators","push","pathParams"],"mappings":"AAAA,SAASA,GAAG,QAAQ,sBAAsB;AAG1C,SAASC,QAAQ,EAAEC,QAAQ,QAAQ,kBAAkB;AAErD;;;CAGC,GACD,OAAO,SAASC,iBAAiBC,KAAa,EAAEC,OAA0B;IACxE,IAAIL,IAAI,yBAAyBK,SAASD,QAAQ;IAElD,IAAIE,cAAcF,MAAME,WAAW,IAAID,QAAQE,OAAO,EAAED;IACxDA,aAAaE,IAAI,CAACC;QAChB,8CAA8C;QAC9C,IAAIA,MAAMC,IAAI,KAAK,SAASN,MAAMM,IAAI,EAAE;QACxC,IAAID,MAAMC,IAAI,KAAK,QAAQ,CAACN,MAAMM,IAAI,EAAE;QAExC,IAAID,MAAME,WAAW,IAAI,CAACF,MAAME,WAAW,CAACC,QAAQ,CAACR,MAAMS,UAAU,GACnE;QAEF,IAAI,CAACJ,MAAMK,IAAI,IAAI;YAAC;YAAU;SAAS,CAACF,QAAQ,CAAC,OAAOH,MAAMM,OAAO,GACnEN,MAAMK,IAAI,GAAG,OAAOL,MAAMM,OAAO;aAC9B,IACHN,MAAMK,IAAI,KAAK,WACdE,MAAMC,OAAO,CAACR,OAAOM,YAAY,CAACN,MAAMK,IAAI,IAAI,CAACL,MAAMS,MAAM,EAC9D;YACAT,QAAQ;gBACNS,QAAQ;oBACNJ,MAAM;oBACNK,OAAO;wBACLL,MAAM;oBACR;gBACF;gBACA,0EAA0E;gBAC1E,yCAAyC;gBACzCM,OAAO;gBACP,+CAA+C;gBAC/C,sBAAsB;gBACtBC,SAAS;gBACT,GAAGZ,KAAK;YACV;YAEA,OAAOA,MAAMK,IAAI;QACnB;QAEA,kDAAkD;QAClDL,MAAMa,QAAQ,GAAG,CAAC,CAACb,MAAMa,QAAQ;QAEjClB,MAAMmB,UAAU,CAACC,IAAI,CAACtB,SAASO;IACjC;IAEAL,MAAMqB,UAAU,EAAEjB,IAAI,CAACC,QAAUL,MAAMmB,UAAU,CAACC,IAAI,CAACvB,SAASQ;AAClE"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { ControllerFactory } from '../controller.factory';
|
|
2
|
-
import type { IRoute } from '../types';
|
|
1
|
+
import { ControllerFactory } from '../controller.factory.js';
|
|
2
|
+
import type { IRoute } from '../types/index.js';
|
|
3
3
|
export declare function addSwaggerResponse(route: IRoute, factory: ControllerFactory): void;
|
|
@@ -1,33 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return addSwaggerResponse;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _reflect = require("../utils/reflect");
|
|
12
|
-
const _common = require("@nestjs/common");
|
|
13
|
-
const _swagger = require("@nestjs/swagger");
|
|
14
|
-
function addSwaggerResponse(route, factory) {
|
|
15
|
-
if ((0, _reflect.has)('swagger/apiResponse', factory, route)) return;
|
|
1
|
+
import { has } from "../utils/reflect.js";
|
|
2
|
+
import { HttpStatus } from "@nestjs/common";
|
|
3
|
+
import { ApiResponse } from "@nestjs/swagger";
|
|
4
|
+
export function addSwaggerResponse(route, factory) {
|
|
5
|
+
if (has('swagger/apiResponse', factory, route)) return;
|
|
16
6
|
let responseModel = route.responseModel || route.model || factory.options?.model;
|
|
17
7
|
// todo: add other status responses
|
|
18
8
|
// todo: add options.responseModels[status] -> example: response models for 200, 401, 500, ...
|
|
19
|
-
route.decorators.push(
|
|
20
|
-
status:
|
|
9
|
+
route.decorators.push(ApiResponse({
|
|
10
|
+
status: HttpStatus.OK,
|
|
21
11
|
type: route.many ? [
|
|
22
12
|
responseModel
|
|
23
13
|
] : responseModel
|
|
24
|
-
}),
|
|
25
|
-
status:
|
|
14
|
+
}), ApiResponse({
|
|
15
|
+
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
|
26
16
|
type: route.errorResponseModel || factory.options?.errorResponseModel
|
|
27
17
|
}), ...[
|
|
28
18
|
...factory.options?.responseModels || [],
|
|
29
19
|
...route.responseModels || []
|
|
30
|
-
].map((model)=>
|
|
20
|
+
].map((model)=>ApiResponse(model)));
|
|
31
21
|
}
|
|
32
22
|
|
|
33
23
|
//# sourceMappingURL=add-swagger-response.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/decorators/controller/add-decorators/add-swagger-response.ts"],"sourcesContent":["import { has } from '../utils/reflect';\nimport { ControllerFactory } from '../controller.factory';\nimport type { IRoute } from '../types';\nimport { HttpStatus } from '@nestjs/common';\nimport { ApiResponse } from '@nestjs/swagger';\n\nexport function addSwaggerResponse(route: IRoute, factory: ControllerFactory) {\n if (has('swagger/apiResponse', factory, route)) return;\n\n let responseModel =\n route.responseModel || route.model || factory.options?.model;\n\n // todo: add other status responses\n // todo: add options.responseModels[status] -> example: response models for 200, 401, 500, ...\n route.decorators.push(\n ApiResponse({\n status: HttpStatus.OK,\n type: route.many ? [responseModel] : responseModel,\n }),\n ApiResponse({\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n type: route.errorResponseModel || factory.options?.errorResponseModel,\n }),\n ...[\n ...(factory.options?.responseModels || []),\n ...(route.responseModels || []),\n ].map((model) => ApiResponse(model)),\n );\n}\n"],"names":["addSwaggerResponse","route","factory","
|
|
1
|
+
{"version":3,"sources":["../../../../../src/decorators/controller/add-decorators/add-swagger-response.ts"],"sourcesContent":["import { has } from '../utils/reflect.js';\nimport { ControllerFactory } from '../controller.factory.js';\nimport type { IRoute } from '../types/index.js';\nimport { HttpStatus } from '@nestjs/common';\nimport { ApiResponse } from '@nestjs/swagger';\n\nexport function addSwaggerResponse(route: IRoute, factory: ControllerFactory) {\n if (has('swagger/apiResponse', factory, route)) return;\n\n let responseModel =\n route.responseModel || route.model || factory.options?.model;\n\n // todo: add other status responses\n // todo: add options.responseModels[status] -> example: response models for 200, 401, 500, ...\n route.decorators.push(\n ApiResponse({\n status: HttpStatus.OK,\n type: route.many ? [responseModel] : responseModel,\n }),\n ApiResponse({\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n type: route.errorResponseModel || factory.options?.errorResponseModel,\n }),\n ...[\n ...(factory.options?.responseModels || []),\n ...(route.responseModels || []),\n ].map((model) => ApiResponse(model)),\n );\n}\n"],"names":["has","HttpStatus","ApiResponse","addSwaggerResponse","route","factory","responseModel","model","options","decorators","push","status","OK","type","many","INTERNAL_SERVER_ERROR","errorResponseModel","responseModels","map"],"mappings":"AAAA,SAASA,GAAG,QAAQ,sBAAsB;AAG1C,SAASC,UAAU,QAAQ,iBAAiB;AAC5C,SAASC,WAAW,QAAQ,kBAAkB;AAE9C,OAAO,SAASC,mBAAmBC,KAAa,EAAEC,OAA0B;IAC1E,IAAIL,IAAI,uBAAuBK,SAASD,QAAQ;IAEhD,IAAIE,gBACFF,MAAME,aAAa,IAAIF,MAAMG,KAAK,IAAIF,QAAQG,OAAO,EAAED;IAEzD,mCAAmC;IACnC,8FAA8F;IAC9FH,MAAMK,UAAU,CAACC,IAAI,CACnBR,YAAY;QACVS,QAAQV,WAAWW,EAAE;QACrBC,MAAMT,MAAMU,IAAI,GAAG;YAACR;SAAc,GAAGA;IACvC,IACAJ,YAAY;QACVS,QAAQV,WAAWc,qBAAqB;QACxCF,MAAMT,MAAMY,kBAAkB,IAAIX,QAAQG,OAAO,EAAEQ;IACrD,OACG;WACGX,QAAQG,OAAO,EAAES,kBAAkB,EAAE;WACrCb,MAAMa,cAAc,IAAI,EAAE;KAC/B,CAACC,GAAG,CAAC,CAACX,QAAUL,YAAYK;AAEjC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ControllerOptions, InitControllerOptions } from './types';
|
|
1
|
+
import type { ControllerOptions, InitControllerOptions } from './types/index.js';
|
|
2
2
|
export declare function Controller(options?: InitControllerOptions | ((defaultOptions: ControllerOptions) => ControllerOptions)): ClassDecorator;
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
function Controller(options) {
|
|
1
|
+
import { ControllerFactory } from "./controller.factory.js";
|
|
2
|
+
/**
|
|
3
|
+
* Mark a class as a controller and automatically generate its crud routes, and add swagger decorators
|
|
4
|
+
* This decorator must be placed before other decorators to avoid overriding their values
|
|
5
|
+
* otherwise, you can disable any functionality by setting it to `null`
|
|
6
|
+
* for instance `@ApiTags("users"); @Controller("my-users")`
|
|
7
|
+
* may cause that the value of `ApiTags` to be replaced
|
|
8
|
+
* either place `@Controller()` first, or use `@Controller({tags: null})`
|
|
9
|
+
* @param options
|
|
10
|
+
* @returns
|
|
11
|
+
*/ export function Controller(options) {
|
|
13
12
|
return (target)=>{
|
|
14
|
-
new
|
|
13
|
+
new ControllerFactory(target, options);
|
|
15
14
|
};
|
|
16
15
|
}
|
|
17
16
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/decorators/controller/controller.decorator.ts"],"sourcesContent":["import { ControllerFactory } from './controller.factory';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../../../../src/decorators/controller/controller.decorator.ts"],"sourcesContent":["import { ControllerFactory } from './controller.factory.js';\nimport type {\n ControllerOptions,\n InitControllerOptions,\n} from './types/index.js';\n\n/**\n * Mark a class as a controller and automatically generate its crud routes, and add swagger decorators\n * This decorator must be placed before other decorators to avoid overriding their values\n * otherwise, you can disable any functionality by setting it to `null`\n * for instance `@ApiTags(\"users\"); @Controller(\"my-users\")`\n * may cause that the value of `ApiTags` to be replaced\n * either place `@Controller()` first, or use `@Controller({tags: null})`\n * @param options\n * @returns\n */\nexport function Controller(\n options?:\n | InitControllerOptions\n | ((defaultOptions: ControllerOptions) => ControllerOptions),\n): ClassDecorator {\n return (target: Function) => {\n new ControllerFactory(target, options);\n };\n}\n"],"names":["ControllerFactory","Controller","options","target"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,0BAA0B;AAM5D;;;;;;;;;CASC,GACD,OAAO,SAASC,WACdC,OAE8D;IAE9D,OAAO,CAACC;QACN,IAAIH,kBAAkBG,QAAQD;IAChC;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ControllerOptions, InitControllerOptions, InitRoute } from './types';
|
|
1
|
+
import type { ControllerOptions, InitControllerOptions, InitRoute } from './types/index.js';
|
|
2
2
|
export declare class ControllerFactory {
|
|
3
3
|
controller: Function;
|
|
4
4
|
options: ControllerOptions;
|
|
@@ -1,28 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { Body, Param, Query, Req } from "@nestjs/common";
|
|
2
|
+
import { addHttpMethod } from "./add-decorators/add-http-method.js";
|
|
3
|
+
import { addSwaggerParams } from "./add-decorators/add-swagger-params.js";
|
|
4
|
+
import { routeHandler } from "./route-handler.js";
|
|
5
|
+
import { addSwaggerOperation } from "./add-decorators/add-swagger-operation.js";
|
|
6
|
+
import { addSwaggerBody } from "./add-decorators/add-swagger-body.js";
|
|
7
|
+
import { addSwaggerResponse } from "./add-decorators/add-swagger-response.js";
|
|
8
|
+
import { QueryInterceptor } from "./interceptors/query.interceptor.js";
|
|
9
|
+
import { defaultOptions } from "./default-options.js";
|
|
10
|
+
import { mergeOptions } from "./utils/merge-options.js";
|
|
11
|
+
import { addControllerTags } from "./add-decorators/add-controller-tags.js";
|
|
12
|
+
import { merge, mergeByKey, removeValues } from "@impactor/javascript";
|
|
13
|
+
import { applyDecorators } from "./utils/reflect.js";
|
|
14
|
+
import { addControllerPath } from "./add-decorators/add-controller-path.js";
|
|
15
|
+
import { toKebabCase } from "@impactor/javascript";
|
|
16
|
+
// import 'reflect-metadata';
|
|
17
|
+
export class ControllerFactory {
|
|
18
|
+
constructor(controller, // if options is provided as a function, the consumer has to maintain the merge strategy
|
|
19
|
+
options){
|
|
20
|
+
this.controller = controller;
|
|
21
|
+
this.decorators = [];
|
|
22
|
+
this.options = typeof options === 'function' ? // we'll need to call it again after receiving the user options to adjust its routes
|
|
23
|
+
options(this.adjustRoutes(defaultOptions)) : mergeOptions(defaultOptions, options || {});
|
|
24
|
+
this.options = this.adjustRoutes({
|
|
25
|
+
...this.options,
|
|
26
|
+
// todo: merge matched routes instead of overriding them
|
|
27
|
+
// i.e. routes with the same methodName, httpMethod, and path
|
|
28
|
+
routes: [
|
|
29
|
+
...this.options.routes || [],
|
|
30
|
+
...this.getExistingMethods()
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
// if path is not provided, use the class name, without the suffix `Controller`,
|
|
34
|
+
// after converting it into url-friendly kebab-case
|
|
35
|
+
if (this.options.path === undefined) {
|
|
36
|
+
this.options.path = toKebabCase(this.controller.name.replace(/Controller$/, ''));
|
|
37
|
+
}
|
|
38
|
+
this.createRoutes();
|
|
39
|
+
this.mergeRoutes();
|
|
40
|
+
addControllerPath(this);
|
|
41
|
+
addControllerTags(this);
|
|
42
|
+
applyDecorators(this);
|
|
9
43
|
}
|
|
10
|
-
});
|
|
11
|
-
const _common = require("@nestjs/common");
|
|
12
|
-
const _addhttpmethod = require("./add-decorators/add-http-method");
|
|
13
|
-
const _addswaggerparams = require("./add-decorators/add-swagger-params");
|
|
14
|
-
const _routehandler = require("./route-handler");
|
|
15
|
-
const _addswaggeroperation = require("./add-decorators/add-swagger-operation");
|
|
16
|
-
const _addswaggerbody = require("./add-decorators/add-swagger-body");
|
|
17
|
-
const _addswaggerresponse = require("./add-decorators/add-swagger-response");
|
|
18
|
-
const _queryinterceptor = require("./interceptors/query.interceptor");
|
|
19
|
-
const _defaultoptions = require("./default-options");
|
|
20
|
-
const _mergeoptions = require("./utils/merge-options");
|
|
21
|
-
const _addcontrollertags = require("./add-decorators/add-controller-tags");
|
|
22
|
-
const _javascript = require("@impactor/javascript");
|
|
23
|
-
const _reflect = require("./utils/reflect");
|
|
24
|
-
const _addcontrollerpath = require("./add-decorators/add-controller-path");
|
|
25
|
-
let ControllerFactory = class ControllerFactory {
|
|
26
44
|
getExistingMethods() {
|
|
27
45
|
let proto = this.controller.prototype;
|
|
28
46
|
return Reflect.ownKeys(proto).filter((key)=>typeof proto[key] === 'function')// convert symbol to string
|
|
@@ -113,10 +131,10 @@ let ControllerFactory = class ControllerFactory {
|
|
|
113
131
|
// todo: we may need to exclude other keys that have a class value such as errorResponseModel
|
|
114
132
|
let { routes, model, ...otherOptions } = opts;
|
|
115
133
|
// todo: use merge()?
|
|
116
|
-
route =
|
|
134
|
+
route = merge([
|
|
117
135
|
otherOptions || {},
|
|
118
136
|
route || {}
|
|
119
|
-
].map((el)=>
|
|
137
|
+
].map((el)=>removeValues(el, [
|
|
120
138
|
undefined,
|
|
121
139
|
null
|
|
122
140
|
])));
|
|
@@ -139,7 +157,7 @@ let ControllerFactory = class ControllerFactory {
|
|
|
139
157
|
// opts[key as keyof typeof opts] || [];
|
|
140
158
|
// }
|
|
141
159
|
// });
|
|
142
|
-
route.interceptors.push(new
|
|
160
|
+
route.interceptors.push(new QueryInterceptor(merge([
|
|
143
161
|
opts.query || {},
|
|
144
162
|
route.query || {}
|
|
145
163
|
])));
|
|
@@ -155,22 +173,22 @@ let ControllerFactory = class ControllerFactory {
|
|
|
155
173
|
// create a route method and add it to the controller
|
|
156
174
|
// todo: use interceptor to create ParsedRequest
|
|
157
175
|
if (!controller[route.methodName]) {
|
|
158
|
-
controller[route.methodName] =
|
|
176
|
+
controller[route.methodName] = routeHandler(route, this);
|
|
159
177
|
// inject the required params
|
|
160
178
|
// similar to patchOne(@Req() req: Request, @Query() query, @Body() dto: MyEntity, @Param("id") id:string)
|
|
161
179
|
// we can get other info such as body and params from req
|
|
162
180
|
// without the need to inject @Body() and @Query()
|
|
163
181
|
// todo: route.primary || getPrimaryKeyFromEntity(opts.model) || "id"
|
|
164
182
|
let paramKey = route.primaryKey || 'id';
|
|
165
|
-
|
|
166
|
-
|
|
183
|
+
Req()(this.controller.prototype, route.methodName, 0);
|
|
184
|
+
Query()(this.controller.prototype, route.methodName, 1);
|
|
167
185
|
if ([
|
|
168
186
|
'GET',
|
|
169
187
|
'DELETE'
|
|
170
188
|
].includes(route.httpMethod) && !route.many) {
|
|
171
|
-
|
|
189
|
+
Param(paramKey)(this.controller.prototype, route.methodName, 2);
|
|
172
190
|
} else if (route.httpMethod === 'POST') {
|
|
173
|
-
|
|
191
|
+
Body()(this.controller.prototype, route.methodName, 2);
|
|
174
192
|
// add 'design:paramtypes' to define the types of each param
|
|
175
193
|
// for instance, the param `@Body()` should be defined as `this.options.model`,
|
|
176
194
|
// so `ValidationPipe()` can work fine.
|
|
@@ -187,7 +205,7 @@ let ControllerFactory = class ControllerFactory {
|
|
|
187
205
|
'PATCH',
|
|
188
206
|
'PUT'
|
|
189
207
|
].includes(route.httpMethod)) {
|
|
190
|
-
|
|
208
|
+
Body()(this.controller.prototype, route.methodName, 2);
|
|
191
209
|
if (route.many) {
|
|
192
210
|
Reflect.defineMetadata('design:paramtypes', [
|
|
193
211
|
Object,
|
|
@@ -199,7 +217,7 @@ let ControllerFactory = class ControllerFactory {
|
|
|
199
217
|
// todo: accept list of pipes for each param -> `Param(paramName, ...pipes)(...)`
|
|
200
218
|
// to accept only string or number -> add a validation in routerHandler i.e. `if(! typeof params[paramKey]) throw ...`,
|
|
201
219
|
// as class-validator will not validate its type because it is not a DTO i.e. `getOne(@param() params: ParamsDTO)`
|
|
202
|
-
|
|
220
|
+
Param(paramKey)(this.controller.prototype, route.methodName, 3);
|
|
203
221
|
Reflect.defineMetadata('design:paramtypes', [
|
|
204
222
|
Object,
|
|
205
223
|
Object,
|
|
@@ -216,46 +234,20 @@ let ControllerFactory = class ControllerFactory {
|
|
|
216
234
|
// if the method is not listed and doesn't has @Route(), the annotations will not be added
|
|
217
235
|
// if no http decorator already applied such as @GET()
|
|
218
236
|
// when adding @Get(), nest adds "method" and "path" metadata
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
237
|
+
addHttpMethod(route, this);
|
|
238
|
+
addSwaggerOperation(route, this);
|
|
239
|
+
addSwaggerParams(route, this);
|
|
240
|
+
addSwaggerBody(route, this);
|
|
241
|
+
addSwaggerResponse(route, this);
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
244
|
/**
|
|
227
245
|
* Merges routes that share the same methodName.
|
|
228
246
|
*/ mergeRoutes() {
|
|
229
|
-
this.options.routes =
|
|
247
|
+
this.options.routes = mergeByKey(this.options.routes, 'methodName', {
|
|
230
248
|
deep: true
|
|
231
249
|
});
|
|
232
250
|
}
|
|
233
|
-
|
|
234
|
-
options){
|
|
235
|
-
this.controller = controller;
|
|
236
|
-
this.decorators = [];
|
|
237
|
-
this.options = typeof options === 'function' ? // we'll need to call it again after receiving the user options to adjust its routes
|
|
238
|
-
options(this.adjustRoutes(_defaultoptions.defaultOptions)) : (0, _mergeoptions.mergeOptions)(_defaultoptions.defaultOptions, options || {});
|
|
239
|
-
this.options = this.adjustRoutes({
|
|
240
|
-
...this.options,
|
|
241
|
-
// todo: merge matched routes instead of overriding them
|
|
242
|
-
// i.e. routes with the same methodName, httpMethod, and path
|
|
243
|
-
routes: [
|
|
244
|
-
...this.options.routes || [],
|
|
245
|
-
...this.getExistingMethods()
|
|
246
|
-
]
|
|
247
|
-
});
|
|
248
|
-
// if path is not provided, use the class name, without the suffix `Controller`,
|
|
249
|
-
// after converting it into url-friendly kebab-case
|
|
250
|
-
if (this.options.path === undefined) {
|
|
251
|
-
this.options.path = (0, _javascript.toKebabCase)(this.controller.name.replace(/Controller$/, ''));
|
|
252
|
-
}
|
|
253
|
-
this.createRoutes();
|
|
254
|
-
this.mergeRoutes();
|
|
255
|
-
(0, _addcontrollerpath.addControllerPath)(this);
|
|
256
|
-
(0, _addcontrollertags.addControllerTags)(this);
|
|
257
|
-
(0, _reflect.applyDecorators)(this);
|
|
258
|
-
}
|
|
259
|
-
};
|
|
251
|
+
}
|
|
260
252
|
|
|
261
253
|
//# sourceMappingURL=controller.factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/decorators/controller/controller.factory.ts"],"sourcesContent":["import { Body, Param, Query, Req } from '@nestjs/common';\nimport type {\n ControllerOptions,\n HttpMethod,\n InitControllerOptions,\n InitRoute,\n IRoute,\n} from './types';\nimport { addHttpMethod } from './add-decorators/add-http-method';\nimport { addSwaggerParams } from './add-decorators/add-swagger-params';\nimport { routeHandler } from './route-handler';\nimport { addSwaggerOperation } from './add-decorators/add-swagger-operation';\nimport { addSwaggerBody } from './add-decorators/add-swagger-body';\nimport { addSwaggerResponse } from './add-decorators/add-swagger-response';\nimport { QueryInterceptor } from './interceptors/query.interceptor';\nimport { defaultOptions } from './default-options';\nimport { mergeOptions } from './utils/merge-options';\nimport { addControllerTags } from './add-decorators/add-controller-tags';\nimport { merge, mergeByKey, removeValues } from '@impactor/javascript';\nimport { applyDecorators } from './utils/reflect';\nimport { addControllerPath } from './add-decorators/add-controller-path';\nimport { toKebabCase } from '@impactor/javascript';\n// import 'reflect-metadata';\n\nexport class ControllerFactory {\n public options: ControllerOptions;\n public decorators: ClassDecorator[] = [];\n\n constructor(\n public controller: Function,\n // if options is provided as a function, the consumer has to maintain the merge strategy\n options?:\n | InitControllerOptions\n | ((defaultOptions: ControllerOptions) => ControllerOptions),\n ) {\n this.options =\n typeof options === 'function'\n ? // The consumer should receive the adjusted routes, so we need to call `adjustRoutes()` here\n // we'll need to call it again after receiving the user options to adjust its routes\n options(this.adjustRoutes(defaultOptions))\n : mergeOptions(defaultOptions, options || {});\n\n this.options = this.adjustRoutes({\n ...this.options,\n // todo: merge matched routes instead of overriding them\n // i.e. routes with the same methodName, httpMethod, and path\n routes: [...(this.options.routes || []), ...this.getExistingMethods()],\n });\n\n // if path is not provided, use the class name, without the suffix `Controller`,\n // after converting it into url-friendly kebab-case\n if (this.options.path === undefined) {\n this.options.path = toKebabCase(\n this.controller.name.replace(/Controller$/, ''),\n );\n }\n\n this.createRoutes();\n this.mergeRoutes();\n\n addControllerPath(this);\n addControllerTags(this);\n\n applyDecorators(this);\n }\n\n getExistingMethods() {\n let proto = this.controller.prototype;\n\n return (\n Reflect.ownKeys(proto)\n .filter((key) => typeof proto[key] === 'function')\n // convert symbol to string\n .map(String)\n .filter(\n (key) =>\n key !== 'constructor' &&\n // a method can be excluded from being handling by this factory, by adding `@Route(false)`\n Reflect.getMetadata('routeOptions', proto, key) !== false &&\n // skip methods that are already included in routes[]\n !this.options.routes?.some((route) => route.methodName === key),\n )\n // convert method to InitRoute{}\n .map((methodName) => {\n let routeOptions: InitRoute = Reflect.getMetadata(\n 'routeOptions',\n proto,\n methodName,\n );\n\n return {\n methodName,\n path: Reflect.getMetadata('path', proto[methodName]),\n ...routeOptions,\n } as InitRoute;\n })\n );\n }\n /**\n * add all missing props to each route, uch as the HTTP method, primaryKey, and operationId .\n * @param opts\n * @returns\n */\n adjustRoutes(opts: InitControllerOptions = this.options): ControllerOptions {\n return <ControllerOptions>{\n ...opts,\n routes: opts?.routes?.map((el) => {\n let route = { ...el };\n\n // NestJs request methods\n let requestMethods = [\n 'GET',\n 'POST',\n 'PUT',\n 'DELETE',\n 'PATCH',\n 'ALL',\n 'OPTIONS',\n 'HEAD',\n 'SEARCH',\n ];\n // capitalize route.httpMethod to much the HttpMethod type\n route.httpMethod = <HttpMethod>(\n (typeof route.httpMethod === 'number'\n ? requestMethods[route.httpMethod]\n : (<HttpMethod>route.httpMethod)?.toUpperCase() || 'GET')\n );\n route.primaryKey = route.primaryKey || opts.primaryKey || 'id';\n\n // generate the default methodName from path\n // examples\n // - {path: \"users/:id\"} -> \"getOneUserById\"\n // - {path: \"users/:userId/profiles/:profileId/posts\", many:true} -> \"getManyUsersByUserIdProfilesProfileIdPosts\"\n route.methodName =\n route.methodName ||\n route.httpMethod.toLowerCase() +\n (route.httpMethod === 'POST' ? '' : route.many ? 'Many' : 'One');\n\n route.operationId =\n route.operationId ||\n `${this.controller.name}_${String(route.methodName)}`;\n\n if (!route.path) {\n // for \"*One()\" operations, add /:id path param\n // example: GET /users/:id\n route.path =\n route.many || ['POST'].includes(route.httpMethod)\n ? '/'\n : `/:${route.primaryKey}`;\n }\n\n route.pathParams = route.pathParams\n ? route.pathParams.map((param) =>\n typeof param === 'string'\n ? { name: param, schema: { type: 'string' }, required: true }\n : param,\n )\n : // generate path params from route.path\n // matches `:id`, `/:id` and `other/:id/:userId`\n [...route.path.matchAll(/^\\/?:([^/]+)/g)].map((match) => ({\n name: match[1],\n schema: { type: 'string' },\n required: true,\n }));\n\n // todo: generate the corresponding service methods\n // +route.path\n // // remove the leading \"/\"\n // .replace(/^\\//, '')\n // .replace(/^:/,'By')\n // // replace \"/:id\" with \"ById\"\n // .replaceAll(/\\/:/g, '/By')\n // .split('/')\n // // convert parts to camel case\n // // example: \"users/profiles\" -> \"usersProfiles\"\n // .map((el, idx) =>\n // idx === 0 ? el : el.charAt(0).toUpperCase() + el.slice(1),\n // )\n // .join('');\n\n // override options per route\n // for example: route.primaryKey = route.primaryKey || opts.primaryKey\n // when opts.model is a class that extends another class, an exception is thrown `OtherClass is not defined`\n // todo: we may need to exclude other keys that have a class value such as errorResponseModel\n let { routes, model, ...otherOptions } = opts;\n // todo: use merge()?\n route = <IRoute>(\n merge(\n [otherOptions || {}, route || {}].map((el) =>\n removeValues(el, [undefined, null]),\n ),\n )\n );\n\n // merge arrays\n // todo: use mergeArray()?\n ['decorators', 'interceptors'].map((key) => {\n route[key as keyof typeof route] = [\n ...(opts[key as keyof typeof opts] || []),\n ...(route[key as keyof typeof route] || []),\n ];\n });\n\n // merge empty arrays\n // for example if route.interceptors=[] but opts.interceptors has values, opts.interceptors is used\n // Object.keys(opts).map((key) => {\n // if (Array.isArray(key) && !route[key as keyof typeof route]?.length) {\n // route[key as keyof typeof route] =\n // opts[key as keyof typeof opts] || [];\n // }\n // });\n\n route.interceptors!.push(\n new QueryInterceptor(\n merge([opts.query || {}, route.query || {}]) as {\n [key: string]: any;\n },\n ),\n );\n\n return route;\n }),\n };\n }\n\n /**\n * add options.routes[] as class methods\n */\n createRoutes() {\n let routes = this.options.routes?.filter(\n (route) => route.disabled === undefined || !route.disabled,\n ),\n controller = this.controller.prototype;\n\n for (let route of routes) {\n // create a route method and add it to the controller\n // todo: use interceptor to create ParsedRequest\n if (!controller[route.methodName]) {\n controller[route.methodName] = routeHandler(route, this);\n\n // inject the required params\n // similar to patchOne(@Req() req: Request, @Query() query, @Body() dto: MyEntity, @Param(\"id\") id:string)\n // we can get other info such as body and params from req\n // without the need to inject @Body() and @Query()\n // todo: route.primary || getPrimaryKeyFromEntity(opts.model) || \"id\"\n let paramKey = route.primaryKey || 'id';\n Req()(this.controller.prototype, route.methodName, 0);\n Query()(this.controller.prototype, route.methodName, 1);\n\n if (['GET', 'DELETE'].includes(route.httpMethod) && !route.many) {\n Param(paramKey)(this.controller.prototype, route.methodName, 2);\n } else if (route.httpMethod === 'POST') {\n Body()(this.controller.prototype, route.methodName, 2);\n // add 'design:paramtypes' to define the types of each param\n // for instance, the param `@Body()` should be defined as `this.options.model`,\n // so `ValidationPipe()` can work fine.\n // without it, wrong body will bypass class-validator and class-transformer decorators such as `@IsString()`\n Reflect.defineMetadata(\n 'design:paramtypes',\n // 0: `@Req() req: Request`\n // 1: `@Query() query: any` -> todo: add type definition\n // 2: `@Body() body: Model`\n [Request, Object, this.options.model],\n this.controller.prototype,\n route.methodName,\n );\n } else if (['PATCH', 'PUT'].includes(route.httpMethod)) {\n Body()(this.controller.prototype, route.methodName, 2);\n if (route.many) {\n Reflect.defineMetadata(\n 'design:paramtypes',\n [Object, Object, this.options.model],\n this.controller.prototype,\n route.methodName,\n );\n } else {\n // todo: or use `Param()` without `paramKey`, then use it like param[paramKey]\n // todo: accept list of pipes for each param -> `Param(paramName, ...pipes)(...)`\n // to accept only string or number -> add a validation in routerHandler i.e. `if(! typeof params[paramKey]) throw ...`,\n // as class-validator will not validate its type because it is not a DTO i.e. `getOne(@param() params: ParamsDTO)`\n Param(paramKey)(this.controller.prototype, route.methodName, 3);\n Reflect.defineMetadata(\n 'design:paramtypes',\n [Object, Object, this.options.model, Object],\n this.controller.prototype,\n route.methodName,\n );\n }\n }\n }\n\n // add decorators to the route\n // annotations are added only if:\n // - the method listed in this.options.routes[] and doesn't has @Route(false)\n // - the method is not listed and has @Route(options?)\n // if the method is not listed and doesn't has @Route(), the annotations will not be added\n // if no http decorator already applied such as @GET()\n // when adding @Get(), nest adds \"method\" and \"path\" metadata\n addHttpMethod(route, this);\n addSwaggerOperation(route, this);\n addSwaggerParams(route, this);\n addSwaggerBody(route, this);\n addSwaggerResponse(route, this);\n }\n }\n\n /**\n * Merges routes that share the same methodName.\n */\n mergeRoutes() {\n this.options.routes = mergeByKey<IRoute>(\n this.options.routes,\n 'methodName',\n {\n deep: true,\n },\n );\n }\n}\n"],"names":["ControllerFactory","getExistingMethods","proto","controller","prototype","Reflect","ownKeys","filter","key","map","String","getMetadata","options","routes","some","route","methodName","routeOptions","path","adjustRoutes","opts","el","requestMethods","httpMethod","toUpperCase","primaryKey","toLowerCase","many","operationId","name","includes","pathParams","param","schema","type","required","matchAll","match","model","otherOptions","merge","removeValues","undefined","interceptors","push","QueryInterceptor","query","createRoutes","disabled","routeHandler","paramKey","Req","Query","Param","Body","defineMetadata","Request","Object","addHttpMethod","addSwaggerOperation","addSwaggerParams","addSwaggerBody","addSwaggerResponse","mergeRoutes","mergeByKey","deep","decorators","defaultOptions","mergeOptions","toKebabCase","replace","addControllerPath","addControllerTags","applyDecorators"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/decorators/controller/controller.factory.ts"],"sourcesContent":["import { Body, Param, Query, Req } from '@nestjs/common';\nimport type {\n ControllerOptions,\n HttpMethod,\n InitControllerOptions,\n InitRoute,\n IRoute,\n} from './types/index.js';\nimport { addHttpMethod } from './add-decorators/add-http-method.js';\nimport { addSwaggerParams } from './add-decorators/add-swagger-params.js';\nimport { routeHandler } from './route-handler.js';\nimport { addSwaggerOperation } from './add-decorators/add-swagger-operation.js';\nimport { addSwaggerBody } from './add-decorators/add-swagger-body.js';\nimport { addSwaggerResponse } from './add-decorators/add-swagger-response.js';\nimport { QueryInterceptor } from './interceptors/query.interceptor.js';\nimport { defaultOptions } from './default-options.js';\nimport { mergeOptions } from './utils/merge-options.js';\nimport { addControllerTags } from './add-decorators/add-controller-tags.js';\nimport { merge, mergeByKey, removeValues } from '@impactor/javascript';\nimport { applyDecorators } from './utils/reflect.js';\nimport { addControllerPath } from './add-decorators/add-controller-path.js';\nimport { toKebabCase } from '@impactor/javascript';\n// import 'reflect-metadata';\n\nexport class ControllerFactory {\n public options: ControllerOptions;\n public decorators: ClassDecorator[] = [];\n\n constructor(\n public controller: Function,\n // if options is provided as a function, the consumer has to maintain the merge strategy\n options?:\n | InitControllerOptions\n | ((defaultOptions: ControllerOptions) => ControllerOptions),\n ) {\n this.options =\n typeof options === 'function'\n ? // The consumer should receive the adjusted routes, so we need to call `adjustRoutes()` here\n // we'll need to call it again after receiving the user options to adjust its routes\n options(this.adjustRoutes(defaultOptions))\n : mergeOptions(defaultOptions, options || {});\n\n this.options = this.adjustRoutes({\n ...this.options,\n // todo: merge matched routes instead of overriding them\n // i.e. routes with the same methodName, httpMethod, and path\n routes: [...(this.options.routes || []), ...this.getExistingMethods()],\n });\n\n // if path is not provided, use the class name, without the suffix `Controller`,\n // after converting it into url-friendly kebab-case\n if (this.options.path === undefined) {\n this.options.path = toKebabCase(\n this.controller.name.replace(/Controller$/, ''),\n );\n }\n\n this.createRoutes();\n this.mergeRoutes();\n\n addControllerPath(this);\n addControllerTags(this);\n\n applyDecorators(this);\n }\n\n getExistingMethods() {\n let proto = this.controller.prototype;\n\n return (\n Reflect.ownKeys(proto)\n .filter((key) => typeof proto[key] === 'function')\n // convert symbol to string\n .map(String)\n .filter(\n (key) =>\n key !== 'constructor' &&\n // a method can be excluded from being handling by this factory, by adding `@Route(false)`\n Reflect.getMetadata('routeOptions', proto, key) !== false &&\n // skip methods that are already included in routes[]\n !this.options.routes?.some((route) => route.methodName === key),\n )\n // convert method to InitRoute{}\n .map((methodName) => {\n let routeOptions: InitRoute = Reflect.getMetadata(\n 'routeOptions',\n proto,\n methodName,\n );\n\n return {\n methodName,\n path: Reflect.getMetadata('path', proto[methodName]),\n ...routeOptions,\n } as InitRoute;\n })\n );\n }\n /**\n * add all missing props to each route, uch as the HTTP method, primaryKey, and operationId .\n * @param opts\n * @returns\n */\n adjustRoutes(opts: InitControllerOptions = this.options): ControllerOptions {\n return <ControllerOptions>{\n ...opts,\n routes: opts?.routes?.map((el) => {\n let route = { ...el };\n\n // NestJs request methods\n let requestMethods = [\n 'GET',\n 'POST',\n 'PUT',\n 'DELETE',\n 'PATCH',\n 'ALL',\n 'OPTIONS',\n 'HEAD',\n 'SEARCH',\n ];\n // capitalize route.httpMethod to much the HttpMethod type\n route.httpMethod = <HttpMethod>(\n (typeof route.httpMethod === 'number'\n ? requestMethods[route.httpMethod]\n : (<HttpMethod>route.httpMethod)?.toUpperCase() || 'GET')\n );\n route.primaryKey = route.primaryKey || opts.primaryKey || 'id';\n\n // generate the default methodName from path\n // examples\n // - {path: \"users/:id\"} -> \"getOneUserById\"\n // - {path: \"users/:userId/profiles/:profileId/posts\", many:true} -> \"getManyUsersByUserIdProfilesProfileIdPosts\"\n route.methodName =\n route.methodName ||\n route.httpMethod.toLowerCase() +\n (route.httpMethod === 'POST' ? '' : route.many ? 'Many' : 'One');\n\n route.operationId =\n route.operationId ||\n `${this.controller.name}_${String(route.methodName)}`;\n\n if (!route.path) {\n // for \"*One()\" operations, add /:id path param\n // example: GET /users/:id\n route.path =\n route.many || ['POST'].includes(route.httpMethod)\n ? '/'\n : `/:${route.primaryKey}`;\n }\n\n route.pathParams = route.pathParams\n ? route.pathParams.map((param) =>\n typeof param === 'string'\n ? { name: param, schema: { type: 'string' }, required: true }\n : param,\n )\n : // generate path params from route.path\n // matches `:id`, `/:id` and `other/:id/:userId`\n [...route.path.matchAll(/^\\/?:([^/]+)/g)].map((match) => ({\n name: match[1],\n schema: { type: 'string' },\n required: true,\n }));\n\n // todo: generate the corresponding service methods\n // +route.path\n // // remove the leading \"/\"\n // .replace(/^\\//, '')\n // .replace(/^:/,'By')\n // // replace \"/:id\" with \"ById\"\n // .replaceAll(/\\/:/g, '/By')\n // .split('/')\n // // convert parts to camel case\n // // example: \"users/profiles\" -> \"usersProfiles\"\n // .map((el, idx) =>\n // idx === 0 ? el : el.charAt(0).toUpperCase() + el.slice(1),\n // )\n // .join('');\n\n // override options per route\n // for example: route.primaryKey = route.primaryKey || opts.primaryKey\n // when opts.model is a class that extends another class, an exception is thrown `OtherClass is not defined`\n // todo: we may need to exclude other keys that have a class value such as errorResponseModel\n let { routes, model, ...otherOptions } = opts;\n // todo: use merge()?\n route = <IRoute>(\n merge(\n [otherOptions || {}, route || {}].map((el) =>\n removeValues(el, [undefined, null]),\n ),\n )\n );\n\n // merge arrays\n // todo: use mergeArray()?\n ['decorators', 'interceptors'].map((key) => {\n route[key as keyof typeof route] = [\n ...(opts[key as keyof typeof opts] || []),\n ...(route[key as keyof typeof route] || []),\n ];\n });\n\n // merge empty arrays\n // for example if route.interceptors=[] but opts.interceptors has values, opts.interceptors is used\n // Object.keys(opts).map((key) => {\n // if (Array.isArray(key) && !route[key as keyof typeof route]?.length) {\n // route[key as keyof typeof route] =\n // opts[key as keyof typeof opts] || [];\n // }\n // });\n\n route.interceptors!.push(\n new QueryInterceptor(\n merge([opts.query || {}, route.query || {}]) as {\n [key: string]: any;\n },\n ),\n );\n\n return route;\n }),\n };\n }\n\n /**\n * add options.routes[] as class methods\n */\n createRoutes() {\n let routes = this.options.routes?.filter(\n (route) => route.disabled === undefined || !route.disabled,\n ),\n controller = this.controller.prototype;\n\n for (let route of routes) {\n // create a route method and add it to the controller\n // todo: use interceptor to create ParsedRequest\n if (!controller[route.methodName]) {\n controller[route.methodName] = routeHandler(route, this);\n\n // inject the required params\n // similar to patchOne(@Req() req: Request, @Query() query, @Body() dto: MyEntity, @Param(\"id\") id:string)\n // we can get other info such as body and params from req\n // without the need to inject @Body() and @Query()\n // todo: route.primary || getPrimaryKeyFromEntity(opts.model) || \"id\"\n let paramKey = route.primaryKey || 'id';\n Req()(this.controller.prototype, route.methodName, 0);\n Query()(this.controller.prototype, route.methodName, 1);\n\n if (['GET', 'DELETE'].includes(route.httpMethod) && !route.many) {\n Param(paramKey)(this.controller.prototype, route.methodName, 2);\n } else if (route.httpMethod === 'POST') {\n Body()(this.controller.prototype, route.methodName, 2);\n // add 'design:paramtypes' to define the types of each param\n // for instance, the param `@Body()` should be defined as `this.options.model`,\n // so `ValidationPipe()` can work fine.\n // without it, wrong body will bypass class-validator and class-transformer decorators such as `@IsString()`\n Reflect.defineMetadata(\n 'design:paramtypes',\n // 0: `@Req() req: Request`\n // 1: `@Query() query: any` -> todo: add type definition\n // 2: `@Body() body: Model`\n [Request, Object, this.options.model],\n this.controller.prototype,\n route.methodName,\n );\n } else if (['PATCH', 'PUT'].includes(route.httpMethod)) {\n Body()(this.controller.prototype, route.methodName, 2);\n if (route.many) {\n Reflect.defineMetadata(\n 'design:paramtypes',\n [Object, Object, this.options.model],\n this.controller.prototype,\n route.methodName,\n );\n } else {\n // todo: or use `Param()` without `paramKey`, then use it like param[paramKey]\n // todo: accept list of pipes for each param -> `Param(paramName, ...pipes)(...)`\n // to accept only string or number -> add a validation in routerHandler i.e. `if(! typeof params[paramKey]) throw ...`,\n // as class-validator will not validate its type because it is not a DTO i.e. `getOne(@param() params: ParamsDTO)`\n Param(paramKey)(this.controller.prototype, route.methodName, 3);\n Reflect.defineMetadata(\n 'design:paramtypes',\n [Object, Object, this.options.model, Object],\n this.controller.prototype,\n route.methodName,\n );\n }\n }\n }\n\n // add decorators to the route\n // annotations are added only if:\n // - the method listed in this.options.routes[] and doesn't has @Route(false)\n // - the method is not listed and has @Route(options?)\n // if the method is not listed and doesn't has @Route(), the annotations will not be added\n // if no http decorator already applied such as @GET()\n // when adding @Get(), nest adds \"method\" and \"path\" metadata\n addHttpMethod(route, this);\n addSwaggerOperation(route, this);\n addSwaggerParams(route, this);\n addSwaggerBody(route, this);\n addSwaggerResponse(route, this);\n }\n }\n\n /**\n * Merges routes that share the same methodName.\n */\n mergeRoutes() {\n this.options.routes = mergeByKey<IRoute>(\n this.options.routes,\n 'methodName',\n {\n deep: true,\n },\n );\n }\n}\n"],"names":["Body","Param","Query","Req","addHttpMethod","addSwaggerParams","routeHandler","addSwaggerOperation","addSwaggerBody","addSwaggerResponse","QueryInterceptor","defaultOptions","mergeOptions","addControllerTags","merge","mergeByKey","removeValues","applyDecorators","addControllerPath","toKebabCase","ControllerFactory","controller","options","decorators","adjustRoutes","routes","getExistingMethods","path","undefined","name","replace","createRoutes","mergeRoutes","proto","prototype","Reflect","ownKeys","filter","key","map","String","getMetadata","some","route","methodName","routeOptions","opts","el","requestMethods","httpMethod","toUpperCase","primaryKey","toLowerCase","many","operationId","includes","pathParams","param","schema","type","required","matchAll","match","model","otherOptions","interceptors","push","query","disabled","paramKey","defineMetadata","Request","Object","deep"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { InitControllerOptions } from './types';
|
|
1
|
+
import type { InitControllerOptions } from './types/index.js';
|
|
2
2
|
export declare const defaultOptions: Partial<InitControllerOptions>;
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Object.defineProperty(exports, "defaultOptions", {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: function() {
|
|
9
|
-
return defaultOptions;
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
const defaultOptions = {
|
|
2
|
+
// todo: add opts.maxTake to limit ?take=
|
|
3
|
+
/**
|
|
4
|
+
* the default options for \@Controller()
|
|
5
|
+
*/ export const defaultOptions = {
|
|
13
6
|
// todo: allow sub routes, such as `/:field`
|
|
14
7
|
routes: [
|
|
15
8
|
{
|
|
@@ -74,8 +67,7 @@ const defaultOptions = {
|
|
|
74
67
|
},
|
|
75
68
|
{
|
|
76
69
|
name: 'skip',
|
|
77
|
-
description: 'skip rows.',
|
|
78
|
-
example: 2,
|
|
70
|
+
description: 'skip rows. example: `2`',
|
|
79
71
|
many: true,
|
|
80
72
|
httpMethods: [
|
|
81
73
|
'GET',
|
|
@@ -98,21 +90,16 @@ const defaultOptions = {
|
|
|
98
90
|
},
|
|
99
91
|
{
|
|
100
92
|
name: 'relations',
|
|
101
|
-
description: 'add joined relational objects, supports nested relations',
|
|
102
|
-
example: [
|
|
103
|
-
'user.profile:name,age'
|
|
104
|
-
],
|
|
93
|
+
description: 'add joined relational objects, supports nested relations. example: `["age", "user.profile:name"]`',
|
|
105
94
|
httpMethods: [
|
|
106
95
|
'GET'
|
|
107
96
|
]
|
|
108
97
|
},
|
|
109
98
|
{
|
|
110
99
|
name: 'where',
|
|
111
|
-
description: 'add where condition. to use sql statement, start the value with `>`.\n to search in an array column, put the value in an array. see <a href="https://orkhan.gitbook.io/typeorm/docs/find-options#advanced-options" target="_blank">see advanced options</a>',
|
|
112
|
-
example
|
|
113
|
-
|
|
114
|
-
email: ">Like '%john%'"
|
|
115
|
-
},
|
|
100
|
+
description: 'add where condition. to use sql statement, start the value with `>`.\n to search in an array column, put the value in an array. see <a href="https://orkhan.gitbook.io/typeorm/docs/find-options#advanced-options" target="_blank">see advanced options</a>\n example: `{ name: "john", email: ">Like \'%john@%\'" }`',
|
|
101
|
+
// when adding an example here, Swagger adds it to the query by default, causing the query to has properties may not be defined in the entity
|
|
102
|
+
// example: { name: 'john', email: ">Like '%john@%'" },
|
|
116
103
|
many: true,
|
|
117
104
|
httpMethods: [
|
|
118
105
|
'GET',
|
|
@@ -124,10 +111,8 @@ const defaultOptions = {
|
|
|
124
111
|
{
|
|
125
112
|
name: 'order',
|
|
126
113
|
// or 'name,id:DESC'
|
|
127
|
-
example: {
|
|
128
|
-
|
|
129
|
-
id: 'DESC'
|
|
130
|
-
},
|
|
114
|
+
// example: { name: 'ASC', id: 'DESC' },
|
|
115
|
+
description: "example: `{ name: 'ASC', id: 'DESC' }`",
|
|
131
116
|
many: true,
|
|
132
117
|
httpMethods: [
|
|
133
118
|
'GET',
|