@nestjsx/crud
Advanced tools
Comparing version 1.1.3 to 2.0.0
@@ -13,3 +13,3 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; | ||
throwBadRequestException(msg?: any): BadRequestException; | ||
throwNotFoundException(name?: string): NotFoundException; | ||
throwNotFoundException(name: string): NotFoundException; | ||
} |
@@ -10,4 +10,3 @@ "use strict"; | ||
throwNotFoundException(name) { | ||
const msg = name ? `${name} not found` : `Not found`; | ||
throw new common_1.NotFoundException(msg); | ||
throw new common_1.NotFoundException(`${name} not found`); | ||
} | ||
@@ -14,0 +13,0 @@ } |
@@ -1,7 +0,5 @@ | ||
import { ValidationPipeOptions } from '@nestjs/common'; | ||
interface CrudOptions { | ||
validation?: ValidationPipeOptions; | ||
} | ||
import { CrudOptions } from '../interfaces'; | ||
declare type BaseRouteName = 'getManyBase' | 'getOneBase' | 'createOneBase' | 'createManyBase' | 'updateOneBase' | 'deleteOneBase'; | ||
export declare const Crud: (dto: any, crudOptions?: CrudOptions) => (target: object) => void; | ||
export declare const Override: (name?: "getManyBase" | "getOneBase" | "createOneBase" | "updateOneBase" | "deleteOneBase") => (target: any, key: any, descriptor: PropertyDescriptor) => PropertyDescriptor; | ||
export declare const Override: (name?: BaseRouteName) => (target: any, key: any, descriptor: PropertyDescriptor) => PropertyDescriptor; | ||
export {}; |
@@ -14,8 +14,8 @@ "use strict"; | ||
const route_paramtypes_enum_1 = require("@nestjs/common/enums/route-paramtypes.enum"); | ||
const constants_1 = require("@nestjs/common/constants"); | ||
const dto_1 = require("../dto"); | ||
const enums_1 = require("../enums"); | ||
const interceptors_1 = require("../interceptors"); | ||
const constants_2 = require("../constants"); | ||
const constants_1 = require("../constants"); | ||
const utils_1 = require("../utils"); | ||
const helpers_1 = require("./helpers"); | ||
exports.Crud = (dto, crudOptions = {}) => (target) => { | ||
@@ -55,20 +55,20 @@ const prototype = target.prototype; | ||
}; | ||
getParamsFilter(prototype); | ||
getMergedOptions(prototype); | ||
getManyBase(target, baseRoutes.getManyBase.name); | ||
getOneBase(target, baseRoutes.getOneBase.name); | ||
createOneBase(target, baseRoutes.createOneBase.name, dto, crudOptions); | ||
createManyBase(target, baseRoutes.createManyBase.name, dto, crudOptions); | ||
updateOneBase(target, baseRoutes.updateOneBase.name, dto, crudOptions); | ||
deleteOneBase(target, baseRoutes.deleteOneBase.name); | ||
getParamsFilterInit(prototype, crudOptions); | ||
getMergedOptionsInit(prototype, crudOptions); | ||
getManyBaseInit(target, baseRoutes.getManyBase.name, dto, crudOptions); | ||
getOneBaseInit(target, baseRoutes.getOneBase.name, dto, crudOptions); | ||
createOneBaseInit(target, baseRoutes.createOneBase.name, dto, crudOptions); | ||
createManyBaseInit(target, baseRoutes.createManyBase.name, dto, crudOptions); | ||
updateOneBaseInit(target, baseRoutes.updateOneBase.name, dto, crudOptions); | ||
deleteOneBaseInit(target, baseRoutes.deleteOneBase.name, crudOptions); | ||
Object.getOwnPropertyNames(prototype).forEach((name) => { | ||
const overrided = getOverrideMetadata(prototype[name]); | ||
const overrided = helpers_1.getOverrideMetadata(prototype[name]); | ||
const route = baseRoutes[overrided]; | ||
if (overrided && route) { | ||
const interceptors = getInterceptors(prototype[name]) || []; | ||
const baseInterceptors = getInterceptors(prototype[overrided]) || []; | ||
const baseAction = getAction(prototype[overrided]); | ||
setInterceptors([...interceptors, ...baseInterceptors], prototype[name]); | ||
setAction(baseAction, prototype[name]); | ||
setRoute(route.path, route.method, prototype[name]); | ||
const interceptors = helpers_1.getInterceptors(prototype[name]) || []; | ||
const baseInterceptors = helpers_1.getInterceptors(prototype[overrided]) || []; | ||
const baseAction = helpers_1.getAction(prototype[overrided]); | ||
helpers_1.setInterceptors([...interceptors, ...baseInterceptors], prototype[name]); | ||
helpers_1.setAction(baseAction, prototype[name]); | ||
helpers_1.setRoute(route.path, route.method, prototype[name]); | ||
route.override = true; | ||
@@ -80,3 +80,3 @@ } | ||
if (!route.override) { | ||
setRoute(route.path, route.method, prototype[route.name]); | ||
helpers_1.setRoute(route.path, route.method, prototype[route.name]); | ||
} | ||
@@ -86,42 +86,47 @@ }); | ||
exports.Override = (name) => (target, key, descriptor) => { | ||
Reflect.defineMetadata(constants_2.OVERRIDE_METHOD_METADATA, name || `${key}Base`, target[key]); | ||
Reflect.defineMetadata(constants_1.OVERRIDE_METHOD_METADATA, name || `${key}Base`, target[key]); | ||
return descriptor; | ||
}; | ||
function getManyBase(target, name) { | ||
function getManyBaseInit(target, name, dto, crudOptions) { | ||
const prototype = target.prototype; | ||
prototype[name] = function (params, query) { | ||
prototype[name] = function getManyBase(params, query) { | ||
const mergedOptions = this.getMergedOptions(params); | ||
return this.service.getMany(query, mergedOptions); | ||
}; | ||
setParams(Object.assign({}, createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0), createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.QUERY, 1)), target, name); | ||
setParamTypes([Object, dto_1.RestfulParamsDto], prototype, name); | ||
setInterceptors([interceptors_1.RestfulQueryInterceptor], prototype[name]); | ||
setAction(enums_1.CrudActions.ReadAll, prototype[name]); | ||
helpers_1.setParams(Object.assign({}, helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0), helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.QUERY, 1)), target, name); | ||
helpers_1.setParamTypes([Object, dto_1.RestfulParamsDto], prototype, name); | ||
helpers_1.setInterceptors([interceptors_1.RestfulQueryInterceptor], prototype[name]); | ||
helpers_1.setAction(enums_1.CrudActions.ReadAll, prototype[name]); | ||
helpers_1.setSwaggerParams(prototype[name], crudOptions); | ||
helpers_1.setSwaggerQueryGetMany(prototype[name], dto.name); | ||
} | ||
function getOneBase(target, name) { | ||
function getOneBaseInit(target, name, dto, crudOptions) { | ||
const prototype = target.prototype; | ||
prototype[name] = function (id, params, query) { | ||
prototype[name] = function getOneBase(id, params, query) { | ||
const mergedOptions = this.getMergedOptions(params); | ||
return this.service.getOne(id, query, mergedOptions); | ||
}; | ||
setParams(Object.assign({}, createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0, [setParseIntPipe()], 'id'), createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 1), createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.QUERY, 2)), target, name); | ||
setParamTypes([Number, Object, dto_1.RestfulParamsDto], prototype, name); | ||
setInterceptors([interceptors_1.RestfulQueryInterceptor], prototype[name]); | ||
setAction(enums_1.CrudActions.ReadOne, prototype[name]); | ||
helpers_1.setParams(Object.assign({}, helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0, [helpers_1.setParseIntPipe()], 'id'), helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 1), helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.QUERY, 2)), target, name); | ||
helpers_1.setParamTypes([Number, Object, dto_1.RestfulParamsDto], prototype, name); | ||
helpers_1.setInterceptors([interceptors_1.RestfulQueryInterceptor], prototype[name]); | ||
helpers_1.setAction(enums_1.CrudActions.ReadOne, prototype[name]); | ||
helpers_1.setSwaggerParams(prototype[name], crudOptions); | ||
helpers_1.setSwaggerQueryGetOne(prototype[name], dto.name); | ||
} | ||
function createOneBase(target, name, dto, crudOptions) { | ||
function createOneBaseInit(target, name, dto, crudOptions) { | ||
const prototype = target.prototype; | ||
prototype[name] = function (params, body) { | ||
prototype[name] = function createOneBase(params, body) { | ||
const paramsFilter = this.getParamsFilter(params); | ||
return this.service.createOne(body, paramsFilter); | ||
}; | ||
setParams(Object.assign({}, createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0), createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.BODY, 1, [ | ||
setValidationPipe(crudOptions, enums_1.CrudValidate.CREATE), | ||
helpers_1.setParams(Object.assign({}, helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0), helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.BODY, 1, [ | ||
helpers_1.setValidationPipe(crudOptions, enums_1.CrudValidate.CREATE), | ||
])), target, name); | ||
setParamTypes([Object, dto], prototype, name); | ||
setAction(enums_1.CrudActions.CreateOne, prototype[name]); | ||
helpers_1.setParamTypes([Object, dto], prototype, name); | ||
helpers_1.setAction(enums_1.CrudActions.CreateOne, prototype[name]); | ||
helpers_1.setSwaggerParams(prototype[name], crudOptions); | ||
} | ||
function createManyBase(target, name, dto, crudOptions) { | ||
function createManyBaseInit(target, name, dto, crudOptions) { | ||
const prototype = target.prototype; | ||
prototype[name] = function (params, body) { | ||
prototype[name] = function createManyBase(params, body) { | ||
const paramsFilter = this.getParamsFilter(params); | ||
@@ -143,40 +148,43 @@ return this.service.createMany(body, paramsFilter); | ||
], BulkDto.prototype, "bulk", void 0); | ||
setParams(Object.assign({}, createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0), createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.BODY, 1, [ | ||
setValidationPipe(crudOptions, enums_1.CrudValidate.CREATE), | ||
helpers_1.setParams(Object.assign({}, helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0), helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.BODY, 1, [ | ||
helpers_1.setValidationPipe(crudOptions, enums_1.CrudValidate.CREATE), | ||
])), target, name); | ||
setParamTypes([Object, utils_1.hasValidator ? BulkDto : {}], prototype, name); | ||
setAction(enums_1.CrudActions.CreateMany, prototype[name]); | ||
helpers_1.setParamTypes([Object, utils_1.hasValidator ? BulkDto : {}], prototype, name); | ||
helpers_1.setAction(enums_1.CrudActions.CreateMany, prototype[name]); | ||
helpers_1.setSwaggerParams(prototype[name], crudOptions); | ||
} | ||
function updateOneBase(target, name, dto, crudOptions) { | ||
function updateOneBaseInit(target, name, dto, crudOptions) { | ||
const prototype = target.prototype; | ||
prototype[name] = function (id, params, body) { | ||
prototype[name] = function updateOneBase(id, params, body) { | ||
const paramsFilter = this.getParamsFilter(params); | ||
return this.service.updateOne(id, body, paramsFilter); | ||
}; | ||
setParams(Object.assign({}, createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0, [setParseIntPipe()], 'id'), createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 1), createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.BODY, 2, [ | ||
setValidationPipe(crudOptions, enums_1.CrudValidate.UPDATE), | ||
helpers_1.setParams(Object.assign({}, helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0, [helpers_1.setParseIntPipe()], 'id'), helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 1), helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.BODY, 2, [ | ||
helpers_1.setValidationPipe(crudOptions, enums_1.CrudValidate.UPDATE), | ||
])), target, name); | ||
setParamTypes([Number, Object, dto], prototype, name); | ||
setAction(enums_1.CrudActions.UpdateOne, prototype[name]); | ||
helpers_1.setParamTypes([Number, Object, dto], prototype, name); | ||
helpers_1.setAction(enums_1.CrudActions.UpdateOne, prototype[name]); | ||
helpers_1.setSwaggerParams(prototype[name], crudOptions); | ||
} | ||
function deleteOneBase(target, name) { | ||
function deleteOneBaseInit(target, name, crudOptions) { | ||
const prototype = target.prototype; | ||
prototype[name] = function (id, params) { | ||
prototype[name] = function deleteOneBase(id, params) { | ||
const paramsFilter = this.getParamsFilter(params); | ||
return this.service.deleteOne(id, paramsFilter); | ||
}; | ||
setParams(Object.assign({}, createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0, [setParseIntPipe()], 'id'), createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 1)), target, name); | ||
setParamTypes([Number, Object], prototype, name); | ||
setAction(enums_1.CrudActions.DeleteOne, prototype[name]); | ||
helpers_1.setParams(Object.assign({}, helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 0, [helpers_1.setParseIntPipe()], 'id'), helpers_1.createParamMetadata(route_paramtypes_enum_1.RouteParamtypes.PARAM, 1)), target, name); | ||
helpers_1.setParamTypes([Number, Object], prototype, name); | ||
helpers_1.setAction(enums_1.CrudActions.DeleteOne, prototype[name]); | ||
helpers_1.setSwaggerParams(prototype[name], crudOptions); | ||
} | ||
function getParamsFilter(prototype) { | ||
prototype['getParamsFilter'] = function (params) { | ||
if (!this.paramsFilter || !params) { | ||
function getParamsFilterInit(prototype, crudOptions) { | ||
prototype['getParamsFilter'] = function getParamsFilter(params) { | ||
if (!crudOptions.params || !params) { | ||
return []; | ||
} | ||
const isArray = Array.isArray(this.paramsFilter); | ||
return (isArray ? this.paramsFilter : Object.keys(this.paramsFilter)) | ||
const isArray = Array.isArray(crudOptions.params); | ||
return (isArray ? crudOptions.params : Object.keys(crudOptions.params)) | ||
.filter((field) => !!params[field]) | ||
.map((field) => ({ | ||
field: isArray ? field : this.paramsFilter[field], | ||
field: isArray ? field : crudOptions.params[field], | ||
operator: 'eq', | ||
@@ -187,6 +195,6 @@ value: params[field], | ||
} | ||
function getMergedOptions(prototype) { | ||
prototype['getMergedOptions'] = function (params) { | ||
function getMergedOptionsInit(prototype, crudOptions) { | ||
prototype['getMergedOptions'] = function getMergedOptions(params) { | ||
const paramsFilter = this.getParamsFilter(params); | ||
const options = this.options || {}; | ||
const options = Object.assign({}, crudOptions.options || {}); | ||
const optionsFilter = options.filter || []; | ||
@@ -200,45 +208,2 @@ const filter = [...optionsFilter, ...paramsFilter]; | ||
} | ||
function setRoute(path, method, func) { | ||
Reflect.defineMetadata(constants_1.PATH_METADATA, path, func); | ||
Reflect.defineMetadata(constants_1.METHOD_METADATA, method, func); | ||
} | ||
function setParamTypes(args, prototype, name) { | ||
Reflect.defineMetadata(constants_1.PARAMTYPES_METADATA, args, prototype, name); | ||
} | ||
function setParams(metadata, target, name) { | ||
Reflect.defineMetadata(constants_1.ROUTE_ARGS_METADATA, metadata, target, name); | ||
} | ||
function setInterceptors(interceptors, func) { | ||
Reflect.defineMetadata(constants_1.INTERCEPTORS_METADATA, interceptors, func); | ||
} | ||
function setAction(action, func) { | ||
Reflect.defineMetadata(constants_2.ACTION_NAME_METADATA, action, func); | ||
} | ||
function createParamMetadata(paramtype, index, pipes = [], data = undefined) { | ||
return { | ||
[`${paramtype}:${index}`]: { | ||
index, | ||
pipes, | ||
data, | ||
}, | ||
}; | ||
} | ||
function getOverrideMetadata(func) { | ||
return Reflect.getMetadata(constants_2.OVERRIDE_METHOD_METADATA, func); | ||
} | ||
function getInterceptors(func) { | ||
return Reflect.getMetadata(constants_1.INTERCEPTORS_METADATA, func); | ||
} | ||
function getAction(func) { | ||
return Reflect.getMetadata(constants_2.ACTION_NAME_METADATA, func); | ||
} | ||
function setValidationPipe(crudOptions = {}, group) { | ||
const options = crudOptions.validation || {}; | ||
return utils_1.hasValidator | ||
? new common_1.ValidationPipe(Object.assign({}, options, { groups: [group], transform: false })) | ||
: undefined; | ||
} | ||
function setParseIntPipe() { | ||
return utils_1.hasTypeorm ? new common_1.ParseIntPipe() : undefined; | ||
} | ||
//# sourceMappingURL=crud.decorator.js.map |
@@ -0,2 +1,5 @@ | ||
import { Type } from '@nestjs/common'; | ||
export declare const Feature: (name: string) => (target: object, key?: any, descriptor?: any) => any; | ||
export declare const Action: (name: string) => (target: object, key?: any, descriptor?: any) => any; | ||
export declare const getFeature: <T = any>(target: Type<T>) => any; | ||
export declare const getAction: (target: Function) => any; |
@@ -7,2 +7,4 @@ "use strict"; | ||
exports.Action = (name) => common_1.ReflectMetadata(constants_1.ACTION_NAME_METADATA, name); | ||
exports.getFeature = (target) => Reflect.getMetadata(constants_1.FEAUTURE_NAME_METADATA, target); | ||
exports.getAction = (target) => Reflect.getMetadata(constants_1.ACTION_NAME_METADATA, target); | ||
//# sourceMappingURL=feature-action.decorator.js.map |
@@ -0,1 +1,2 @@ | ||
import { ValidationPipeOptions } from '@nestjs/common'; | ||
import { ObjectLiteral } from './object-literal.interface'; | ||
@@ -7,4 +8,2 @@ import { RestfulOptions } from './restful-options.interface'; | ||
service: S; | ||
paramsFilter?: string[] | ObjectLiteral; | ||
options?: RestfulOptions; | ||
getManyBase?(params: ObjectLiteral, query: RestfulParamsDto): Promise<T[]>; | ||
@@ -20,1 +19,6 @@ getOneBase?(id: number, params: ObjectLiteral, query: RestfulParamsDto): Promise<T>; | ||
} | ||
export interface CrudOptions { | ||
options?: RestfulOptions; | ||
params?: ObjectLiteral | string[]; | ||
validation?: ValidationPipeOptions; | ||
} |
{ | ||
"name": "@nestjsx/crud", | ||
"version": "1.1.3", | ||
"version": "2.0.0", | ||
"description": "NestJs CRUD for RESTful APIs", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
146
README.md
@@ -36,2 +36,3 @@ <p align="center"> | ||
- [API Endpoints](#api-endpoints) | ||
- [Swagger](#swagger) | ||
- [Query Parameters](#query-parameters) | ||
@@ -41,7 +42,7 @@ - [Repository Service](#repository-service) | ||
- [Crud Controller](#crud-controller) | ||
- [Restful Options merge](#restful-options-merge) | ||
- [Path Filter](#path-filter) | ||
- [Validation](#validation) | ||
- [IntelliSense](#intellisense) | ||
- [Method Override](#method-override) | ||
- [Restful Options merge](#restful-options-merge) | ||
- [Path Filter](#path-filter) | ||
- [Additional Decorators](#additional-decorators) | ||
@@ -189,2 +190,6 @@ - [Example Project](#example-project) | ||
## Swagger | ||
[Swagger](https://docs.nestjs.com/recipes/swagger) support is present out of the box, including [Query Parameters](#query-parameters) and [Path Filter](#path-filter). | ||
## Query Parameters | ||
@@ -200,3 +205,3 @@ | ||
- [**`limit`**](#limit) (alias `per_page`) - receive `N` amount of entities | ||
- [**`offset`**](#offset) (alias `skip`) - offset `N` amount of entities | ||
- [**`offset`**](#offset) - offset `N` amount of entities | ||
- [**`page`**](#page) - receive a portion of `limit` (`per_page`) entities (alternative to `offset`) | ||
@@ -540,4 +545,67 @@ - [**`cache`**](#cache) - reset cache (if was enabled) and receive entities from the DB | ||
Our newly generated working horse. Let's dive in some details. | ||
Our newly generated working horse. `@Crud()` decorator accepts two arguments - Entity class and `CrudOptions` object. Let's dive in some details. | ||
### Restful Options merge | ||
```typescript | ||
... | ||
import { Crud } from '@nestjsx/crud'; | ||
@Crud(Hero, { | ||
options: { | ||
// RestfulOptions goes here | ||
} | ||
}) | ||
@Controller('heroes') | ||
export class HeroesCrudController { | ||
constructor(public service: HeroesService) {} | ||
} | ||
``` | ||
`CrudOptions` object may have `options` parameter which is the same object as [Restful Options](#restful-options). | ||
**_Notice:_** If you have this options set up in your `RepositoryService`, in that case they will be **merged**. | ||
### Path Filter | ||
`CrudOptions` object may have `params` parameter that will be used for auto filtering by URL path parameters. | ||
Assume, you have an entity `User` that belongs to some `Company` and has a field `companyId`. And you whant to create `UsersController` so that an admin could access users from his own Company only. Let's do this: | ||
```typescript | ||
... | ||
import { Crud } from '@nestjsx/crud'; | ||
@Crud(Hero, { | ||
params: ['companyId'] | ||
}) | ||
@Controller('/company/:companyId/users') | ||
export class UsersCrud { | ||
constructor(public service: UsersService) {} | ||
} | ||
``` | ||
In this example you're URL param name `companyId` should match the name of `User.companyId` field. If not, you can do mapping, like this: | ||
```typescript | ||
... | ||
import { Crud } from '@nestjsx/crud'; | ||
@Crud(Hero, { | ||
params: { | ||
company: 'companyId' | ||
} | ||
}) | ||
@Controller('/company/:company/users') | ||
export class UsersCrud { | ||
constructor(public service: UsersService) {} | ||
} | ||
``` | ||
Where `company` is the name of the URL param, and `companyId` is the name of the entity field. | ||
As you might guess, all request will add `companyId` to the DB queries alongside with the `:id` of `GET`, `PATCH`, `DELETE` requests. On `POST` (both: one and bulk) requests, `companyId` will be added to the `dto` automatically. | ||
When you done with the controller, you'll need to add some logic to your `AuthGuard` or any other interface, where you do the authorization of a requester. You will need to match `companyId` URL param with the `user.companyId` entity that has been validated from the DB. | ||
### Validation | ||
@@ -731,63 +799,2 @@ | ||
### Restful Options merge | ||
```typescript | ||
... | ||
import { Crud, CrudController, RestfulOptions } from '@nestjsx/crud'; | ||
@Crud(Hero) | ||
@Controller('heroes') | ||
export class HeroesCrud implements CrudController<HeroesService, Hero> { | ||
options: RestfulOptions = {}; | ||
constructor(public service: HeroesService) {} | ||
} | ||
``` | ||
Controller can accept optioanl `options` parameter and it's the same object as [Restful Options](#restful-options). It's very useful when you have one `RepositoryService` and several controllers. | ||
**_Notice:_** If you have this options set up in your `RepositoryService`, in that case they will be **merged**. | ||
### Path Filter | ||
Assume, you have an entity `User` that belongs to some `Company` and has a field `companyId`. And you whant to create `UsersController` so that an admin could access users from his own Company only. Let's do this: | ||
```typescript | ||
... | ||
import { Crud, CrudController } from '@nestjsx/crud'; | ||
@Crud(Hero) | ||
@Controller('/company/:companyId/users') | ||
export class UsersCrud implements CrudController<UsersService, User> { | ||
paramsFilter = ['companyId']; | ||
constructor(public service: UsersService) {} | ||
} | ||
``` | ||
A property `paramsFilter` is designed fo that purpose. | ||
In this example you're URL param name `companyId` should match the name of `User.companyId` field. If not, you can do mapping, like this: | ||
```typescript | ||
... | ||
import { Crud, CrudController } from '@nestjsx/crud'; | ||
@Crud(Hero) | ||
@Controller('/company/:company/users') | ||
export class UsersCrud implements CrudController<UsersService, User> { | ||
paramsFilter = { | ||
company: 'companyId' | ||
}; | ||
constructor(public service: UsersService) {} | ||
} | ||
``` | ||
Where `company` - name of the URL param, and `companyId` - name of the entity field. | ||
As you might guess, all request will add `companyId` to the DB queries alongside with the `:id` of `GET`, `PATCH`, `DELETE` requests. On `POST` (both: one and bulk) requests, `companyId` will be added to the `dto` automatically. | ||
When you done with the controller, you'll need to add some logic to your `AuthGuard` or any other interface, where you do the authorization of a requester. You will need to match `companyId` URL param with the `user.companyId` entity that has been validated from the DB. | ||
### Additional Decorators | ||
@@ -825,7 +832,10 @@ | ||
```typescript | ||
import { Reflector } from '@nestjs/core'; | ||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; | ||
import { FEAUTURE_NAME_METADATA, ACTION_NAME_METADATA } from '@nestjsx/crud'; | ||
import { getFeature, getAction } from '@nestjsx/crud'; | ||
@Injectable() | ||
export class ACLGuard implements CanActivate { | ||
constructor(private reflector: Reflector) {} | ||
canActivate(ctx: ExecutionContext): boolean { | ||
@@ -835,4 +845,4 @@ const handler = ctx.getHandler(); | ||
const feature = this.reflector.get(FEAUTURE_NAME_METADATA, controller); | ||
const action = this.reflector.get(ACTION_NAME_METADATA, handler); | ||
const feature = getFeature(controller); | ||
const action = getAction(handler); | ||
@@ -839,0 +849,0 @@ console.log(`${feature}-${action}`); // e.g. 'Heroes-Read-All' |
@@ -22,3 +22,3 @@ import { Repository, DeepPartial } from 'typeorm'; | ||
private getOneOrFail; | ||
private query; | ||
private buildQuery; | ||
private plainToClass; | ||
@@ -25,0 +25,0 @@ private onInitMapEntityColumns; |
@@ -34,3 +34,3 @@ "use strict"; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const builder = yield this.query(query, options); | ||
const builder = yield this.buildQuery(query, options); | ||
return builder.getMany(); | ||
@@ -92,3 +92,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const builder = yield this.query({ filter, fields, join, cache }, options, false); | ||
const builder = yield this.buildQuery({ filter, fields, join, cache }, options, false); | ||
const found = yield builder.getOne(); | ||
@@ -101,3 +101,3 @@ if (!found) { | ||
} | ||
query(query, options = {}, many = true) { | ||
buildQuery(query, options = {}, many = true) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -182,7 +182,7 @@ const mergedOptions = Object.assign({}, this.options, options); | ||
this.repo.metadata.connection.queryResultCache.remove) { | ||
const cacheId = this.getCacheId(query); | ||
const cacheId = this.getCacheId(query, options); | ||
yield this.repo.metadata.connection.queryResultCache.remove([cacheId]); | ||
} | ||
if (mergedOptions.cache) { | ||
const cacheId = this.getCacheId(query); | ||
const cacheId = this.getCacheId(query, options); | ||
builder.cache(cacheId, mergedOptions.cache); | ||
@@ -282,4 +282,4 @@ } | ||
} | ||
getCacheId(query) { | ||
return JSON.stringify(Object.assign({}, query, { cache: undefined })); | ||
getCacheId(query, options) { | ||
return JSON.stringify({ query, options, cache: undefined }); | ||
} | ||
@@ -286,0 +286,0 @@ getSelect(query, options) { |
@@ -0,1 +1,2 @@ | ||
export declare const swagger: any; | ||
export declare const hasValidator: boolean; | ||
@@ -2,0 +3,0 @@ export declare const hasTypeorm: boolean; |
30
utils.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
let classValidator; | ||
let classTransformer; | ||
let typeorm; | ||
let classValidatorPkg; | ||
let classTransformerPkg; | ||
let typeormPkg; | ||
let swaggerPkg; | ||
try { | ||
classValidator = require('class-validator'); | ||
classValidatorPkg = require('class-validator'); | ||
} | ||
catch (error) { } | ||
try { | ||
classTransformer = require('class-transformer'); | ||
classTransformerPkg = require('class-transformer'); | ||
} | ||
catch (error) { } | ||
try { | ||
typeorm = require('typeorm/decorator/entity/Entity'); | ||
typeormPkg = require('typeorm/decorator/entity/Entity'); | ||
} | ||
catch (error) { } | ||
exports.hasValidator = !!classValidator; | ||
exports.hasTypeorm = !!typeorm; | ||
try { | ||
swaggerPkg = require('@nestjs/swagger/dist/constants'); | ||
} | ||
catch (error) { } | ||
exports.swagger = swaggerPkg ? swaggerPkg : null; | ||
exports.hasValidator = !!classValidatorPkg; | ||
exports.hasTypeorm = !!typeormPkg; | ||
exports.isArrayFull = (obj) => Array.isArray(obj) && obj.length !== 0; | ||
exports.mockValidatorDecorator = (name) => classValidator && classValidator[name] | ||
? classValidator[name] | ||
exports.mockValidatorDecorator = (name) => classValidatorPkg && classValidatorPkg[name] | ||
? classValidatorPkg[name] | ||
: (...args) => (target, key) => { }; | ||
exports.mockTransformerDecorator = (name) => classTransformer && classTransformer[name] | ||
? classTransformer[name] | ||
exports.mockTransformerDecorator = (name) => classTransformerPkg && classTransformerPkg[name] | ||
? classTransformerPkg[name] | ||
: (...args) => (target, key) => { }; | ||
//# sourceMappingURL=utils.js.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
133341
90
1568
903