@plumier/core
Version:
Delightful Node.js Rest Framework
272 lines (271 loc) • 11.1 kB
JavaScript
"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 = {}));