UNPKG

ncrudify

Version:

Configurable CRUD module for NestJS and Mongoose.

440 lines (423 loc) 13.2 kB
import { ICrudify } from "./interface/crudify.interface"; import { UseGuards, UsePipes, ValidationPipe } from "@nestjs/common"; import { DisableRouteGuard } from "./disable.guard"; import { ApiOperation, ApiCreatedResponse, ApiOkResponse, ApiNotFoundResponse, ApiBadRequestResponse, ApiConflictResponse, ApiParam, ApiBody, ApiQuery, ApiExcludeEndpoint, } from "@nestjs/swagger"; import { ControllerMethods } from "./interface/controllermethods.type"; import { IRouteConfig } from "./interface/routeconfig.interface"; export namespace CrudifyRoutesDecorator { export function getDecorators(options: ICrudify, route: ControllerMethods) { const routeconfig: IRouteConfig | undefined = options.routes?.config?.[route]; if ( options.routes?.exclude?.includes(route) || routeconfig?.disabled == true ) return [ApiExcludeEndpoint(), UseGuards(DisableRouteGuard)]; let routeDecorators: MethodDecorator[] = getRouteDecorators(options, route); const generalUserRouteDecorators: MethodDecorator[] = options?.routes?.decorators || []; const userRouteDecorators: MethodDecorator[] = routeconfig?.decorators || []; return [ ...routeDecorators, ...generalUserRouteDecorators, ...userRouteDecorators, ]; } function getRouteDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { switch (route) { case "create": return createDecorators(options, route); case "createBulk": return createBulkDecorators(options, route); case "findAll": return findAllDecorators(options, route); case "findOne": return findOneDecorators(options, route); case "put": return overwriteDecorators(options, route); case "update": return updateDecorators(options, route); case "updateBulk": return updateBulkDecorators(options, route); case "delete": return deleteDecorators(options, route); case "deleteBulk": return deleteBulkDecorators(options, route); case "restore": return restoreDecorators(options, route); case "restoreBulk": return restoreBulkDecorators(options, route); default: return []; } } function createDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Create a ${name} resource`, operationId: `${name}_${route}`, }), ApiCreatedResponse({ description: `The resource ${name} has been successfully created`, type: options.model.type, }), ApiBadRequestResponse({ description: "Invalid input data" }), ApiConflictResponse({ description: "Conflict in creating the resource", }), ApiBody({ description: "Data of the new resource", type: options.model.cdto || options.model.type, }), ]; } function createBulkDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Create multiple ${name} resources`, operationId: `${name}_${route}`, }), ApiCreatedResponse({ description: "The resources have been created", type: [options.model.type], }), ApiBadRequestResponse({ description: "Invalid input data" }), ApiConflictResponse({ description: "Conflict in creating the resources", }), ApiBody({ description: "Data of the new resources", type: [options.model.cdto || options.model.type], }), ]; } function findAllDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); let path_types: any = { String: "string", Number: "number", Date: "string", Buffer: "string", Boolean: "boolean", Mixed: "string", ObjectId: "string", Array: "array", Decimal128: "number", Map: "object", Schema: "object", UUID: "string", BigInt: "number", Double: "number", Int32: "number", }; let list: any[] = []; if (options.model.schema) { const fields = Object.keys(options.model.schema.paths); const operators = ["eq", "ne", "gt", "gte", "lt", "lte", "in"]; fields.forEach((field) => { let type = options.model.schema!.paths[field].instance; let path_type = path_types[type]; if (!path_type) { path_type = "string"; } operators.forEach((operator) => { list.push({ [`${field}[${operator}]`]: { type: path_type } }); }); }); } const patternProperties = list.reduce((acc, item) => { const key = Object.keys(item)[0]; acc[key] = item[key]; return acc; }, {}); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Retrieve all ${name} resources`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: `List of ${name} resources`, type: [options.model.type], }), ApiNotFoundResponse({ description: "Resources not found" }), ApiQuery({ name: "filters", required: false, type: "object", style: "form", explode: true, description: "Filters to apply", properties: patternProperties, examples: { void: { value: {} }, example: { value: { "filter_string[eq]": "filter_value", "filter_num[gt]": -1, }, }, }, default: {}, }), ApiQuery({ name: "limit", required: false, type: Number, description: "Number of records to return", }), ApiQuery({ name: "skip", required: false, type: Number, description: "Number of records to skip", }), ApiQuery({ name: "sort", required: false, type: String, description: "Sorting fields", }), ]; } function findOneDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Retrive a ${name} resource by ID`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: `Resource ${name} found`, type: options.model.type, }), ApiNotFoundResponse({ description: "Resource not found" }), ApiParam({ name: "id", description: "ID of the resource", type: String, }), ]; } function overwriteDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Overwrite a ${name} resource`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: "The resource has been overwrited", type: options.model.type, }), ApiBadRequestResponse({ description: "Invalid data" }), ApiParam({ name: "id", description: `ID of the ${name} resource`, type: String, }), ApiBody({ description: "Overwrited data of the resource", type: options.model.udto || options.model.type, }), ]; } function updateDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Update a ${name} resource`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: `The resource ${name} has been updated`, type: options.model.type, }), ApiBadRequestResponse({ description: "Invalid data" }), ApiParam({ name: "id", description: `ID of the ${name} resource`, type: String, }), ApiBody({ description: "Updated data of the resource", type: options.model.udto || options.model.type, }), ]; } function updateBulkDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Update multiple ${name} resources`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: `The resources have been updated`, type: options.model.type, }), ApiBadRequestResponse({ description: "Invalid data" }), ApiBody({ description: `Object containing filter and updated data`, schema: { type: "object", properties: { filter: { type: "object", description: "Filter to select the resources to update", }, updateDto: { type: "object", description: "Data to apply to the selected resources", }, }, }, }), ]; } function deleteDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ ApiOperation({ summary: `Delete a ${name} resource`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: "The resource has been deleted" }), ApiNotFoundResponse({ description: "Resource not found" }), ApiParam({ name: "id", description: "ID of the resource", type: String, }), ]; } function deleteBulkDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ ApiOperation({ summary: `Delete multiple ${name} resources`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: "The resources have been deleted" }), ApiNotFoundResponse({ description: "Some resources not found" }), ApiBadRequestResponse({ description: "Invalid input, expected an array of IDs", }), ApiBody({ description: `Object containing filter`, schema: { type: "object", properties: { filter: { type: "object", description: "Filter to select the resources to delete", }, }, }, }), ]; } function restoreDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Restore a ${name} resource`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: `The resource ${name} has been restored`, type: options.model.type, }), ApiBadRequestResponse({ description: "Invalid data" }), ApiParam({ name: "id", description: `ID of the ${name} resource`, type: String, }), ]; } function restoreBulkDecorators( options: ICrudify, route: ControllerMethods ): MethodDecorator[] { const name: string = options.model.type.name.toLowerCase(); return [ UsePipes(new ValidationPipe({ transform: true })), ApiOperation({ summary: `Restore multiple ${name} resources`, operationId: `${name}_${route}`, }), ApiOkResponse({ description: `The resources have been restored`, type: options.model.type, }), ApiBadRequestResponse({ description: "Invalid data" }), ApiBody({ description: `Object containing filter`, schema: { type: "object", properties: { filter: { type: "object", description: "Filter to select the resources to restore", }, }, }, }), ]; } }