@nestjsx/crud
Version:
NestJs CRUD for RESTful APIs
439 lines • 19.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("@nestjs/common");
const route_paramtypes_enum_1 = require("@nestjs/common/enums/route-paramtypes.enum");
const util_1 = require("@nestjsx/util");
const deepmerge = require("deepmerge");
const reflection_helper_1 = require("./reflection.helper");
const serialize_helper_1 = require("./serialize.helper");
const swagger_helper_1 = require("./swagger.helper");
const validation_helper_1 = require("./validation.helper");
const interceptors_1 = require("../interceptors");
const enums_1 = require("../enums");
const module_1 = require("../module");
class CrudRoutesFactory {
constructor(target, options) {
this.target = target;
this.swaggerModels = {};
this.options = options;
this.create();
}
static create(target, options) {
return new CrudRoutesFactory(target, options);
}
get targetProto() {
return this.target.prototype;
}
get modelName() {
return this.options.model.type.name;
}
get modelType() {
return this.options.model.type;
}
get actionsMap() {
return {
getManyBase: enums_1.CrudActions.ReadAll,
getOneBase: enums_1.CrudActions.ReadOne,
createManyBase: enums_1.CrudActions.CreateMany,
createOneBase: enums_1.CrudActions.CreateOne,
updateOneBase: enums_1.CrudActions.UpdateOne,
deleteOneBase: enums_1.CrudActions.DeleteOne,
replaceOneBase: enums_1.CrudActions.ReplaceOne,
recoverOneBase: enums_1.CrudActions.RecoverOne,
};
}
create() {
const routesSchema = this.getRoutesSchema();
this.mergeOptions();
this.setResponseModels();
this.createRoutes(routesSchema);
this.overrideRoutes(routesSchema);
this.enableRoutes(routesSchema);
}
mergeOptions() {
const authOptions = reflection_helper_1.R.getCrudAuthOptions(this.target);
this.options.auth = util_1.isObjectFull(authOptions) ? authOptions : {};
if (util_1.isUndefined(this.options.auth.property)) {
this.options.auth.property = module_1.CrudConfigService.config.auth.property;
}
const query = util_1.isObjectFull(this.options.query) ? this.options.query : {};
this.options.query = { ...module_1.CrudConfigService.config.query, ...query };
const routes = util_1.isObjectFull(this.options.routes) ? this.options.routes : {};
this.options.routes = deepmerge(module_1.CrudConfigService.config.routes, routes, {
arrayMerge: (a, b, c) => b,
});
this.options.params = util_1.isObjectFull(this.options.params)
? this.options.params
: util_1.isObjectFull(module_1.CrudConfigService.config.params)
? module_1.CrudConfigService.config.params
: {};
const hasPrimary = this.getPrimaryParams().length > 0;
if (!hasPrimary) {
this.options.params['id'] = {
field: 'id',
type: 'number',
primary: true,
};
}
if (!util_1.isObjectFull(this.options.dto)) {
this.options.dto = {};
}
const serialize = util_1.isObjectFull(this.options.serialize) ? this.options.serialize : {};
this.options.serialize = { ...module_1.CrudConfigService.config.serialize, ...serialize };
this.options.serialize.get = util_1.isFalse(this.options.serialize.get)
? false
: this.options.serialize.get || this.modelType;
this.options.serialize.getMany = util_1.isFalse(this.options.serialize.getMany)
? false
: this.options.serialize.getMany
? this.options.serialize.getMany
: util_1.isFalse(this.options.serialize.get)
? false
: serialize_helper_1.SerializeHelper.createGetManyDto(this.options.serialize.get, this.modelName);
this.options.serialize.create = util_1.isFalse(this.options.serialize.create)
? false
: this.options.serialize.create || this.modelType;
this.options.serialize.update = util_1.isFalse(this.options.serialize.update)
? false
: this.options.serialize.update || this.modelType;
this.options.serialize.replace = util_1.isFalse(this.options.serialize.replace)
? false
: this.options.serialize.replace || this.modelType;
this.options.serialize.delete =
util_1.isFalse(this.options.serialize.delete) ||
!this.options.routes.deleteOneBase.returnDeleted
? false
: this.options.serialize.delete || this.modelType;
reflection_helper_1.R.setCrudOptions(this.options, this.target);
}
getRoutesSchema() {
return [
{
name: 'getOneBase',
path: '/',
method: common_1.RequestMethod.GET,
enable: false,
override: false,
withParams: true,
},
{
name: 'getManyBase',
path: '/',
method: common_1.RequestMethod.GET,
enable: false,
override: false,
withParams: false,
},
{
name: 'createOneBase',
path: '/',
method: common_1.RequestMethod.POST,
enable: false,
override: false,
withParams: false,
},
{
name: 'createManyBase',
path: '/bulk',
method: common_1.RequestMethod.POST,
enable: false,
override: false,
withParams: false,
},
{
name: 'updateOneBase',
path: '/',
method: common_1.RequestMethod.PATCH,
enable: false,
override: false,
withParams: true,
},
{
name: 'replaceOneBase',
path: '/',
method: common_1.RequestMethod.PUT,
enable: false,
override: false,
withParams: true,
},
{
name: 'deleteOneBase',
path: '/',
method: common_1.RequestMethod.DELETE,
enable: false,
override: false,
withParams: true,
},
{
name: 'recoverOneBase',
path: '/recover',
method: common_1.RequestMethod.PATCH,
enable: false,
override: false,
withParams: true,
},
];
}
getManyBase(name) {
this.targetProto[name] = function getManyBase(req) {
return this.service.getMany(req);
};
}
getOneBase(name) {
this.targetProto[name] = function getOneBase(req) {
return this.service.getOne(req);
};
}
createOneBase(name) {
this.targetProto[name] = function createOneBase(req, dto) {
return this.service.createOne(req, dto);
};
}
createManyBase(name) {
this.targetProto[name] = function createManyBase(req, dto) {
return this.service.createMany(req, dto);
};
}
updateOneBase(name) {
this.targetProto[name] = function updateOneBase(req, dto) {
return this.service.updateOne(req, dto);
};
}
replaceOneBase(name) {
this.targetProto[name] = function replaceOneBase(req, dto) {
return this.service.replaceOne(req, dto);
};
}
deleteOneBase(name) {
this.targetProto[name] = function deleteOneBase(req) {
return this.service.deleteOne(req);
};
}
recoverOneBase(name) {
this.targetProto[name] = function recoverOneBase(req) {
return this.service.recoverOne(req);
};
}
canCreateRoute(name) {
const only = this.options.routes.only;
const exclude = this.options.routes.exclude;
if (name === 'recoverOneBase' && this.options.query.softDelete !== true) {
return false;
}
if (util_1.isArrayFull(only)) {
return only.some((route) => route === name);
}
if (util_1.isArrayFull(exclude)) {
return !exclude.some((route) => route === name);
}
return true;
}
setResponseModels() {
const modelType = util_1.isFunction(this.modelType)
? this.modelType
: serialize_helper_1.SerializeHelper.createGetOneResponseDto(this.modelName);
this.swaggerModels.get = util_1.isFunction(this.options.serialize.get)
? this.options.serialize.get
: modelType;
this.swaggerModels.getMany =
this.options.serialize.getMany ||
serialize_helper_1.SerializeHelper.createGetManyDto(this.swaggerModels.get, this.modelName);
this.swaggerModels.create = util_1.isFunction(this.options.serialize.create)
? this.options.serialize.create
: modelType;
this.swaggerModels.update = util_1.isFunction(this.options.serialize.update)
? this.options.serialize.update
: modelType;
this.swaggerModels.replace = util_1.isFunction(this.options.serialize.replace)
? this.options.serialize.replace
: modelType;
this.swaggerModels.delete = util_1.isFunction(this.options.serialize.delete)
? this.options.serialize.delete
: modelType;
this.swaggerModels.recover = util_1.isFunction(this.options.serialize.recover)
? this.options.serialize.recover
: modelType;
swagger_helper_1.Swagger.setExtraModels(this.swaggerModels);
}
createRoutes(routesSchema) {
const primaryParams = this.getPrimaryParams().filter((param) => !this.options.params[param].disabled);
routesSchema.forEach((route) => {
if (this.canCreateRoute(route.name)) {
this[route.name](route.name);
route.enable = true;
this.setBaseRouteMeta(route.name);
}
if (route.withParams && primaryParams.length > 0) {
route.path =
route.path !== '/'
? `${primaryParams.map((param) => `/:${param}`).join('')}${route.path}`
: primaryParams.map((param) => `/:${param}`).join('');
}
});
}
overrideRoutes(routesSchema) {
util_1.getOwnPropNames(this.targetProto).forEach((name) => {
const override = reflection_helper_1.R.getOverrideRoute(this.targetProto[name]);
const route = routesSchema.find((r) => util_1.isEqual(r.name, override));
if (override && route && route.enable) {
const interceptors = reflection_helper_1.R.getInterceptors(this.targetProto[name]);
const baseInterceptors = reflection_helper_1.R.getInterceptors(this.targetProto[override]);
const baseAction = reflection_helper_1.R.getAction(this.targetProto[override]);
const operation = swagger_helper_1.Swagger.getOperation(this.targetProto[name]);
const baseOperation = swagger_helper_1.Swagger.getOperation(this.targetProto[override]);
const swaggerParams = swagger_helper_1.Swagger.getParams(this.targetProto[name]);
const baseSwaggerParams = swagger_helper_1.Swagger.getParams(this.targetProto[override]);
const responseOk = swagger_helper_1.Swagger.getResponseOk(this.targetProto[name]);
const baseResponseOk = swagger_helper_1.Swagger.getResponseOk(this.targetProto[override]);
reflection_helper_1.R.setInterceptors([...baseInterceptors, ...interceptors], this.targetProto[name]);
reflection_helper_1.R.setAction(baseAction, this.targetProto[name]);
swagger_helper_1.Swagger.setOperation({ ...baseOperation, ...operation }, this.targetProto[name]);
swagger_helper_1.Swagger.setParams([...baseSwaggerParams, ...swaggerParams], this.targetProto[name]);
swagger_helper_1.Swagger.setResponseOk({ ...baseResponseOk, ...responseOk }, this.targetProto[name]);
this.overrideParsedBodyDecorator(override, name);
reflection_helper_1.R.setRoute(route, this.targetProto[name]);
route.override = true;
}
});
}
enableRoutes(routesSchema) {
routesSchema.forEach((route) => {
if (!route.override && route.enable) {
reflection_helper_1.R.setRoute(route, this.targetProto[route.name]);
}
});
}
overrideParsedBodyDecorator(override, name) {
const allowed = [
'createManyBase',
'createOneBase',
'updateOneBase',
'replaceOneBase',
];
const withBody = util_1.isIn(override, allowed);
const parsedBody = reflection_helper_1.R.getParsedBody(this.targetProto[name]);
if (withBody && parsedBody) {
const baseKey = `${route_paramtypes_enum_1.RouteParamtypes.BODY}:1`;
const key = `${route_paramtypes_enum_1.RouteParamtypes.BODY}:${parsedBody.index}`;
const baseRouteArgs = reflection_helper_1.R.getRouteArgs(this.target, override);
const routeArgs = reflection_helper_1.R.getRouteArgs(this.target, name);
const baseBodyArg = baseRouteArgs[baseKey];
reflection_helper_1.R.setRouteArgs({
...routeArgs,
[key]: {
...baseBodyArg,
index: parsedBody.index,
},
}, this.target, name);
if (util_1.isEqual(override, 'createManyBase')) {
const paramTypes = reflection_helper_1.R.getRouteArgsTypes(this.targetProto, name);
const metatype = paramTypes[parsedBody.index];
const types = [String, Boolean, Number, Array, Object];
const toCopy = util_1.isIn(metatype, types) || util_1.isNil(metatype);
if (toCopy) {
const baseParamTypes = reflection_helper_1.R.getRouteArgsTypes(this.targetProto, override);
const baseMetatype = baseParamTypes[1];
paramTypes.splice(parsedBody.index, 1, baseMetatype);
reflection_helper_1.R.setRouteArgsTypes(paramTypes, this.targetProto, name);
}
}
}
}
getPrimaryParams() {
return util_1.objKeys(this.options.params).filter((param) => this.options.params[param] && this.options.params[param].primary);
}
setBaseRouteMeta(name) {
this.setRouteArgs(name);
this.setRouteArgsTypes(name);
this.setInterceptors(name);
this.setAction(name);
this.setSwaggerOperation(name);
this.setSwaggerPathParams(name);
this.setSwaggerQueryParams(name);
this.setSwaggerResponseOk(name);
this.setDecorators(name);
}
setRouteArgs(name) {
let rest = {};
const routes = [
'createManyBase',
'createOneBase',
'updateOneBase',
'replaceOneBase',
];
if (util_1.isIn(name, routes)) {
const action = this.routeNameAction(name);
const hasDto = !util_1.isNil(this.options.dto[action]);
const { UPDATE, CREATE } = enums_1.CrudValidationGroups;
const groupEnum = util_1.isIn(name, ['updateOneBase', 'replaceOneBase']) ? UPDATE : CREATE;
const group = !hasDto ? groupEnum : undefined;
rest = reflection_helper_1.R.setBodyArg(1, [validation_helper_1.Validation.getValidationPipe(this.options, group)]);
}
reflection_helper_1.R.setRouteArgs({ ...reflection_helper_1.R.setParsedRequestArg(0), ...rest }, this.target, name);
}
setRouteArgsTypes(name) {
if (util_1.isEqual(name, 'createManyBase')) {
const bulkDto = validation_helper_1.Validation.createBulkDto(this.options);
reflection_helper_1.R.setRouteArgsTypes([Object, bulkDto], this.targetProto, name);
}
else if (util_1.isIn(name, ['createOneBase', 'updateOneBase', 'replaceOneBase'])) {
const action = this.routeNameAction(name);
const dto = this.options.dto[action] || this.modelType;
reflection_helper_1.R.setRouteArgsTypes([Object, dto], this.targetProto, name);
}
else {
reflection_helper_1.R.setRouteArgsTypes([Object], this.targetProto, name);
}
}
setInterceptors(name) {
const interceptors = this.options.routes[name].interceptors;
reflection_helper_1.R.setInterceptors([
interceptors_1.CrudRequestInterceptor,
interceptors_1.CrudResponseInterceptor,
...(util_1.isArrayFull(interceptors) ? interceptors : []),
], this.targetProto[name]);
}
setDecorators(name) {
const decorators = this.options.routes[name].decorators;
reflection_helper_1.R.setDecorators(util_1.isArrayFull(decorators) ? decorators : [], this.targetProto, name);
}
setAction(name) {
reflection_helper_1.R.setAction(this.actionsMap[name], this.targetProto[name]);
}
setSwaggerOperation(name) {
const summary = swagger_helper_1.Swagger.operationsMap(this.modelName)[name];
const operationId = name + this.targetProto.constructor.name + this.modelName;
swagger_helper_1.Swagger.setOperation({ summary, operationId }, this.targetProto[name]);
}
setSwaggerPathParams(name) {
const metadata = swagger_helper_1.Swagger.getParams(this.targetProto[name]);
const withoutPrimary = [
'createManyBase',
'createOneBase',
'getManyBase',
];
const removePrimary = util_1.isIn(name, withoutPrimary);
const params = util_1.objKeys(this.options.params)
.filter((key) => !this.options.params[key].disabled)
.filter((key) => !(removePrimary && this.options.params[key].primary))
.reduce((a, c) => ({ ...a, [c]: this.options.params[c] }), {});
const pathParamsMeta = swagger_helper_1.Swagger.createPathParamsMeta(params);
swagger_helper_1.Swagger.setParams([...metadata, ...pathParamsMeta], this.targetProto[name]);
}
setSwaggerQueryParams(name) {
const metadata = swagger_helper_1.Swagger.getParams(this.targetProto[name]);
const queryParamsMeta = swagger_helper_1.Swagger.createQueryParamsMeta(name, this.options);
swagger_helper_1.Swagger.setParams([...metadata, ...queryParamsMeta], this.targetProto[name]);
}
setSwaggerResponseOk(name) {
const metadata = swagger_helper_1.Swagger.getResponseOk(this.targetProto[name]);
const metadataToAdd = swagger_helper_1.Swagger.createResponseMeta(name, this.options, this.swaggerModels) ||
{};
swagger_helper_1.Swagger.setResponseOk({ ...metadata, ...metadataToAdd }, this.targetProto[name]);
}
routeNameAction(name) {
return (name.split('OneBase')[0] || name.split('ManyBase')[0]);
}
}
exports.CrudRoutesFactory = CrudRoutesFactory;
//# sourceMappingURL=crud-routes.factory.js.map