From 8a75d629a29183f117c94c80ba04f575c2c90929 Mon Sep 17 00:00:00 2001 From: Carterhaugh LLC Date: Thu, 18 Jul 2019 17:04:58 -0700 Subject: [PATCH 1/8] set up context request property decorator --- src/decorators/parameters.ts | 34 ++++++++++++++++++++++++++++++ src/server/model/metadata.ts | 1 + src/server/parameter-processor.ts | 1 + test/integration/datatypes.spec.ts | 2 +- 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/decorators/parameters.ts b/src/decorators/parameters.ts index 0a92792..5862dbf 100644 --- a/src/decorators/parameters.ts +++ b/src/decorators/parameters.ts @@ -54,6 +54,40 @@ export function ContextRequest(...args: Array) { .decorateParameterOrProperty(args); } +/** + * 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 given property name in the current request. This could be used, for + * example, to extract values inserted by prior middlewares into the request + * object. + * + * For example: + * + * ``` + * const authMiddleware = async (req, res, next) => { + * const userId: string = await authenticateRequestAndReturnCurrentUserId(...); + * req.userId = userId; + * next(); + * } + * + * ... + * + * @ Path('people') + * class PeopleService { + * @ GET + * getCurrentUser(@ ContextRequestProperty('userId') userId: string) { + * } + * ``` + * + * The `authMiddleware` function will insert the `userId` property into + * the request object. Then, that value will be passed along into the + * `userId` parameter when `getCurrentUser` is called. + */ +export function ContextRequestProperty(name: string) { + return new ParameterDecorator('ContextRequestProperty').withType(ParamType.context_request_property).withName(name) + .decorateNamedParameterOrProperty(); +} + /** * 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 diff --git a/src/server/model/metadata.ts b/src/server/model/metadata.ts index 122208b..20b5c74 100644 --- a/src/server/model/metadata.ts +++ b/src/server/model/metadata.ts @@ -115,6 +115,7 @@ export enum ParamType { files = 'files', context = 'context', context_request = 'context_request', + context_request_property = 'context_request_property', context_response = 'context_response', context_next = 'context_next', context_accept = 'context_accept', diff --git a/src/server/parameter-processor.ts b/src/server/parameter-processor.ts index 86d6989..c6b719c 100644 --- a/src/server/parameter-processor.ts +++ b/src/server/parameter-processor.ts @@ -42,6 +42,7 @@ export class ParameterProcessor { parameterMapper.set(ParamType.header, (context, property) => this.convertType(context.request.header(property.name), property.propertyType)); parameterMapper.set(ParamType.cookie, (context, property) => this.convertType(context.request.cookies[property.name], property.propertyType)); parameterMapper.set(ParamType.body, (context, property) => this.convertType(context.request.body, property.propertyType)); + parameterMapper.set(ParamType.context_request_property, (context, property) => this.convertType((context.request as any)[property.name], property.propertyType)); parameterMapper.set(ParamType.file, (context, property) => { this.debugger.runtime('Processing file parameter'); // @ts-ignore diff --git a/test/integration/datatypes.spec.ts b/test/integration/datatypes.spec.ts index b4ca066..f3022c2 100644 --- a/test/integration/datatypes.spec.ts +++ b/test/integration/datatypes.spec.ts @@ -9,7 +9,7 @@ import * as request from 'request'; import { Container } from 'typescript-ioc'; import { BodyOptions, BodyType, Context, ContextNext, - ContextRequest, ContextResponse, CookieParam, FileParam, FormParam, + ContextRequest, ContextRequestProperty, ContextResponse, CookieParam, FileParam, FormParam, GET, HeaderParam, Param, ParserType, Path, PathParam, POST, PUT, QueryParam, Return, Server, ServiceContext } from '../../src/typescript-rest'; const expect = chai.expect; From 10c6cab7df68c2ee63679a5c97fe4d1ff6fca24f Mon Sep 17 00:00:00 2001 From: Carterhaugh LLC Date: Thu, 18 Jul 2019 17:08:18 -0700 Subject: [PATCH 2/8] update comments --- src/decorators/parameters.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/decorators/parameters.ts b/src/decorators/parameters.ts index 5862dbf..e9cb0db 100644 --- a/src/decorators/parameters.ts +++ b/src/decorators/parameters.ts @@ -55,11 +55,9 @@ export function ContextRequest(...args: Array) { } /** - * 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 given property name in the current request. This could be used, for - * example, to extract values inserted by prior middlewares into the request - * object. + * Creates a mapping between properties on the request object and a method + * argument. This could be used, for example, to extract values inserted by + * prior middlewares into the request. * * For example: * From 9177e172e761c89310f203cabb5c607ea0fce3f3 Mon Sep 17 00:00:00 2001 From: Carterhaugh LLC Date: Fri, 19 Jul 2019 13:18:49 -0700 Subject: [PATCH 3/8] set up tests --- test/integration/datatypes.spec.ts | 54 ++++++++++++++++++++++++++++-- test/unit/decorators.spec.ts | 3 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/test/integration/datatypes.spec.ts b/test/integration/datatypes.spec.ts index f3022c2..5db18c1 100644 --- a/test/integration/datatypes.spec.ts +++ b/test/integration/datatypes.spec.ts @@ -10,7 +10,7 @@ import { Container } from 'typescript-ioc'; import { BodyOptions, BodyType, Context, ContextNext, ContextRequest, ContextRequestProperty, ContextResponse, CookieParam, FileParam, FormParam, - GET, HeaderParam, Param, ParserType, Path, PathParam, POST, PUT, QueryParam, Return, Server, ServiceContext + GET, HeaderParam, Param, ParserType, Path, PathParam, POST, PreProcessor, PUT, QueryParam, Return, Server, ServiceContext } from '../../src/typescript-rest'; const expect = chai.expect; @@ -223,6 +223,37 @@ export class TestReturnService { } } +export function testContextRequestPropertyMiddleware(req: any) { + req.userId = '123'; +} + +@Path("testcontextrequestproperty") +export class TestContextRequestPropertyService { + @GET + @Path('existent') + @PreProcessor(testContextRequestPropertyMiddleware) + public testSuccessfulInjection(@ContextRequestProperty('userId') userId: string) { + if (userId === '123') { + return 'OK'; + } + else { + return 'NOT OK'; + } + } + + @GET + @Path('nonexistent') + @PreProcessor(testContextRequestPropertyMiddleware) + public testNonexistentInjection(@ContextRequestProperty('otherName') otherName: string) { + if (otherName) { + return 'NOT OK'; + } + else { + return 'OK'; + } + } +} + describe('Data Types Tests', () => { before(() => { @@ -525,6 +556,7 @@ describe('Data Types Tests', () => { }); }); }); + describe('Param Converters', () => { it('should intercept parameters', (done) => { @@ -548,7 +580,25 @@ describe('Data Types Tests', () => { }); }); + describe('ContextRequestProperty injection', () => { + it('should have injected an existent request property', (done) => { + request.get({ + url: 'http://localhost:5674/testcontextrequestproperty/existent' + }, (error, response, body) => { + expect(body).to.eq('OK'); + done(); + }); + }); + it('should not have injected a non-existent request property', (done) => { + request.get({ + url: 'http://localhost:5674/testcontextrequestproperty/nonexistent' + }, (error, response, body) => { + expect(body).to.eq('OK'); + done(); + }); + }); + }); }); @@ -558,7 +608,7 @@ export function startApi(): Promise { return new Promise((resolve, reject) => { const app: express.Application = express(); app.set('env', 'test'); - Server.buildServices(app, TestParamsService, TestReturnService); + Server.buildServices(app, TestParamsService, TestReturnService, TestContextRequestPropertyService); app.use('/testreturn', (req, res, next) => { if (!res.headersSent) { res.send('handled by middleware'); diff --git a/test/unit/decorators.spec.ts b/test/unit/decorators.spec.ts index b47d84c..2828ae1 100644 --- a/test/unit/decorators.spec.ts +++ b/test/unit/decorators.spec.ts @@ -361,7 +361,8 @@ describe('Decorators', () => { { name: 'HeaderParam', paramType: ParamType.header }, { name: 'CookieParam', paramType: ParamType.cookie }, { name: 'FormParam', paramType: ParamType.form }, - { name: 'Param', paramType: ParamType.param } + { name: 'Param', paramType: ParamType.param }, + // { name: 'ContextRequestProperty', paramType: ParamType.context_request_property } ].forEach(test => { describe(`${test.name} Decorator`, () => { it(`should bind a @${test.name} to one service property`, async () => { From 57c7086ed594e40b7b7562c375fd46af6b25b3f4 Mon Sep 17 00:00:00 2001 From: Carterhaugh LLC Date: Fri, 19 Jul 2019 13:21:00 -0700 Subject: [PATCH 4/8] swap check order --- test/integration/datatypes.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/datatypes.spec.ts b/test/integration/datatypes.spec.ts index 5db18c1..e5d03fb 100644 --- a/test/integration/datatypes.spec.ts +++ b/test/integration/datatypes.spec.ts @@ -245,11 +245,11 @@ export class TestContextRequestPropertyService { @Path('nonexistent') @PreProcessor(testContextRequestPropertyMiddleware) public testNonexistentInjection(@ContextRequestProperty('otherName') otherName: string) { - if (otherName) { - return 'NOT OK'; + if (!otherName) { + return 'OK'; } else { - return 'OK'; + return 'NOT OK'; } } } From a859465ab759516534b8ec6ef7f27a9bbf9db4df Mon Sep 17 00:00:00 2001 From: Carterhaugh LLC Date: Fri, 19 Jul 2019 13:27:05 -0700 Subject: [PATCH 5/8] uncommented ContextRequestProperty from test --- test/unit/decorators.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/decorators.spec.ts b/test/unit/decorators.spec.ts index 2828ae1..f030c1a 100644 --- a/test/unit/decorators.spec.ts +++ b/test/unit/decorators.spec.ts @@ -362,7 +362,7 @@ describe('Decorators', () => { { name: 'CookieParam', paramType: ParamType.cookie }, { name: 'FormParam', paramType: ParamType.form }, { name: 'Param', paramType: ParamType.param }, - // { name: 'ContextRequestProperty', paramType: ParamType.context_request_property } + { name: 'ContextRequestProperty', paramType: ParamType.context_request_property } ].forEach(test => { describe(`${test.name} Decorator`, () => { it(`should bind a @${test.name} to one service property`, async () => { From f1b8ba4a9bc61bd1addca5be9dc8d478ad7a5666 Mon Sep 17 00:00:00 2001 From: Carterhaugh LLC Date: Sat, 20 Jul 2019 14:29:24 -0700 Subject: [PATCH 6/8] postinstall for forked repos --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 9291688..b97c0c9 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "lint": "tslint ./src/**/*.ts ./test/**/*.ts", "lint:fix": "tslint --fix ./src/**/*.ts ./test/**/*.ts -t verbose", "pretest": "cross-env NODE_ENV=test npm run build && npm run lint", + "postinstall": "npm run build", "test": "cross-env NODE_ENV=test mocha --exit", "test:integration": "cross-env NODE_ENV=test mocha --opts ./test/mocha-integration.opts --exit", "test:unit": "cross-env NODE_ENV=test mocha --opts ./test/mocha-unit.opts --exit", From 078e78c9870a2d4f21feda356a6ab07ffd50e106 Mon Sep 17 00:00:00 2001 From: Carterhaugh LLC Date: Sat, 20 Jul 2019 14:31:34 -0700 Subject: [PATCH 7/8] Revert "uncommented ContextRequestProperty from test" This reverts commit a859465ab759516534b8ec6ef7f27a9bbf9db4df. --- test/unit/decorators.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/decorators.spec.ts b/test/unit/decorators.spec.ts index f030c1a..2828ae1 100644 --- a/test/unit/decorators.spec.ts +++ b/test/unit/decorators.spec.ts @@ -362,7 +362,7 @@ describe('Decorators', () => { { name: 'CookieParam', paramType: ParamType.cookie }, { name: 'FormParam', paramType: ParamType.form }, { name: 'Param', paramType: ParamType.param }, - { name: 'ContextRequestProperty', paramType: ParamType.context_request_property } + // { name: 'ContextRequestProperty', paramType: ParamType.context_request_property } ].forEach(test => { describe(`${test.name} Decorator`, () => { it(`should bind a @${test.name} to one service property`, async () => { From 3e941bd635682cd89e09c6c69dab2f4739b4a7c4 Mon Sep 17 00:00:00 2001 From: Carterhaugh LLC Date: Sat, 20 Jul 2019 14:32:55 -0700 Subject: [PATCH 8/8] small fixes --- package.json | 1 - test/unit/decorators.spec.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index b97c0c9..9291688 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "lint": "tslint ./src/**/*.ts ./test/**/*.ts", "lint:fix": "tslint --fix ./src/**/*.ts ./test/**/*.ts -t verbose", "pretest": "cross-env NODE_ENV=test npm run build && npm run lint", - "postinstall": "npm run build", "test": "cross-env NODE_ENV=test mocha --exit", "test:integration": "cross-env NODE_ENV=test mocha --opts ./test/mocha-integration.opts --exit", "test:unit": "cross-env NODE_ENV=test mocha --opts ./test/mocha-unit.opts --exit", diff --git a/test/unit/decorators.spec.ts b/test/unit/decorators.spec.ts index 2828ae1..f030c1a 100644 --- a/test/unit/decorators.spec.ts +++ b/test/unit/decorators.spec.ts @@ -362,7 +362,7 @@ describe('Decorators', () => { { name: 'CookieParam', paramType: ParamType.cookie }, { name: 'FormParam', paramType: ParamType.form }, { name: 'Param', paramType: ParamType.param }, - // { name: 'ContextRequestProperty', paramType: ParamType.context_request_property } + { name: 'ContextRequestProperty', paramType: ParamType.context_request_property } ].forEach(test => { describe(`${test.name} Decorator`, () => { it(`should bind a @${test.name} to one service property`, async () => {