UNPKG

typescript-rest

Version:
979 lines 28.8 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var server_container_1 = require("./server-container"); var server_types_1 = require("./server-types"); var metadata = require("./metadata"); require("reflect-metadata"); var _ = require("lodash"); /** * A decorator to tell the [[Server]] that a class or a method * should be bound to a given path. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ PUT * @ Path(':id') * savePerson(person:Person) { * // ... * } * * @ GET * @ Path(':id') * getPerson():Person { * // ... * } * } * ``` * * Will create services that listen for requests like: * * ``` * PUT http://mydomain/people/123 or * GET http://mydomain/people/123 * ``` */ function Path(path) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); if (args.length === 1) { return PathTypeDecorator.apply(this, [args[0], path]); } else if (args.length === 3 && typeof args[2] === 'object') { return PathMethodDecorator.apply(this, [args[0], args[1], args[2], path]); } throw new Error('Invalid @Path Decorator declaration.'); }; } exports.Path = Path; /** * A decorator to tell the [[Server]] that a class or a method * should only accept requests from clients that accepts one of * the supported languages. * * For example: * * ``` * @ Path('accept') * @ AcceptLanguage('en', 'pt-BR') * class TestAcceptService { * // ... * } * ``` * * Will reject requests that only accepts languages that are not * English or Brazilian portuguese * * If the language requested is not supported, a status code 406 returned */ function AcceptLanguage() { var languages = []; for (var _i = 0; _i < arguments.length; _i++) { languages[_i] = arguments[_i]; } return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); if (args.length === 1) { return AcceptLanguageTypeDecorator.apply(this, [args[0], languages]); } else if (args.length === 3 && typeof args[2] === 'object') { return AcceptLanguageMethodDecorator.apply(this, [args[0], args[1], args[2], languages]); } throw new Error('Invalid @AcceptLanguage Decorator declaration.'); }; } exports.AcceptLanguage = AcceptLanguage; /** * A decorator to tell the [[Server]] that a class or a method * should only accept requests from clients that accepts one of * the supported mime types. * * For example: * * ``` * @ Path('accept') * @ Accept('application/json') * class TestAcceptService { * // ... * } * ``` * * Will reject requests that only accepts mime types that are not * 'application/json' * * If the mime type requested is not supported, a status code 406 returned */ function Accept() { var accepts = []; for (var _i = 0; _i < arguments.length; _i++) { accepts[_i] = arguments[_i]; } return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); if (args.length === 1) { return AcceptTypeDecorator.apply(this, [args[0], accepts]); } else if (args.length === 3 && typeof args[2] === 'object') { return AcceptMethodDecorator.apply(this, [args[0], args[1], args[2], accepts]); } throw new Error('Invalid @Accept Decorator declaration.'); }; } exports.Accept = Accept; /** * A decorator to be used on class properties or on service method arguments * to inform that the decorated property or argument should be bound to the * [[ServiceContext]] object associated to the current request. * * For example: * * ``` * @ Path('context') * class TestService { * @ Context context: ServiceContext; * // ... * } * ``` * * The field context on the above class will point to the current * [[ServiceContext]] instance. */ function Context() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.context, null]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @Context Decorator declaration.'); } exports.Context = Context; /** * A decorator to be used on class properties or on service method arguments * to inform that the decorated property or argument should be bound to the * the current request. * * For example: * * ``` * @ Path('context') * class TestService { * @ ContextRequest request: express.Request; * // ... * } * ``` * * The field request on the above class will point to the current * request. */ function ContextRequest() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.context_request, null]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @ContextRequest Decorator declaration.'); } exports.ContextRequest = ContextRequest; /** * A decorator to be used on class properties or on service method arguments * to inform that the decorated property or argument should be bound to the * the current response object. * * For example: * * ``` * @ Path('context') * class TestService { * @ ContextResponse response: express.Response; * // ... * } * ``` * * The field response on the above class will point to the current * response object. */ function ContextResponse() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.context_response, null]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @ContextResponse Decorator declaration.'); } exports.ContextResponse = ContextResponse; /** * A decorator to be used on class properties or on service method arguments * to inform that the decorated property or argument should be bound to the * the next function. * * For example: * * ``` * @ Path('context') * class TestService { * @ ContextNext * next: express.NextFunction * // ... * } * ``` * * The next function can be used to delegate to the next registered * middleware the current request processing. */ function ContextNext() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.context_next, null]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @ContextNext Decorator declaration.'); } exports.ContextNext = ContextNext; /** * A decorator to be used on class properties or on service method arguments * to inform that the decorated property or argument should be bound to the * the current context language. * * For example: * * ``` * @ Path('context') * class TestService { * @ ContextLanguage * language: string * // ... * } * ``` */ function ContextLanguage() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.context_accept_language, null]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @ContextLanguage Decorator declaration.'); } exports.ContextLanguage = ContextLanguage; /** * A decorator to be used on class properties or on service method arguments * to inform that the decorated property or argument should be bound to the * the preferred media type for the current request. * * For example: * * ``` * @ Path('context') * class TestService { * @ ContextAccept * media: string * // ... * } * ``` */ function ContextAccept() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.context_accept, null]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @ContextAccept Decorator declaration.'); } exports.ContextAccept = ContextAccept; /** * A decorator to tell the [[Server]] that a method * should be called to process HTTP GET requests. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ GET * getPeople() { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * GET http://mydomain/people * ``` */ function GET(target, propertyKey, descriptor) { processHttpVerb(target, propertyKey, server_types_1.HttpMethod.GET); } exports.GET = GET; /** * A decorator to tell the [[Server]] that a method * should be called to process HTTP POST requests. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ POST * addPerson() { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * POST http://mydomain/people * ``` */ function POST(target, propertyKey, descriptor) { processHttpVerb(target, propertyKey, server_types_1.HttpMethod.POST); } exports.POST = POST; /** * A decorator to tell the [[Server]] that a method * should be called to process HTTP PUT requests. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ PUT * @ Path(':id') * savePerson(person: Person) { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * PUT http://mydomain/people/123 * ``` */ function PUT(target, propertyKey, descriptor) { processHttpVerb(target, propertyKey, server_types_1.HttpMethod.PUT); } exports.PUT = PUT; /** * A decorator to tell the [[Server]] that a method * should be called to process HTTP DELETE requests. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ DELETE * @ Path(':id') * removePerson(@ PathParam('id')id: string) { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * PUT http://mydomain/people/123 * ``` */ function DELETE(target, propertyKey, descriptor) { processHttpVerb(target, propertyKey, server_types_1.HttpMethod.DELETE); } exports.DELETE = DELETE; /** * A decorator to tell the [[Server]] that a method * should be called to process HTTP HEAD requests. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ HEAD * headPerson() { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * HEAD http://mydomain/people/123 * ``` */ function HEAD(target, propertyKey, descriptor) { processHttpVerb(target, propertyKey, server_types_1.HttpMethod.HEAD); } exports.HEAD = HEAD; /** * A decorator to tell the [[Server]] that a method * should be called to process HTTP OPTIONS requests. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ OPTIONS * optionsPerson() { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * OPTIONS http://mydomain/people/123 * ``` */ function OPTIONS(target, propertyKey, descriptor) { processHttpVerb(target, propertyKey, server_types_1.HttpMethod.OPTIONS); } exports.OPTIONS = OPTIONS; /** * A decorator to tell the [[Server]] that a method * should be called to process HTTP PATCH requests. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ PATCH * @ Path(':id') * savePerson(person: Person) { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * PATCH http://mydomain/people/123 * ``` */ function PATCH(target, propertyKey, descriptor) { processHttpVerb(target, propertyKey, server_types_1.HttpMethod.PATCH); } exports.PATCH = PATCH; /** * A decorator to inform options to pe passed to bodyParser. * You can inform any property accepted by * [[bodyParser]](https://www.npmjs.com/package/body-parser) */ function BodyOptions(options) { return function (target, propertyKey, descriptor) { var serviceMethod = server_container_1.InternalServer.registerServiceMethod(target.constructor, propertyKey); if (serviceMethod) { serviceMethod.bodyParserOptions = options; } }; } exports.BodyOptions = BodyOptions; /** * Creates a mapping between a fragment of the requested path and * a method argument. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ GET * @ Path(':id') * getPerson(@ PathParam('id') id: string) { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * GET http://mydomain/people/123 * ``` * * And pass 123 as the id argument on getPerson method's call. */ function PathParam(name) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.path, name]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @PathParam Decorator declaration.'); }; } exports.PathParam = PathParam; /** * Creates a mapping between a file on a multipart request and a method * argument. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ POST * @ Path('id') * addAvatar(@ PathParam('id') id: string, * @ FileParam('avatar') file: Express.Multer.File) { * // ... * } * } * ``` * * Will create a service that listen for requests and bind the * file with name 'avatar' on the requested form to the file * argument on addAvatar method's call. */ function FileParam(name) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.file, name]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @FileParam Decorator declaration.'); }; } exports.FileParam = FileParam; /** * Creates a mapping between a list of files on a multipart request and a method * argument. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ POST * @ Path('id') * addAvatar(@ PathParam('id') id: string, * @ FilesParam('avatar') Array<file>: Express.Multer.File) { * // ... * } * } * ``` * * Will create a service that listen for requests and bind the * files with name 'avatar' on the request form to the file * argument on addAvatar method's call. */ function FilesParam(name) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.files, name]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @FilesParam Decorator declaration.'); }; } exports.FilesParam = FilesParam; /** * Creates a mapping between a query parameter on request and a method * argument. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ GET * getPeople(@ QueryParam('name') name: string) { * // ... * } * } * ``` * * Will create a service that listen for requests like: * * ``` * GET http://mydomain/people?name=joe * ``` * * And pass 'joe' as the name argument on getPerson method's call. */ function QueryParam(name) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.query, name]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @QueryParam Decorator declaration.'); }; } exports.QueryParam = QueryParam; /** * Creates a mapping between a header on request and a method * argument. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ GET * getPeople(@ HeaderParam('header') header: string) { * // ... * } * } * ``` * * Will create a service that listen for requests and bind the * header called 'header' to the header argument on getPerson method's call. */ function HeaderParam(name) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.header, name]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @HeaderParam Decorator declaration.'); }; } exports.HeaderParam = HeaderParam; /** * Creates a mapping between a cookie on request and a method * argument. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ GET * getPeople(@ CookieParam('cookie') cookie: string) { * // ... * } * } * ``` * * Will create a service that listen for requests and bind the * cookie called 'cookie' to the cookie argument on getPerson method's call. */ function CookieParam(name) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.cookie, name]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @CookieParam Decorator declaration.'); }; } exports.CookieParam = CookieParam; /** * Creates a mapping between a form parameter on request and a method * argument. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ GET * getPeople(@ FormParam('name') name: string) { * // ... * } * } * ``` * * Will create a service that listen for requests and bind the * request paramenter called 'name' to the name argument on getPerson * method's call. */ function FormParam(name) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.form, name]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @FormParam Decorator declaration.'); }; } exports.FormParam = FormParam; /** * Creates a mapping between a parameter on request and a method * argument. * * For example: * * ``` * @ Path('people') * class PeopleService { * @ GET * getPeople(@ Param('name') name: string) { * // ... * } * } * ``` * * Will create a service that listen for requests and bind the * request paramenter called 'name' to the name argument on getPerson * method's call. It will work to query parameters or form parameters * received in the current request. */ function Param(name) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } args = _.without(args, undefined); var newArgs = args.concat([metadata.ParamType.param, name]); if (args.length < 3 || typeof args[2] === 'undefined') { return processDecoratedProperty.apply(this, newArgs); } else if (args.length === 3 && typeof args[2] === 'number') { return processDecoratedParameter.apply(this, newArgs); } throw new Error('Invalid @Param Decorator declaration.'); }; } exports.Param = Param; /** * Decorator processor for [[AcceptLanguage]] decorator on classes */ function AcceptLanguageTypeDecorator(target, languages) { var classData = server_container_1.InternalServer.registerServiceClass(target); classData.languages = _.union(classData.languages, languages); } /** * Decorator processor for [[AcceptLanguage]] decorator on methods */ function AcceptLanguageMethodDecorator(target, propertyKey, descriptor, languages) { var serviceMethod = server_container_1.InternalServer.registerServiceMethod(target.constructor, propertyKey); if (serviceMethod) { serviceMethod.languages = languages; } } /** * Decorator processor for [[Accept]] decorator on classes */ function AcceptTypeDecorator(target, accepts) { var classData = server_container_1.InternalServer.registerServiceClass(target); classData.accepts = _.union(classData.accepts, accepts); } /** * Decorator processor for [[Accept]] decorator on methods */ function AcceptMethodDecorator(target, propertyKey, descriptor, accepts) { var serviceMethod = server_container_1.InternalServer.registerServiceMethod(target.constructor, propertyKey); if (serviceMethod) { serviceMethod.accepts = accepts; } } /** * Decorator processor for [[Path]] decorator on classes */ function PathTypeDecorator(target, path) { var classData = server_container_1.InternalServer.registerServiceClass(target); classData.path = path; } /** * Decorator processor for [[Path]] decorator on methods */ function PathMethodDecorator(target, propertyKey, descriptor, path) { var serviceMethod = server_container_1.InternalServer.registerServiceMethod(target.constructor, propertyKey); if (serviceMethod) { serviceMethod.path = path; } } /** * Decorator processor for parameter annotations on methods */ function processDecoratedParameter(target, propertyKey, parameterIndex, paramType, name) { var serviceMethod = server_container_1.InternalServer.registerServiceMethod(target.constructor, propertyKey); if (serviceMethod) { var paramTypes = Reflect.getOwnMetadata('design:paramtypes', target, propertyKey); while (paramTypes && serviceMethod.parameters.length < paramTypes.length) { serviceMethod.parameters.push(new metadata.MethodParam(null, paramTypes[serviceMethod.parameters.length], metadata.ParamType.body)); } serviceMethod.parameters[parameterIndex] = new metadata.MethodParam(name, paramTypes[parameterIndex], paramType); } } /** * Decorator processor for annotations on properties */ function processDecoratedProperty(target, key, paramType, paramName) { var classData = server_container_1.InternalServer.registerServiceClass(target.constructor); var propertyType = Reflect.getMetadata('design:type', target, key); classData.addProperty(key, paramType, paramName, propertyType); } /** * Decorator processor for HTTP verb annotations on methods */ function processHttpVerb(target, propertyKey, httpMethod) { var serviceMethod = server_container_1.InternalServer.registerServiceMethod(target.constructor, propertyKey); if (serviceMethod) { if (serviceMethod.httpMethod) { throw new Error('Method is already annotated with @' + serviceMethod.httpMethod + '. You can only map a method to one HTTP verb.'); } serviceMethod.httpMethod = httpMethod; processServiceMethod(target, propertyKey, serviceMethod); } } /** * Extract metadata for rest methods */ function processServiceMethod(target, propertyKey, serviceMethod) { serviceMethod.name = propertyKey; var paramTypes = Reflect.getOwnMetadata('design:paramtypes', target, propertyKey); while (paramTypes && paramTypes.length > serviceMethod.parameters.length) { serviceMethod.parameters.push(new metadata.MethodParam(null, paramTypes[serviceMethod.parameters.length], metadata.ParamType.body)); } serviceMethod.parameters.forEach(function (param) { if (param.paramType === metadata.ParamType.cookie) { serviceMethod.mustParseCookies = true; } else if (param.paramType === metadata.ParamType.file) { serviceMethod.files.push(new metadata.FileParam(param.name, true)); } else if (param.paramType === metadata.ParamType.files) { serviceMethod.files.push(new metadata.FileParam(param.name, false)); } else if (param.paramType === metadata.ParamType.param) { serviceMethod.acceptMultiTypedParam = true; } else if (param.paramType === metadata.ParamType.form) { if (serviceMethod.mustParseBody) { throw Error('Can not use form parameters with a body parameter on the same method.'); } serviceMethod.mustParseForms = true; } else if (param.paramType === metadata.ParamType.body) { if (serviceMethod.mustParseForms) { throw Error('Can not use form parameters with a body parameter on the same method.'); } if (serviceMethod.mustParseBody) { throw Error('Can not use more than one body parameter on the same method.'); } serviceMethod.mustParseBody = true; } }); } //# sourceMappingURL=decorators.js.map