UNPKG

typescript-rest

Version:
481 lines 20.1 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var bodyParser = require("body-parser"); var cookieParser = require("cookie-parser"); var multer = require("multer"); var metadata = require("./metadata"); var Errors = require("./server-errors"); var _ = require("lodash"); var server_types_1 = require("./server-types"); var server_return_1 = require("./server-return"); var InternalServer = (function () { function InternalServer(router) { this.router = router; } InternalServer.registerServiceClass = function (target) { InternalServer.pathsResolved = false; target = InternalServer.serviceFactory.getTargetClass(target); var name = target['name'] || target.constructor['name']; if (!InternalServer.serverClasses.has(name)) { InternalServer.serverClasses.set(name, new metadata.ServiceClass(target)); InternalServer.inheritParentClass(name); } var serviceClass = InternalServer.serverClasses.get(name); return serviceClass; }; InternalServer.inheritParentClass = function (name) { var classData = InternalServer.serverClasses.get(name); var parent = Object.getPrototypeOf(classData.targetClass.prototype).constructor; var parentClassData = InternalServer.getServiceClass(parent); if (parentClassData) { if (parentClassData.methods) { parentClassData.methods.forEach(function (value, key) { classData.methods.set(key, _.cloneDeep(value)); }); } if (parentClassData.properties) { parentClassData.properties.forEach(function (value, key) { classData.properties.set(key, _.cloneDeep(value)); }); } if (parentClassData.languages) { for (var _i = 0, _a = parentClassData.languages; _i < _a.length; _i++) { var lang = _a[_i]; classData.languages.push(lang); } } if (parentClassData.accepts) { for (var _b = 0, _c = parentClassData.accepts; _b < _c.length; _b++) { var acc = _c[_b]; classData.accepts.push(acc); } } } }; InternalServer.registerServiceMethod = function (target, methodName) { if (methodName) { InternalServer.pathsResolved = false; var classData = InternalServer.registerServiceClass(target); if (!classData.methods.has(methodName)) { classData.methods.set(methodName, new metadata.ServiceMethod()); } var serviceMethod = classData.methods.get(methodName); return serviceMethod; } return null; }; InternalServer.prototype.buildServices = function (types) { var _this = this; if (types) { types = types.map(function (type) { return InternalServer.serviceFactory.getTargetClass(type); }); } InternalServer.serverClasses.forEach(function (classData) { classData.methods.forEach(function (method) { if (_this.validateTargetType(classData.targetClass, types)) { _this.buildService(classData, method); } }); }); InternalServer.pathsResolved = true; this.handleNotAllowedMethods(); }; InternalServer.prototype.buildService = function (serviceClass, serviceMethod) { var _this = this; var handler = function (req, res, next) { _this.callTargetEndPoint(serviceClass, serviceMethod, req, res, next); }; if (!serviceMethod.resolvedPath) { InternalServer.resolveProperties(serviceClass, serviceMethod); } var middleware = this.buildServiceMiddleware(serviceMethod); var args = [serviceMethod.resolvedPath]; args = args.concat(middleware); args.push(handler); switch (serviceMethod.httpMethod) { case server_types_1.HttpMethod.GET: this.router.get.apply(this.router, args); break; case server_types_1.HttpMethod.POST: this.router.post.apply(this.router, args); break; case server_types_1.HttpMethod.PUT: this.router.put.apply(this.router, args); break; case server_types_1.HttpMethod.DELETE: this.router.delete.apply(this.router, args); break; case server_types_1.HttpMethod.HEAD: this.router.head.apply(this.router, args); break; case server_types_1.HttpMethod.OPTIONS: this.router.options.apply(this.router, args); break; case server_types_1.HttpMethod.PATCH: this.router.patch.apply(this.router, args); break; default: throw Error("Invalid http method for service [" + serviceMethod.resolvedPath + "]"); } }; InternalServer.getServiceClass = function (target) { target = InternalServer.serviceFactory.getTargetClass(target); return InternalServer.serverClasses.get(target['name'] || target.constructor['name']) || null; }; InternalServer.prototype.validateTargetType = function (targetClass, types) { if (types && types.length > 0) { return (types.indexOf(targetClass) > -1); } return true; }; InternalServer.prototype.handleNotAllowedMethods = function () { var _this = this; var paths = InternalServer.getPaths(); paths.forEach(function (path) { var supported = InternalServer.getHttpMethods(path); var allowedMethods = new Array(); supported.forEach(function (method) { allowedMethods.push(server_types_1.HttpMethod[method]); }); var allowed = allowedMethods.join(', '); _this.router.all(path, function (req, res, next) { res.set('Allow', allowed); throw new Errors.MethodNotAllowedError(); }); }); }; InternalServer.prototype.getUploader = function () { if (!this.upload) { var options = {}; if (InternalServer.fileDest) { options.dest = InternalServer.fileDest; } if (InternalServer.fileFilter) { options.fileFilter = InternalServer.fileFilter; } if (InternalServer.fileLimits) { options.limits = InternalServer.fileLimits; } if (options.dest) { this.upload = multer(options); } else { this.upload = multer(); } } return this.upload; }; InternalServer.prototype.buildServiceMiddleware = function (serviceMethod) { var result = new Array(); if (serviceMethod.mustParseCookies) { var args = []; if (InternalServer.cookiesSecret) { args.push(InternalServer.cookiesSecret); } if (InternalServer.cookiesDecoder) { args.push({ decode: InternalServer.cookiesDecoder }); } result.push(cookieParser.apply(this, args)); } if (serviceMethod.mustParseBody) { if (serviceMethod.bodyParserOptions) { result.push(bodyParser.json(serviceMethod.bodyParserOptions)); } else { result.push(bodyParser.json()); } // TODO adicionar parser de XML para o body } if (serviceMethod.mustParseForms || serviceMethod.acceptMultiTypedParam) { if (serviceMethod.bodyParserOptions) { result.push(bodyParser.urlencoded(serviceMethod.bodyParserOptions)); } else { result.push(bodyParser.urlencoded({ extended: true })); } } if (serviceMethod.files.length > 0) { var options_1 = new Array(); serviceMethod.files.forEach(function (fileData) { if (fileData.singleFile) { options_1.push({ 'name': fileData.name, 'maxCount': 1 }); } else { options_1.push({ 'name': fileData.name }); } }); result.push(this.getUploader().fields(options_1)); } return result; }; InternalServer.prototype.processResponseHeaders = function (serviceMethod, context) { if (serviceMethod.resolvedLanguages) { if (serviceMethod.httpMethod === server_types_1.HttpMethod.GET) { context.response.vary('Accept-Language'); } context.response.set('Content-Language', context.language); } if (serviceMethod.resolvedAccepts) { context.response.vary('Accept'); } }; InternalServer.prototype.checkAcceptance = function (serviceMethod, context) { if (serviceMethod.resolvedLanguages) { var lang = context.request.acceptsLanguages(serviceMethod.resolvedLanguages); if (lang) { context.language = lang; } } else { var languages = context.request.acceptsLanguages(); if (languages && languages.length > 0) { context.language = languages[0]; } } if (serviceMethod.resolvedAccepts) { var accept = context.request.accepts(serviceMethod.resolvedAccepts); if (accept) { context.accept = accept; } else { throw new Errors.NotAcceptableError('Accept'); } } if (!context.language) { throw new Errors.NotAcceptableError('Accept-Language'); } }; InternalServer.prototype.createService = function (serviceClass, context) { var _this = this; var serviceObject = InternalServer.serviceFactory.create(serviceClass.targetClass); if (serviceClass.hasProperties()) { serviceClass.properties.forEach(function (property, key) { serviceObject[key] = _this.processParameter(property.type, context, property.name, property.propertyType); }); } return serviceObject; }; InternalServer.prototype.callTargetEndPoint = function (serviceClass, serviceMethod, req, res, next) { var context = new server_types_1.ServiceContext(); context.request = req; context.response = res; context.next = next; this.checkAcceptance(serviceMethod, context); var serviceObject = this.createService(serviceClass, context); var args = this.buildArgumentsList(serviceMethod, context); var toCall = serviceClass.targetClass.prototype[serviceMethod.name] || serviceClass.targetClass[serviceMethod.name]; var result = toCall.apply(serviceObject, args); this.processResponseHeaders(serviceMethod, context); this.sendValue(result, res, next); }; InternalServer.prototype.sendValue = function (value, res, next) { var _this = this; switch (typeof value) { case 'number': res.send(value.toString()); break; case 'string': res.send(value); break; case 'boolean': res.send(value.toString()); break; case 'undefined': if (!res.headersSent) { res.sendStatus(204); } break; default: if (value.filePath && value instanceof server_return_1.DownloadResource) { res.download(value.filePath, value.fileName); } else if (value instanceof server_return_1.DownloadBinaryData) { res.writeHead(200, { 'Content-Length': value.content.length, 'Content-Type': value.mimeType, 'Content-disposition': 'attachment;filename=' + value.fileName }); res.end(value.content); } else if (value.location && value instanceof server_types_1.ReferencedResource) { res.set('Location', value.location); if (value.body) { res.status(value.statusCode); this.sendValue(value.body, res, next); } else { res.sendStatus(value.statusCode); } } else if (value.then) { Promise.resolve(value) .then(function (val) { _this.sendValue(val, res, next); }).catch(function (err) { next(err); }); } else { res.json(value); } } }; InternalServer.prototype.buildArgumentsList = function (serviceMethod, context) { var _this = this; var result = new Array(); serviceMethod.parameters.forEach(function (param) { result.push(_this.processParameter(param.paramType, context, param.name, param.type)); }); return result; }; InternalServer.prototype.processParameter = function (paramType, context, name, type) { switch (paramType) { case metadata.ParamType.path: return this.convertType(context.request.params[name], type); case metadata.ParamType.query: return this.convertType(context.request.query[name], type); case metadata.ParamType.header: return this.convertType(context.request.header(name), type); case metadata.ParamType.cookie: return this.convertType(context.request.cookies[name], type); case metadata.ParamType.body: return this.convertType(context.request.body, type); case metadata.ParamType.file: var files = context.request.files ? context.request.files[name] : null; if (files && files.length > 0) { return files[0]; } return null; case metadata.ParamType.files: return context.request.files[name]; case metadata.ParamType.form: return this.convertType(context.request.body[name], type); case metadata.ParamType.param: var paramValue = context.request.body[name] || context.request.query[name]; return this.convertType(paramValue, type); case metadata.ParamType.context: return context; case metadata.ParamType.context_request: return context.request; case metadata.ParamType.context_response: return context.response; case metadata.ParamType.context_next: return context.next; case metadata.ParamType.context_accept: return context.accept; case metadata.ParamType.context_accept_language: return context.language; default: throw Error('Invalid parameter type'); } }; InternalServer.prototype.convertType = function (paramValue, paramType) { var serializedType = paramType['name']; switch (serializedType) { case 'Number': return paramValue ? parseFloat(paramValue) : 0; case 'Boolean': return paramValue === 'true'; default: return paramValue; } }; InternalServer.resolveAllPaths = function () { if (!InternalServer.pathsResolved) { InternalServer.paths.clear(); InternalServer.serverClasses.forEach(function (classData) { classData.methods.forEach(function (method) { if (!method.resolvedPath) { InternalServer.resolveProperties(classData, method); } }); }); InternalServer.pathsResolved = true; } }; InternalServer.getPaths = function () { InternalServer.resolveAllPaths(); var result = new Set(); InternalServer.paths.forEach(function (value, key) { result.add(key); }); return result; }; InternalServer.getHttpMethods = function (path) { InternalServer.resolveAllPaths(); var methods = InternalServer.paths.get(path); return methods || new Set(); }; InternalServer.resolveLanguages = function (serviceClass, serviceMethod) { var resolvedLanguages = new Array(); if (serviceClass.languages) { serviceClass.languages.forEach(function (lang) { resolvedLanguages.push(lang); }); } if (serviceMethod.languages) { serviceMethod.languages.forEach(function (lang) { resolvedLanguages.push(lang); }); } if (resolvedLanguages.length > 0) { serviceMethod.resolvedLanguages = resolvedLanguages; } }; InternalServer.resolveAccepts = function (serviceClass, serviceMethod) { var resolvedAccepts = new Array(); if (serviceClass.accepts) { serviceClass.accepts.forEach(function (accept) { resolvedAccepts.push(accept); }); } if (serviceMethod.accepts) { serviceMethod.accepts.forEach(function (accept) { resolvedAccepts.push(accept); }); } if (resolvedAccepts.length > 0) { serviceMethod.resolvedAccepts = resolvedAccepts; } }; InternalServer.resolveProperties = function (serviceClass, serviceMethod) { InternalServer.resolveLanguages(serviceClass, serviceMethod); InternalServer.resolveAccepts(serviceClass, serviceMethod); InternalServer.resolvePath(serviceClass, serviceMethod); }; InternalServer.resolvePath = function (serviceClass, serviceMethod) { var classPath = serviceClass.path ? serviceClass.path.trim() : ''; var resolvedPath = _.startsWith(classPath, '/') ? classPath : '/' + classPath; if (_.endsWith(resolvedPath, '/')) { resolvedPath = resolvedPath.slice(0, resolvedPath.length - 1); } if (serviceMethod.path) { var methodPath = serviceMethod.path.trim(); resolvedPath = resolvedPath + (_.startsWith(methodPath, '/') ? methodPath : '/' + methodPath); } var declaredHttpMethods = InternalServer.paths.get(resolvedPath); if (!declaredHttpMethods) { declaredHttpMethods = new Set(); InternalServer.paths.set(resolvedPath, declaredHttpMethods); } if (declaredHttpMethods.has(serviceMethod.httpMethod)) { throw Error("Duplicated declaration for path [" + resolvedPath + "], method [" + serviceMethod.httpMethod + "]."); } declaredHttpMethods.add(serviceMethod.httpMethod); serviceMethod.resolvedPath = resolvedPath; }; return InternalServer; }()); InternalServer.serverClasses = new Map(); InternalServer.paths = new Map(); InternalServer.pathsResolved = false; InternalServer.serviceFactory = { create: function (serviceClass) { return new serviceClass(); }, getTargetClass: function (serviceClass) { return serviceClass; } }; exports.InternalServer = InternalServer; //# sourceMappingURL=server-container.js.map