From d13f28f400fd1d19fcc2bc06a172feb040de3e01 Mon Sep 17 00:00:00 2001 From: Teddy Chambard Date: Wed, 4 Jul 2018 10:46:34 +0200 Subject: [PATCH 1/5] Add StreamFile(s)Param decorators --- src/decorators.ts | 23 +++++++++++++++++++++++ src/metadata/parameterGenerator.ts | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/src/decorators.ts b/src/decorators.ts index af6e42c..640e93f 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -110,3 +110,26 @@ export function IsFloat(target: any, propertyKey: string, parameterIndex?: numbe export function IsDouble(target: any, propertyKey: string, parameterIndex?: number) { return; } + +/** + * Creates a mapping between a file on a multipart request and a method + * argument. + * Unlike @FileParam provided by typescript-rest, this decorator allows to pipe the request. + */ +export function StreamFileParam(name: string) { + return function (...args: any[]) { + return; + }; +} + +/** + * Creates a mapping between multiple files on a multipart request and a method + * argument. + * Unlike @FileParam provided by typescript-rest, this decorator allows to pipe the request. + */ +export function StreamFilesParam(name: string) { + return function (...args: any[]) { + return; + }; +} + diff --git a/src/metadata/parameterGenerator.ts b/src/metadata/parameterGenerator.ts index 5c1699f..051efa2 100644 --- a/src/metadata/parameterGenerator.ts +++ b/src/metadata/parameterGenerator.ts @@ -31,6 +31,10 @@ export class ParameterGenerator { return this.getFileParameter(this.parameter); case 'FilesParam': return this.getFilesParameter(this.parameter); + case 'StreamFileParam': + return this.getFileParameter(this.parameter); + case 'StreamFilesParam': + return this.getFilesParameter(this.parameter); case 'Context': case 'ContextRequest': case 'ContextResponse': From 9bc704a3393c475f4e799f3d63268d9e2d1affac Mon Sep 17 00:00:00 2001 From: Teddy Chambard Date: Wed, 4 Jul 2018 11:10:20 +0200 Subject: [PATCH 2/5] Fix missing conditions --- src/metadata/parameterGenerator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/metadata/parameterGenerator.ts b/src/metadata/parameterGenerator.ts index 051efa2..6e952d8 100644 --- a/src/metadata/parameterGenerator.ts +++ b/src/metadata/parameterGenerator.ts @@ -93,7 +93,7 @@ export class ParameterGenerator { return { description: this.getParameterDescription(parameter), in: 'formData', - name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FileParam') || parameterName, + name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FileParam' || ident.text === 'StreamFileParam') || parameterName, parameterName, required: !parameter.questionToken, type: { typeName: 'file' } @@ -110,7 +110,7 @@ export class ParameterGenerator { return { description: this.getParameterDescription(parameter), in: 'formData', - name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FilesParam') || parameterName, + name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FilesParam' || ident.text === 'StreamFilesParam') || parameterName, parameterName, required: !parameter.questionToken, type: { typeName: 'file' } @@ -259,7 +259,7 @@ export class ParameterGenerator { return ['HeaderParam', 'QueryParam', 'Param', 'FileParam', 'PathParam', 'FilesParam', 'FormParam', 'CookieParam', 'Context', 'ContextRequest', 'ContextResponse', 'ContextNext', - 'ContextLanguage', 'ContextAccept'].some(d => d === decoratorName); + 'ContextLanguage', 'ContextAccept', 'StreamFileParam', 'StreamFilesParam'].some(d => d === decoratorName); } private supportPathDataType(parameterType: Type) { From 990aa14d10d42d4c5c05d05374f6bba46d435010 Mon Sep 17 00:00:00 2001 From: Teddy Chambard Date: Fri, 19 Oct 2018 15:08:08 +0200 Subject: [PATCH 3/5] Add unit tests --- test/data/apis.ts | 12 ++++++++++++ test/unit/definitions.spec.ts | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/test/data/apis.ts b/test/data/apis.ts index 096d047..4ba4c0b 100644 --- a/test/data/apis.ts +++ b/test/data/apis.ts @@ -339,6 +339,18 @@ export class ParameterizedEndpoint { test(@PathParam('objectId') objectId: string): PrimitiveClassModel { return new PrimitiveClassModel(); } + + @Path('/file') + @POST + file(@FileParam('file') file: Express.Multer.File): PrimitiveClassModel { + return new PrimitiveClassModel(); + } + + @Path('/stream') + @POST + stream(@swagger.StreamFileParam('stream') file: Express.Multer.File): PrimitiveClassModel { + return new PrimitiveClassModel(); + } } export abstract class Entity { diff --git a/test/unit/definitions.spec.ts b/test/unit/definitions.spec.ts index 75977e8..0ce5577 100644 --- a/test/unit/definitions.spec.ts +++ b/test/unit/definitions.spec.ts @@ -298,6 +298,16 @@ describe('Definition generation', () => { const expression = jsonata('paths."/parameterized/{objectId}/test".get.parameters[0].in'); expect(expression.evaluate(spec)).to.eq('path'); }); + + it('should generate formData param for params declared on method', () => { + const expression = jsonata('paths."/parameterized/{objectId}/file".post.parameters[0].in'); + expect(expression.evaluate(spec)).to.eq('formData'); + }); + + it('should generate path param for params declared on class', () => { + const expression = jsonata('paths."/parameterized/{objectId}/stream".post.parameters[0].in'); + expect(expression.evaluate(spec)).to.eq('formData'); + }); }); describe('AbstractEntityEndpoint', () => { From cb45ed9acfa23bb8bb2247451f8ef1bc349214d2 Mon Sep 17 00:00:00 2001 From: Teddy Chambard Date: Wed, 4 Jul 2018 10:46:34 +0200 Subject: [PATCH 4/5] Add StreamFile(s)Param decorators --- src/decorators.ts | 23 +++++++++++++++++++++++ src/metadata/parameterGenerator.ts | 10 +++++++--- test/data/apis.ts | 12 ++++++++++++ test/unit/definitions.spec.ts | 10 ++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/decorators.ts b/src/decorators.ts index af6e42c..640e93f 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -110,3 +110,26 @@ export function IsFloat(target: any, propertyKey: string, parameterIndex?: numbe export function IsDouble(target: any, propertyKey: string, parameterIndex?: number) { return; } + +/** + * Creates a mapping between a file on a multipart request and a method + * argument. + * Unlike @FileParam provided by typescript-rest, this decorator allows to pipe the request. + */ +export function StreamFileParam(name: string) { + return function (...args: any[]) { + return; + }; +} + +/** + * Creates a mapping between multiple files on a multipart request and a method + * argument. + * Unlike @FileParam provided by typescript-rest, this decorator allows to pipe the request. + */ +export function StreamFilesParam(name: string) { + return function (...args: any[]) { + return; + }; +} + diff --git a/src/metadata/parameterGenerator.ts b/src/metadata/parameterGenerator.ts index 5c1699f..6e952d8 100644 --- a/src/metadata/parameterGenerator.ts +++ b/src/metadata/parameterGenerator.ts @@ -31,6 +31,10 @@ export class ParameterGenerator { return this.getFileParameter(this.parameter); case 'FilesParam': return this.getFilesParameter(this.parameter); + case 'StreamFileParam': + return this.getFileParameter(this.parameter); + case 'StreamFilesParam': + return this.getFilesParameter(this.parameter); case 'Context': case 'ContextRequest': case 'ContextResponse': @@ -89,7 +93,7 @@ export class ParameterGenerator { return { description: this.getParameterDescription(parameter), in: 'formData', - name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FileParam') || parameterName, + name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FileParam' || ident.text === 'StreamFileParam') || parameterName, parameterName, required: !parameter.questionToken, type: { typeName: 'file' } @@ -106,7 +110,7 @@ export class ParameterGenerator { return { description: this.getParameterDescription(parameter), in: 'formData', - name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FilesParam') || parameterName, + name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FilesParam' || ident.text === 'StreamFilesParam') || parameterName, parameterName, required: !parameter.questionToken, type: { typeName: 'file' } @@ -255,7 +259,7 @@ export class ParameterGenerator { return ['HeaderParam', 'QueryParam', 'Param', 'FileParam', 'PathParam', 'FilesParam', 'FormParam', 'CookieParam', 'Context', 'ContextRequest', 'ContextResponse', 'ContextNext', - 'ContextLanguage', 'ContextAccept'].some(d => d === decoratorName); + 'ContextLanguage', 'ContextAccept', 'StreamFileParam', 'StreamFilesParam'].some(d => d === decoratorName); } private supportPathDataType(parameterType: Type) { diff --git a/test/data/apis.ts b/test/data/apis.ts index 096d047..4ba4c0b 100644 --- a/test/data/apis.ts +++ b/test/data/apis.ts @@ -339,6 +339,18 @@ export class ParameterizedEndpoint { test(@PathParam('objectId') objectId: string): PrimitiveClassModel { return new PrimitiveClassModel(); } + + @Path('/file') + @POST + file(@FileParam('file') file: Express.Multer.File): PrimitiveClassModel { + return new PrimitiveClassModel(); + } + + @Path('/stream') + @POST + stream(@swagger.StreamFileParam('stream') file: Express.Multer.File): PrimitiveClassModel { + return new PrimitiveClassModel(); + } } export abstract class Entity { diff --git a/test/unit/definitions.spec.ts b/test/unit/definitions.spec.ts index 75977e8..0ce5577 100644 --- a/test/unit/definitions.spec.ts +++ b/test/unit/definitions.spec.ts @@ -298,6 +298,16 @@ describe('Definition generation', () => { const expression = jsonata('paths."/parameterized/{objectId}/test".get.parameters[0].in'); expect(expression.evaluate(spec)).to.eq('path'); }); + + it('should generate formData param for params declared on method', () => { + const expression = jsonata('paths."/parameterized/{objectId}/file".post.parameters[0].in'); + expect(expression.evaluate(spec)).to.eq('formData'); + }); + + it('should generate path param for params declared on class', () => { + const expression = jsonata('paths."/parameterized/{objectId}/stream".post.parameters[0].in'); + expect(expression.evaluate(spec)).to.eq('formData'); + }); }); describe('AbstractEntityEndpoint', () => { From 2cb7cb9987eed82617ef14465210d6cb0edb3cba Mon Sep 17 00:00:00 2001 From: "t.chambard" Date: Fri, 23 Oct 2020 09:05:29 +0200 Subject: [PATCH 5/5] Apply changes after merge --- test/data/apis.ts | 21 +++++---------------- test/unit/definitions.spec.ts | 8 ++++---- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/test/data/apis.ts b/test/data/apis.ts index 7faeca7..049c339 100644 --- a/test/data/apis.ts +++ b/test/data/apis.ts @@ -1,8 +1,9 @@ 'use strict'; import { - Accept, DELETE, FormParam, GET, Path, - PathParam, POST, PUT, QueryParam, + Accept, DELETE, FileParam, FormParam, GET, + Path, PathParam, POST, PUT, + QueryParam, Return, Security } from 'typescript-rest'; @@ -402,25 +403,13 @@ export class ParameterizedEndpoint { @Path('/file') @POST - file(@FileParam('file') file: Express.Multer.File): PrimitiveClassModel { + public file(@FileParam('file') file: Express.Multer.File): PrimitiveClassModel { return new PrimitiveClassModel(); } @Path('/stream') @POST - stream(@swagger.StreamFileParam('stream') file: Express.Multer.File): PrimitiveClassModel { - return new PrimitiveClassModel(); - } - - @Path('/file') - @POST - file(@FileParam('file') file: Express.Multer.File): PrimitiveClassModel { - return new PrimitiveClassModel(); - } - - @Path('/stream') - @POST - stream(@swagger.StreamFileParam('stream') file: Express.Multer.File): PrimitiveClassModel { + public stream(@swagger.StreamFileParam('stream') file: Express.Multer.File): PrimitiveClassModel { return new PrimitiveClassModel(); } } diff --git a/test/unit/definitions.spec.ts b/test/unit/definitions.spec.ts index 5a4d0ad..9ad179f 100644 --- a/test/unit/definitions.spec.ts +++ b/test/unit/definitions.spec.ts @@ -361,22 +361,22 @@ describe('Definition generation', () => { it('should generate formData param for params declared on method', () => { const expression = jsonata('paths."/parameterized/{objectId}/file".post.parameters[0].in'); - expect(expression.evaluate(spec)).to.eq('formData'); + expect(expression.evaluate(spec)).toEqual('formData'); }); it('should generate path param for params declared on class', () => { const expression = jsonata('paths."/parameterized/{objectId}/stream".post.parameters[0].in'); - expect(expression.evaluate(spec)).to.eq('formData'); + expect(expression.evaluate(spec)).toEqual('formData'); }); it('should generate formData param for params declared on method', () => { const expression = jsonata('paths."/parameterized/{objectId}/file".post.parameters[0].in'); - expect(expression.evaluate(spec)).to.eq('formData'); + expect(expression.evaluate(spec)).toEqual('formData'); }); it('should generate path param for params declared on class', () => { const expression = jsonata('paths."/parameterized/{objectId}/stream".post.parameters[0].in'); - expect(expression.evaluate(spec)).to.eq('formData'); + expect(expression.evaluate(spec)).toEqual('formData'); }); });