UNPKG

@plumier/core

Version:

Delightful Node.js Rest Framework

272 lines (271 loc) • 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.errorMessage = exports.MetadataImpl = exports.ParameterMetadata = exports.ValidationError = exports.HttpStatusError = exports.NestedControllerGeneric = exports.ControllerGeneric = exports.FormFile = exports.DefaultDependencyResolver = exports.MiddlewareUtil = exports.DefaultFacility = exports.RedirectActionResult = exports.ActionResult = void 0; const tslib_1 = require("tslib"); const fs_1 = require("fs"); const path_1 = require("path"); const reflect_1 = tslib_1.__importStar(require("@plumier/reflect")); const util_1 = require("util"); const http_status_1 = require("./http-status"); const copyFileAsync = (0, util_1.promisify)(fs_1.copyFile); class ActionResult { constructor(body, status) { this.body = body; this.status = status; this.headers = {}; this.cookies = []; } static fromContext(ctx) { return new ActionResult(ctx.body, ctx.status); } setHeader(key, value) { this.headers[key] = value; return this; } setStatus(status) { this.status = status; return this; } setCookie(key, value, option) { if (typeof key === "string") this.cookies.push({ key, value, option }); else if (Array.isArray(key)) { key.forEach(x => this.cookies.push(x)); } else this.cookies.push(key); return this; } async execute(ctx) { Object.keys(this.headers).forEach(x => { ctx.set(x, this.headers[x]); }); if (this.status) ctx.status = this.status; for (const cookie of this.cookies) { if (!cookie.value) ctx.cookies.set(cookie.key); else ctx.cookies.set(cookie.key, cookie.value, cookie.option); } if (this.body) ctx.body = this.body; } } exports.ActionResult = ActionResult; class RedirectActionResult extends ActionResult { constructor(path) { super(); this.path = path; } async execute(ctx) { ctx.redirect(this.path); } } exports.RedirectActionResult = RedirectActionResult; class DefaultFacility { async generateRoutes(app, routes) { return []; } setup(app) { } async preInitialize(app) { } async initialize(app, routes) { } } exports.DefaultFacility = DefaultFacility; var MiddlewareUtil; (function (MiddlewareUtil) { function fromKoa(middleware) { return { execute: async (x) => { await middleware(x.ctx, async () => { const nextResult = await x.proceed(); await nextResult.execute(x.ctx); }); return ActionResult.fromContext(x.ctx); } }; } MiddlewareUtil.fromKoa = fromKoa; function extractDecorators(route) { const middlewares = []; for (let i = route.controller.decorators.length; i--;) { const dec = route.controller.decorators[i]; if (dec.name === "Middleware") middlewares.push(...dec.value.map(middleware => ({ middleware, target: dec.target }))); } for (let i = route.action.decorators.length; i--;) { const dec = route.action.decorators[i]; if (dec.name === "Middleware") middlewares.push(...dec.value.map(middleware => ({ middleware, target: dec.target }))); } return middlewares; } MiddlewareUtil.extractDecorators = extractDecorators; })(MiddlewareUtil = exports.MiddlewareUtil || (exports.MiddlewareUtil = {})); class DefaultDependencyResolver { constructor() { this.registry = new Map(); } register(id) { return (0, reflect_1.decorateClass)(cls => { this.registry.set(id, cls); return { type: "RegistryDecorator", id }; }); } resolve(type) { if (typeof type === "function") { return new type(); } else { const Type = this.registry.get(type); if (!Type) throw new Error(errorMessage.ObjectNotFound.format(type)); return new Type(); } } } exports.DefaultDependencyResolver = DefaultDependencyResolver; // --------------------------------------------------------------------- // // ----------------------------- MULTIPART ----------------------------- // // --------------------------------------------------------------------- // let FormFile = class FormFile { constructor( /** * Size of the file (bytes) */ size, /** * Temporary path of the uploaded file */ path, /** * Original file name provided by client */ name, /** * Mime type of the file */ type, /** * The file timestamp */ mtime) { this.size = size; this.path = path; this.name = name; this.type = type; this.mtime = mtime; } /** * Copy uploaded file into target directory, file name automatically generated * @param dir target directory * @returns the full path of the new location { fullPath, name } */ async copy(dir) { const random = Math.round((Math.random() * 10000)).toString(36); const time = new Date().getTime().toString(36); const name = time + random + (0, path_1.extname)(this.name); const fullPath = (0, path_1.join)(dir, name); await copyFileAsync(this.path, fullPath); return { fullPath, name }; } }; FormFile = tslib_1.__decorate([ reflect_1.default.parameterProperties(), tslib_1.__metadata("design:paramtypes", [Number, String, String, String, String]) ], FormFile); exports.FormFile = FormFile; class ControllerGeneric { } exports.ControllerGeneric = ControllerGeneric; class NestedControllerGeneric { } exports.NestedControllerGeneric = NestedControllerGeneric; // --------------------------------------------------------------------- // // ------------------------------- ERROR ------------------------------- // // --------------------------------------------------------------------- // class HttpStatusError extends Error { constructor(status, message) { super(message); this.status = status; Object.setPrototypeOf(this, HttpStatusError.prototype); } } exports.HttpStatusError = HttpStatusError; class ValidationError extends HttpStatusError { constructor(issues) { super(http_status_1.HttpStatus.UnprocessableEntity, JSON.stringify(issues)); this.issues = issues; Object.setPrototypeOf(this, ValidationError.prototype); } } exports.ValidationError = ValidationError; class ParameterMetadata { constructor(parameters, meta) { this.parameters = parameters; this.meta = meta; } get(nameOrIndex) { if (typeof nameOrIndex === "number") return this.parameters[nameOrIndex]; const idx = this.meta.findIndex(x => x.name.toLowerCase() === nameOrIndex.toLowerCase()); if (idx === -1) return; return this.parameters[idx]; } /** * Get all parameter values */ values() { return this.parameters; } /** * Get all action's parameter names */ names() { return this.meta.map(x => x.name); } /** * Check if action has specified parameter (case insensitive) * @param name name of parameter */ hasName(name) { return !!this.meta.find(x => x.name.toLowerCase() === name.toLowerCase()); } } exports.ParameterMetadata = ParameterMetadata; class MetadataImpl { constructor(params, routeInfo, current) { this.controller = routeInfo.controller; this.action = routeInfo.action; this.access = routeInfo.access; //if (params) this.actionParams = new ParameterMetadata(params, routeInfo.action.parameters); this.current = current; } } exports.MetadataImpl = MetadataImpl; // --------------------------------------------------------------------- // // --------------------------- ERROR MESSAGE --------------------------- // // --------------------------------------------------------------------- // var errorMessage; (function (errorMessage) { //PLUM1XXX User configuration error errorMessage.RouteDoesNotHaveBackingParam = "Route parameters ({0}) doesn't have appropriate backing parameter"; errorMessage.DuplicateRouteFound = "Duplicate route found in {0}"; errorMessage.ControllerPathNotFound = "Controller file or directory {0} not found"; errorMessage.ObjectNotFound = "Object with id {0} not found in Object registry"; errorMessage.ActionParameterDoesNotHaveTypeInfo = "Parameter binding skipped because action parameters doesn't have type information in ({0})"; errorMessage.ModelWithoutTypeInformation = "Parameter binding skipped because {0} doesn't have type information on its properties"; errorMessage.ArrayWithoutTypeInformation = "Parameter binding skipped because array element doesn't have type information in ({0})"; errorMessage.PropertyWithoutTypeInformation = "Parameter binding skipped because property doesn't have type information in ({0})"; errorMessage.GenericControllerImplementationNotFound = "Generic controller implementation not installed"; errorMessage.GenericControllerRequired = "@genericController() required generic controller implementation, please install the appropriate facility"; errorMessage.GenericControllerMissingTypeInfo = "{0} marked with @genericController() but doesn't have type information"; errorMessage.GenericControllerInNonArrayProperty = "Nested generic controller can not be created using non array relation on: {0}.{1}"; errorMessage.CustomRouteEndWithParameter = "Custom route path '{0}' on {1} entity, require path that ends with route parameter, example: animals/:animalId"; errorMessage.CustomRouteRequiredTwoParameters = "Nested custom route path '{0}' on {1} entity, must have two route parameters, example: users/:userId/animals/:animalId"; errorMessage.CustomRouteMustHaveOneParameter = "Custom route path '{0}' on {1} entity, must have one route parameter, example: animals/:animalId"; errorMessage.EntityRequireID = "Entity {0} used by generic controller doesn't have an ID property"; errorMessage.UnableToGetMemberDataType = "Unable to get data type of member {0}.{1}. Make sure to provide type information, or manage if its has cross reference to other class"; //PLUM2XXX internal app error errorMessage.UnableToInstantiateModel = `Unable to instantiate {0}. Domain model should not throw error inside constructor`; //End user error (no error code) errorMessage.UnableToConvertValue = `Unable to convert "{0}" into {1}`; errorMessage.FileSizeExceeded = "File {0} size exceeded the maximum size"; errorMessage.NumberOfFilesExceeded = "Number of files exceeded the maximum allowed"; })(errorMessage = exports.errorMessage || (exports.errorMessage = {}));