From f5a92fbbefe7c8e90ce73f93e00a69b4810bf611 Mon Sep 17 00:00:00 2001 From: Clooooode Date: Thu, 18 Jan 2024 21:52:48 +0800 Subject: [PATCH 1/4] refactor: interface & implementations of hbs templates --- packages/cli/src/index.ts | 3 + .../routeGeneration/defaultRouteGenerator.ts | 8 +- .../templates/{ => express}/express.hbs | 99 +--------------- .../express/expressTemplateService.ts | 95 +++++++++++++++ .../templates/{ => hapi}/hapi.hbs | 109 +----------------- .../templates/hapi/hapiTemplateService.ts | 106 +++++++++++++++++ .../templates/{ => koa}/koa.hbs | 109 +----------------- .../templates/koa/koaTemplateService.ts | 99 ++++++++++++++++ .../templates/templateService.ts | 13 +++ packages/cli/tsconfig.json | 2 +- 10 files changed, 336 insertions(+), 307 deletions(-) rename packages/cli/src/routeGeneration/templates/{ => express}/express.hbs (59%) create mode 100644 packages/cli/src/routeGeneration/templates/express/expressTemplateService.ts rename packages/cli/src/routeGeneration/templates/{ => hapi}/hapi.hbs (69%) create mode 100644 packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts rename packages/cli/src/routeGeneration/templates/{ => koa}/koa.hbs (57%) create mode 100644 packages/cli/src/routeGeneration/templates/koa/koaTemplateService.ts create mode 100644 packages/cli/src/routeGeneration/templates/templateService.ts diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index fcae90e4c..cd42e1e33 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,5 +1,8 @@ export * from './module/generate-spec'; export * from './module/generate-routes'; +export { ExpressTemplateService } from './routeGeneration/templates/express/expressTemplateService'; +export { HapiTemplateService } from './routeGeneration/templates/hapi/hapiTemplateService'; +export { KoaTemplateService } from './routeGeneration/templates/koa/koaTemplateService'; export { AbstractRouteGenerator } from './routeGeneration/routeGenerator'; export { DefaultRouteGenerator } from './routeGeneration/defaultRouteGenerator'; export { Config } from '@tsoa/runtime'; diff --git a/packages/cli/src/routeGeneration/defaultRouteGenerator.ts b/packages/cli/src/routeGeneration/defaultRouteGenerator.ts index cad14531f..14a1e75c9 100644 --- a/packages/cli/src/routeGeneration/defaultRouteGenerator.ts +++ b/packages/cli/src/routeGeneration/defaultRouteGenerator.ts @@ -16,17 +16,17 @@ export class DefaultRouteGenerator extends AbstractRouteGenerator path; break; case 'koa': - this.template = path.join(__dirname, '..', 'routeGeneration/templates/koa.hbs'); + this.template = path.join(__dirname, '..', 'routeGeneration/templates/koa/koa.hbs'); break; default: - this.template = path.join(__dirname, '..', 'routeGeneration/templates/express.hbs'); + this.template = path.join(__dirname, '..', 'routeGeneration/templates/express/express.hbs'); } if (options.middlewareTemplate) { diff --git a/packages/cli/src/routeGeneration/templates/express.hbs b/packages/cli/src/routeGeneration/templates/express/express.hbs similarity index 59% rename from packages/cli/src/routeGeneration/templates/express.hbs rename to packages/cli/src/routeGeneration/templates/express/express.hbs index b05f33fbb..ab866976f 100644 --- a/packages/cli/src/routeGeneration/templates/express.hbs +++ b/packages/cli/src/routeGeneration/templates/express/express.hbs @@ -1,7 +1,8 @@ /* tslint:disable */ /* eslint-disable */ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa -import { Controller, ValidationService, FieldErrors, ValidateError, TsoaRoute, HttpStatusCodeLiteral, TsoaResponse, fetchMiddlewares } from '@tsoa/runtime'; +import { TsoaRoute, fetchMiddlewares } from '@tsoa/runtime'; +import { ExpressTemplateService } from '@tsoa/cli'; {{#each controllers}} // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa import { {{name}} } from '{{modulePath}}'; @@ -50,7 +51,7 @@ const models: TsoaRoute.Models = { // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa {{/each}} }; -const validationService = new ValidationService(models); +const templateService = new ExpressTemplateService(models, {{{ json minimalSwaggerConfig}}}); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa @@ -85,7 +86,7 @@ export function RegisterRoutes(app: Router) { let validatedArgs: any[] = []; try { - validatedArgs = getValidatedArgs(args, request, response); + validatedArgs = templateService.getValidatedArgs(args, request, response); {{#if ../../iocModule}} const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer; @@ -100,7 +101,7 @@ export function RegisterRoutes(app: Router) { const promise = controller.{{name}}.apply(controller, validatedArgs as any); - promiseHandler(controller, promise, response, {{successStatus}}, next); + templateService.promiseHandler(controller, promise, response, {{successStatus}}, next); } catch (err) { return next(err); } @@ -174,96 +175,6 @@ export function RegisterRoutes(app: Router) { {{/if}} // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function isController(object: any): object is Controller { - return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; - } - - function promiseHandler(controllerObj: any, promise: any, response: any, successStatus: any, next: any) { - return Promise.resolve(promise) - .then((data: any) => { - let statusCode = successStatus; - let headers; - if (isController(controllerObj)) { - headers = controllerObj.getHeaders(); - statusCode = controllerObj.getStatus() || statusCode; - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - returnHandler(response, statusCode, data, headers) - }) - .catch((error: any) => next(error)); - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function returnHandler(response: any, statusCode?: number, data?: any, headers: any = {}) { - if (response.headersSent) { - return; - } - Object.keys(headers).forEach((name: string) => { - response.set(name, headers[name]); - }); - if (data && typeof data.pipe === 'function' && data.readable && typeof data._read === 'function') { - response.status(statusCode || 200) - data.pipe(response); - } else if (data !== null && data !== undefined) { - response.status(statusCode || 200).json(data); - } else { - response.status(statusCode || 204).end(); - } - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function responder(response: any): TsoaResponse { - return function(status, data, headers) { - returnHandler(response, status, data, headers); - }; - }; - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function getValidatedArgs(args: any, request: any, response: any): any[] { - const fieldErrors: FieldErrors = {}; - const values = Object.keys(args).map((key) => { - const name = args[key].name; - switch (args[key].in) { - case 'request': - return request; - case 'query': - return validationService.ValidateParam(args[key], request.query[name], name, fieldErrors, undefined, {{{json minimalSwaggerConfig}}}); - case 'queries': - return validationService.ValidateParam(args[key], request.query, name, fieldErrors, undefined, {{{json minimalSwaggerConfig}}}); - case 'path': - return validationService.ValidateParam(args[key], request.params[name], name, fieldErrors, undefined, {{{json minimalSwaggerConfig}}}); - case 'header': - return validationService.ValidateParam(args[key], request.header(name), name, fieldErrors, undefined, {{{json minimalSwaggerConfig}}}); - case 'body': - return validationService.ValidateParam(args[key], request.body, name, fieldErrors, undefined, {{{json minimalSwaggerConfig}}}); - case 'body-prop': - return validationService.ValidateParam(args[key], request.body[name], name, fieldErrors, 'body.', {{{json minimalSwaggerConfig}}}); - case 'formData': - if (args[key].dataType === 'file') { - return validationService.ValidateParam(args[key], request.file, name, fieldErrors, undefined, {{{json minimalSwaggerConfig}}}); - } else if (args[key].dataType === 'array' && args[key].array.dataType === 'file') { - return validationService.ValidateParam(args[key], request.files, name, fieldErrors, undefined, {{{json minimalSwaggerConfig}}}); - } else { - return validationService.ValidateParam(args[key], request.body[name], name, fieldErrors, undefined, {{{json minimalSwaggerConfig}}}); - } - case 'res': - return responder(response); - } - }); - - if (Object.keys(fieldErrors).length > 0) { - throw new ValidateError(fieldErrors, ''); - } - return values; - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa } // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa diff --git a/packages/cli/src/routeGeneration/templates/express/expressTemplateService.ts b/packages/cli/src/routeGeneration/templates/express/expressTemplateService.ts new file mode 100644 index 000000000..ca08e34ee --- /dev/null +++ b/packages/cli/src/routeGeneration/templates/express/expressTemplateService.ts @@ -0,0 +1,95 @@ +import { Controller, FieldErrors, HttpStatusCodeLiteral, TsoaResponse, ValidateError, ValidationService } from "@tsoa/runtime"; +import { TemplateService } from '../templateService'; + +export class ExpressTemplateService implements TemplateService { + private readonly validationService: ValidationService; + + constructor( + readonly models: any, + private readonly minimalSwaggerConfig: any, + ) { + this.validationService = new ValidationService(models); + } + + isController(object: any): object is Controller { + return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; + } + + promiseHandler(controllerObj: any, promise: any, response: any, successStatus: any, next: any) { + return Promise.resolve(promise) + .then((data: any) => { + let statusCode = successStatus; + let headers; + if (this.isController(controllerObj)) { + headers = controllerObj.getHeaders(); + statusCode = controllerObj.getStatus() || statusCode; + } + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + this.returnHandler(response, headers, statusCode, data) + }) + .catch((error: any) => next(error)); + } + + returnHandler(response: any, headers: any = {}, statusCode?: number, data?: any) { + if (response.headersSent) { + return; + } + Object.keys(headers).forEach((name: string) => { + response.set(name, headers[name]); + }); + if (data && typeof data.pipe === 'function' && data.readable && typeof data._read === 'function') { + response.status(statusCode || 200) + data.pipe(response); + } else if (data !== null && data !== undefined) { + response.status(statusCode || 200).json(data); + } else { + response.status(statusCode || 204).end(); + } + } + + responder(response: any): TsoaResponse { + return (status, data, headers) => { + this.returnHandler(response, headers, status, data); + }; + } + + getValidatedArgs(args: any, request: any, response: any): any[] { + const fieldErrors: FieldErrors = {}; + const values = Object.keys(args).map((key) => { + const name = args[key].name; + switch (args[key].in) { + case 'request': + return request; + case 'query': + return this.validationService.ValidateParam(args[key], request.query[name], name, fieldErrors, undefined, this.minimalSwaggerConfig); + case 'queries': + return this.validationService.ValidateParam(args[key], request.query, name, fieldErrors, undefined, this.minimalSwaggerConfig); + case 'path': + return this.validationService.ValidateParam(args[key], request.params[name], name, fieldErrors, undefined, this.minimalSwaggerConfig); + case 'header': + return this.validationService.ValidateParam(args[key], request.header(name), name, fieldErrors, undefined, this.minimalSwaggerConfig); + case 'body': + return this.validationService.ValidateParam(args[key], request.body, name, fieldErrors, undefined, this.minimalSwaggerConfig); + case 'body-prop': + return this.validationService.ValidateParam(args[key], request.body[name], name, fieldErrors, 'body.', this.minimalSwaggerConfig); + case 'formData': + if (args[key].dataType === 'file') { + return this.validationService.ValidateParam(args[key], request.file, name, fieldErrors, undefined, this.minimalSwaggerConfig); + } else if (args[key].dataType === 'array' && args[key].array.dataType === 'file') { + return this.validationService.ValidateParam(args[key], request.files, name, fieldErrors, undefined, this.minimalSwaggerConfig); + } else { + return this.validationService.ValidateParam(args[key], request.body[name], name, fieldErrors, undefined, this.minimalSwaggerConfig); + } + case 'res': + return this.responder(response); + } + }); + + if (Object.keys(fieldErrors).length > 0) { + throw new ValidateError(fieldErrors, ''); + } + return values; + } +} diff --git a/packages/cli/src/routeGeneration/templates/hapi.hbs b/packages/cli/src/routeGeneration/templates/hapi/hapi.hbs similarity index 69% rename from packages/cli/src/routeGeneration/templates/hapi.hbs rename to packages/cli/src/routeGeneration/templates/hapi/hapi.hbs index 9d7c2e316..9427c49dc 100644 --- a/packages/cli/src/routeGeneration/templates/hapi.hbs +++ b/packages/cli/src/routeGeneration/templates/hapi/hapi.hbs @@ -1,7 +1,8 @@ /* tslint:disable */ /* eslint-disable */ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - import { Controller, ValidationService, FieldErrors, ValidateError, TsoaRoute, HttpStatusCodeLiteral, TsoaResponse, fetchMiddlewares } from '@tsoa/runtime'; +import { TsoaRoute, fetchMiddlewares } from '@tsoa/runtime'; +import { HapiTemplateService } from '@tsoa/cli'; {{#each controllers}} // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa import { {{name}} } from '{{modulePath}}'; @@ -43,7 +44,7 @@ const models: TsoaRoute.Models = { // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa {{/each}} }; -const validationService = new ValidationService(models); +const templateService = new HapiTemplateService(models, {{{ json minimalSwaggerConfig }}}); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa @@ -101,7 +102,7 @@ export function RegisterRoutes(server: any) { let validatedArgs: any[] = []; try { - validatedArgs = getValidatedArgs(args, request, h); + validatedArgs = templateService.getValidatedArgs(args, request, h); } catch (err) { const error = err as any; if (isBoom(error)) { @@ -130,7 +131,7 @@ export function RegisterRoutes(server: any) { {{/if}} const promise = controller.{{name}}.apply(controller, validatedArgs as any); - return promiseHandler(controller, promise, request, {{successStatus}}, h); + return templateService.promiseHandler(controller, promise, request, {{successStatus}}, h); } } }); @@ -210,12 +211,6 @@ export function RegisterRoutes(server: any) { } {{/if}} - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function isController(object: any): object is Controller { - return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; - } - {{#if useFileUploads}} function fileUploadMiddleware(fieldname: string, multiple: boolean = false) { return (request: Request, h: any) => { @@ -263,100 +258,6 @@ export function RegisterRoutes(server: any) { } {{/if}} - function promiseHandler(controllerObj: any, promise: any, request: any, successStatus: any, h: any) { - return Promise.resolve(promise) - .then((data: any) => { - let statusCode = successStatus; - let headers; - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - if (isController(controllerObj)) { - headers = controllerObj.getHeaders(); - statusCode = controllerObj.getStatus() || statusCode; - } - return returnHandler(h, statusCode, data, headers);; - }) - .catch((error: any) => { - if (isBoom(error)) { - throw error; - } - - const boomErr = boomify(error instanceof Error ? error : new Error(error.message)); - boomErr.output.statusCode = error.status || 500; - boomErr.output.payload = { - name: error.name, - message: error.message, - } as unknown as Payload; - throw boomErr; - }); - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function returnHandler(h: any, statusCode?: number, data?: any, headers: any = {}) { - if (h.__isTsoaResponded) { - return h.__isTsoaResponded; - } - - let response = data !== null && data !== undefined - ? h.response(data).code(200) - : h.response("").code(204); - - Object.keys(headers).forEach((name: string) => { - response.header(name, headers[name]); - }); - - if (statusCode) { - response.code(statusCode); - } - - h.__isTsoaResponded = response; - - return response; - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function getValidatedArgs(args: any, request: any, h: any): any[] { - const errorFields: FieldErrors = {}; - const values = Object.keys(args).map(key => { - const name = args[key].name; - switch (args[key].in) { - case 'request': - return request; - case 'query': - return validationService.ValidateParam(args[key], request.query[name], name, errorFields, undefined, {{{json minimalSwaggerConfig}}}) - case 'queries': - return validationService.ValidateParam(args[key], request.query, name, errorFields, undefined, {{{json minimalSwaggerConfig}}}) - case 'path': - return validationService.ValidateParam(args[key], request.params[name], name, errorFields, undefined, {{{json minimalSwaggerConfig}}}) - case 'header': - return validationService.ValidateParam(args[key], request.headers[name], name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - case 'body': - return validationService.ValidateParam(args[key], request.payload, name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - case 'body-prop': - return validationService.ValidateParam(args[key], request.payload[name], name, errorFields, 'body.', {{{json minimalSwaggerConfig}}}); - case 'formData': - return validationService.ValidateParam(args[key], request.payload[name], name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - case 'res': - return responder(h); - } - }); - if (Object.keys(errorFields).length > 0) { - throw new ValidateError(errorFields, ''); - } - return values; - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function responder(h: any): TsoaResponse { - return function(status, data, headers) { - returnHandler(h, status, data, headers); - }; - }; - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa } diff --git a/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts b/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts new file mode 100644 index 000000000..0e2e49972 --- /dev/null +++ b/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts @@ -0,0 +1,106 @@ +import { boomify, isBoom, type Payload } from '@hapi/boom'; +import { Controller, TsoaResponse, HttpStatusCodeLiteral, FieldErrors, ValidationService, ValidateError } from "@tsoa/runtime"; + +import { TemplateService } from "../templateService"; + +export class HapiTemplateService implements TemplateService { + private readonly validationService: ValidationService; + + constructor( + readonly models: any, + private readonly minimalSwaggerConfig: any, + ) { + this.validationService = new ValidationService(models); + } + + isController(object: any): object is Controller { + return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; + } + + promiseHandler(controllerObj: any, promise: any, request: any, successStatus: any, h: any) { + return Promise.resolve(promise) + .then((data: any) => { + let statusCode = successStatus; + let header; + + if (this.isController(controllerObj)) { + header = controllerObj.getHeaders(); + statusCode = controllerObj.getStatus() || statusCode; + } + return this.returnHandler(h, header, statusCode, data); + }) + .catch((error: any) => { + if (isBoom(error)) { + throw error; + } + + const boomErr = boomify(error instanceof Error ? error : new Error(error.message)); + boomErr.output.statusCode = error.status || 500; + boomErr.output.payload = { + name: error.name, + message: error.message, + } as unknown as Payload; + throw boomErr; + }); + } + + returnHandler(h: any, headers: any = {}, statusCode?: number | undefined, data?: any) { + if (h.__isTsoaResponded) { + return h.__isTsoaResponded; + } + + let response = data !== null && data !== undefined + ? h.response(data).code(200) + : h.response("").code(204); + + Object.keys(headers).forEach((name: string) => { + response.header(name, headers[name]); + }); + + if (statusCode) { + response.code(statusCode); + } + + h.__isTsoaResponded = response; + + return response; + } + + responder(h: any): TsoaResponse { + return (status, data, headers) => { + this.returnHandler(h, headers, status, data); + }; + } + + getValidatedArgs(args: any, request: any, h: any): any[] { + const errorFields: FieldErrors = {}; + const values = Object.keys(args).map(key => { + const name = args[key].name; + switch (args[key].in) { + case 'request': + return request; + case 'query': + return this.validationService.ValidateParam(args[key], request.query[name], name, errorFields, undefined, this.minimalSwaggerConfig) + case 'queries': + return this.validationService.ValidateParam(args[key], request.query, name, errorFields, undefined, this.minimalSwaggerConfig) + case 'path': + return this.validationService.ValidateParam(args[key], request.params[name], name, errorFields, undefined, this.minimalSwaggerConfig) + case 'header': + return this.validationService.ValidateParam(args[key], request.headers[name], name, errorFields, undefined, this.minimalSwaggerConfig); + case 'body': + return this.validationService.ValidateParam(args[key], request.payload, name, errorFields, undefined, this.minimalSwaggerConfig); + case 'body-prop': + return this.validationService.ValidateParam(args[key], request.payload[name], name, errorFields, 'body.', this.minimalSwaggerConfig); + case 'formData': + return this.validationService.ValidateParam(args[key], request.payload[name], name, errorFields, undefined, this.minimalSwaggerConfig); + case 'res': + return this.responder(h); + } + }); + if (Object.keys(errorFields).length > 0) { + throw new ValidateError(errorFields, ''); + } + return values; +} + +} diff --git a/packages/cli/src/routeGeneration/templates/koa.hbs b/packages/cli/src/routeGeneration/templates/koa/koa.hbs similarity index 57% rename from packages/cli/src/routeGeneration/templates/koa.hbs rename to packages/cli/src/routeGeneration/templates/koa/koa.hbs index 392fa256c..77537af30 100644 --- a/packages/cli/src/routeGeneration/templates/koa.hbs +++ b/packages/cli/src/routeGeneration/templates/koa/koa.hbs @@ -1,11 +1,8 @@ /* tslint:disable */ /* eslint-disable */ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa -{{#if canImportByAlias}} - import { Controller, ValidationService, FieldErrors, ValidateError, TsoaRoute, HttpStatusCodeLiteral, TsoaResponse, fetchMiddlewares } from '@tsoa/runtime'; -{{else}} - import { Controller, ValidationService, FieldErrors, ValidateError, TsoaRoute, TsoaResponse, HttpStatusCodeLiteral } from '../../../src'; -{{/if}} +import { TsoaRoute, fetchMiddlewares } from '@tsoa/runtime'; +import { KoaTemplateService } from '@tsoa/cli'; {{#each controllers}} // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa import { {{name}} } from '{{modulePath}}'; @@ -55,7 +52,7 @@ const models: TsoaRoute.Models = { // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa {{/each}} }; -const validationService = new ValidationService(models); +const templateService = new KoaTemplateService(models, {{{ json minimalSwaggerConfig }}}); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa @@ -88,7 +85,7 @@ export function RegisterRoutes(router: KoaRouter) { let validatedArgs: any[] = []; try { - validatedArgs = getValidatedArgs(args, context, next); + validatedArgs = templateService.getValidatedArgs(args, context, next); } catch (err) { const error = err as any; error.message ||= JSON.stringify({ fields: error.fields }); @@ -108,7 +105,7 @@ export function RegisterRoutes(router: KoaRouter) { {{/if}} const promise = controller.{{name}}.apply(controller, validatedArgs as any); - return promiseHandler(controller, promise, context, {{successStatus}}, undefined); + return templateService.promiseHandler(controller, promise, context, {{successStatus}}, undefined); }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa {{/each}} @@ -180,102 +177,6 @@ export function RegisterRoutes(router: KoaRouter) { } {{/if}} - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function isController(object: any): object is Controller { - return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function promiseHandler(controllerObj: any, promise: Promise, context: any, successStatus: any, next?: () => Promise) { - return Promise.resolve(promise) - .then((data: any) => { - let statusCode = successStatus; - let headers; - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - if (isController(controllerObj)) { - headers = controllerObj.getHeaders(); - statusCode = controllerObj.getStatus() || statusCode; - } - return returnHandler(context, next, statusCode, data, headers); - }) - .catch((error: any) => { - context.status = error.status || 500; - context.throw(context.status, error.message, error); - }); - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function returnHandler(context: any, next?: () => any, statusCode?: number, data?: any, headers: any={}) { - if (!context.headerSent && !context.response.__tsoaResponded) { - if (data !== null && data !== undefined) { - context.body = data; - context.status = 200; - } else { - context.status = 204; - } - - if (statusCode) { - context.status = statusCode; - } - - context.set(headers); - context.response.__tsoaResponded = true; - return next ? next() : context; - } - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function getValidatedArgs(args: any, context: any, next: () => any): any[] { - const errorFields: FieldErrors = {}; - const values = Object.keys(args).map(key => { - const name = args[key].name; - switch (args[key].in) { - case 'request': - return context.request; - case 'query': - return validationService.ValidateParam(args[key], context.request.query[name], name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - case 'queries': - return validationService.ValidateParam(args[key], context.request.query, name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - case 'path': - return validationService.ValidateParam(args[key], context.params[name], name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - case 'header': - return validationService.ValidateParam(args[key], context.request.headers[name], name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - case 'body': - return validationService.ValidateParam(args[key], context.request.body, name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - case 'body-prop': - return validationService.ValidateParam(args[key], context.request.body[name], name, errorFields, 'body.', {{{json minimalSwaggerConfig}}}); - case 'formData': - if (args[key].dataType === 'file') { - return validationService.ValidateParam(args[key], context.request.file, name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - } else if (args[key].dataType === 'array' && args[key].array.dataType === 'file') { - return validationService.ValidateParam(args[key], context.request.files, name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - } else { - return validationService.ValidateParam(args[key], context.request.body[name], name, errorFields, undefined, {{{json minimalSwaggerConfig}}}); - } - case 'res': - return responder(context, next); - } - }); - if (Object.keys(errorFields).length > 0) { - throw new ValidateError(errorFields, ''); - } - return values; - } - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - function responder(context: any, next: () => any): TsoaResponse { - return function(status, data, headers) { - returnHandler(context, next, status, data, headers); - }; - }; - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa } diff --git a/packages/cli/src/routeGeneration/templates/koa/koaTemplateService.ts b/packages/cli/src/routeGeneration/templates/koa/koaTemplateService.ts new file mode 100644 index 000000000..959d760d7 --- /dev/null +++ b/packages/cli/src/routeGeneration/templates/koa/koaTemplateService.ts @@ -0,0 +1,99 @@ +import { Controller, TsoaResponse, HttpStatusCodeLiteral, FieldErrors, ValidationService, ValidateError } from "@tsoa/runtime"; +import { TemplateService } from "../templateService"; + +export class KoaTemplateService implements TemplateService { + private readonly validationService: ValidationService; + + constructor( + readonly models: any, + private readonly minimalSwaggerConfig: any, + ) { + this.validationService = new ValidationService(models); + } + + isController(object: any): object is Controller { + return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; + } + + promiseHandler(controllerObj: any, promise: any, context: any, successStatus: any, next: any) { + return Promise.resolve(promise) + .then((data: any) => { + let statusCode = successStatus; + let headers; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + if (this.isController(controllerObj)) { + headers = controllerObj.getHeaders(); + statusCode = controllerObj.getStatus() || statusCode; + } + return this.returnHandler(context, headers, statusCode, data, next); + }) + .catch((error: any) => { + context.status = error.status || 500; + context.throw(context.status, error.message, error); + }); + } + + returnHandler(context: any, headers: any, statusCode?: number | undefined, data?: any, next?: any) { + if (!context.headerSent && !context.response.__tsoaResponded) { + if (data !== null && data !== undefined) { + context.body = data; + context.status = 200; + } else { + context.status = 204; + } + + if (statusCode) { + context.status = statusCode; + } + + context.set(headers); + context.response.__tsoaResponded = true; + return next ? next() : context; + } + } + + responder(context: any, next?: any): TsoaResponse { + return (status, data, headers) => { + this.returnHandler(context, headers, status, data, next); + }; + } + + getValidatedArgs(args: any, context: any, next: () => any): any[] { + const errorFields: FieldErrors = {}; + const values = Object.keys(args).map(key => { + const name = args[key].name; + switch (args[key].in) { + case 'request': + return context.request; + case 'query': + return this.validationService.ValidateParam(args[key], context.request.query[name], name, errorFields, undefined, this.minimalSwaggerConfig); + case 'queries': + return this.validationService.ValidateParam(args[key], context.request.query, name, errorFields, undefined, this.minimalSwaggerConfig); + case 'path': + return this.validationService.ValidateParam(args[key], context.params[name], name, errorFields, undefined, this.minimalSwaggerConfig); + case 'header': + return this.validationService.ValidateParam(args[key], context.request.headers[name], name, errorFields, undefined, this.minimalSwaggerConfig); + case 'body': + return this.validationService.ValidateParam(args[key], context.request.body, name, errorFields, undefined, this.minimalSwaggerConfig); + case 'body-prop': + return this.validationService.ValidateParam(args[key], context.request.body[name], name, errorFields, 'body.', this.minimalSwaggerConfig); + case 'formData': + if (args[key].dataType === 'file') { + return this.validationService.ValidateParam(args[key], context.request.file, name, errorFields, undefined, this.minimalSwaggerConfig); + } else if (args[key].dataType === 'array' && args[key].array.dataType === 'file') { + return this.validationService.ValidateParam(args[key], context.request.files, name, errorFields, undefined, this.minimalSwaggerConfig); + } else { + return this.validationService.ValidateParam(args[key], context.request.body[name], name, errorFields, undefined, this.minimalSwaggerConfig); + } + case 'res': + return this.responder(context, next); + } + }); + if (Object.keys(errorFields).length > 0) { + throw new ValidateError(errorFields, ''); + } + return values; +} +} diff --git a/packages/cli/src/routeGeneration/templates/templateService.ts b/packages/cli/src/routeGeneration/templates/templateService.ts new file mode 100644 index 000000000..5a72d2b3b --- /dev/null +++ b/packages/cli/src/routeGeneration/templates/templateService.ts @@ -0,0 +1,13 @@ +import { Controller, HttpStatusCodeLiteral, TsoaResponse } from "@tsoa/runtime"; + +export interface TemplateService { + isController(object: any): object is Controller; + + promiseHandler(controllerObj: any, promise: any, response: any, successStatus: any, next: any): any; + + returnHandler(response: any, header: any, statusCode?: number, data?: any, next?: any): any; + + responder(response: any, next?: any): TsoaResponse; + + getValidatedArgs(args: any, request: any, response: any): any[]; +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 5eaf93467..cfc7b8e47 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -20,7 +20,7 @@ "esModuleInterop": true, "strict": true }, - "exclude": ["src/routeGeneration/templates/**/*"], + "exclude": ["src/routeGeneration/templates/**/*.hbs"], "include": ["src/**/*.ts"], "references": [{ "path": "../runtime" }] } From 523593e3948b263658ab74c620af2d507bc3080c Mon Sep 17 00:00:00 2001 From: Clooooode Date: Thu, 18 Jan 2024 21:59:11 +0800 Subject: [PATCH 2/4] chore: lint --- .../src/routeGeneration/templates/hapi/hapiTemplateService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts b/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts index 0e2e49972..9a4714101 100644 --- a/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts +++ b/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts @@ -49,7 +49,7 @@ export class HapiTemplateService implements TemplateService { return h.__isTsoaResponded; } - let response = data !== null && data !== undefined + const response = data !== null && data !== undefined ? h.response(data).code(200) : h.response("").code(204); From 0e44b99bb225cbc4b93c5dd21ebf89af9cd1e0fb Mon Sep 17 00:00:00 2001 From: Clooooode Date: Thu, 18 Jan 2024 22:46:01 +0800 Subject: [PATCH 3/4] fix: package script cause incorrect hbs copy to dist --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index df89b1743..859760717 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -22,7 +22,7 @@ ], "scripts": { "build": "yarn clean && yarn tsc && yarn copy-templates", - "copy-templates": "copyfiles -u 1 ./src/routeGeneration/templates/**/* ./dist", + "copy-templates": "copyfiles -u 1 ./src/routeGeneration/templates/**/*.hbs ./dist", "clean": "rimraf dist tsconfig.tsbuildinfo", "tsc": "tsc -b", "watch": "tsc -b -w" From aaa28065f868614dd3a96fc225fb6546af1ffab1 Mon Sep 17 00:00:00 2001 From: Clooooode Date: Thu, 18 Jan 2024 23:58:23 +0800 Subject: [PATCH 4/4] feat: generic type @ TemplateService interface --- .../templates/express/express.hbs | 4 +- .../express/expressTemplateService.ts | 18 ++++---- .../routeGeneration/templates/hapi/hapi.hbs | 4 +- .../templates/hapi/hapiTemplateService.ts | 25 +++++------ .../src/routeGeneration/templates/koa/koa.hbs | 6 +-- .../templates/koa/koaTemplateService.ts | 35 +++++++--------- .../templates/templateService.ts | 14 ++++--- tests/package.json | 1 + yarn.lock | 42 ++++++++++++++++++- 9 files changed, 91 insertions(+), 58 deletions(-) diff --git a/packages/cli/src/routeGeneration/templates/express/express.hbs b/packages/cli/src/routeGeneration/templates/express/express.hbs index ab866976f..52e93c724 100644 --- a/packages/cli/src/routeGeneration/templates/express/express.hbs +++ b/packages/cli/src/routeGeneration/templates/express/express.hbs @@ -15,7 +15,7 @@ import { expressAuthentication } from '{{authenticationModule}}'; import { iocContainer } from '{{iocModule}}'; import type { IocContainer, IocContainerFactory } from '@tsoa/runtime'; {{/if}} -import type { RequestHandler, Router } from 'express'; +import type { Request as ExRequest, Response as ExResponse, RequestHandler, Router } from 'express'; {{#if useFileUploads}} {{#if esm}} import multer from 'multer'; @@ -75,7 +75,7 @@ export function RegisterRoutes(app: Router) { ...(fetchMiddlewares({{../name}})), ...(fetchMiddlewares({{../name}}.prototype.{{name}})), - {{#if ../../iocModule}}async {{/if}}function {{../name}}_{{name}}(request: any, response: any, next: any) { + {{#if ../../iocModule}}async {{/if}}function {{../name}}_{{name}}(request: ExRequest, response: ExResponse, next: any) { const args = { {{#each parameters}} {{@key}}: {{{json this}}}, diff --git a/packages/cli/src/routeGeneration/templates/express/expressTemplateService.ts b/packages/cli/src/routeGeneration/templates/express/expressTemplateService.ts index ca08e34ee..bb4ee352f 100644 --- a/packages/cli/src/routeGeneration/templates/express/expressTemplateService.ts +++ b/packages/cli/src/routeGeneration/templates/express/expressTemplateService.ts @@ -1,7 +1,9 @@ -import { Controller, FieldErrors, HttpStatusCodeLiteral, TsoaResponse, ValidateError, ValidationService } from "@tsoa/runtime"; -import { TemplateService } from '../templateService'; +import { Request as ExRequest, Response as ExResponse } from 'express'; +import { FieldErrors, HttpStatusCodeLiteral, TsoaResponse, ValidateError, ValidationService } from "@tsoa/runtime"; -export class ExpressTemplateService implements TemplateService { +import { TemplateService, isController } from '../templateService'; + +export class ExpressTemplateService implements TemplateService { private readonly validationService: ValidationService; constructor( @@ -11,16 +13,12 @@ export class ExpressTemplateService implements TemplateService { this.validationService = new ValidationService(models); } - isController(object: any): object is Controller { - return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; - } - - promiseHandler(controllerObj: any, promise: any, response: any, successStatus: any, next: any) { + promiseHandler(controllerObj: any, promise: any, response: ExResponse, successStatus: any, next: any) { return Promise.resolve(promise) .then((data: any) => { let statusCode = successStatus; let headers; - if (this.isController(controllerObj)) { + if (isController(controllerObj)) { headers = controllerObj.getHeaders(); statusCode = controllerObj.getStatus() || statusCode; } @@ -55,7 +53,7 @@ export class ExpressTemplateService implements TemplateService { }; } - getValidatedArgs(args: any, request: any, response: any): any[] { + getValidatedArgs(args: any, request: ExRequest, response: ExResponse): any[] { const fieldErrors: FieldErrors = {}; const values = Object.keys(args).map((key) => { const name = args[key].name; diff --git a/packages/cli/src/routeGeneration/templates/hapi/hapi.hbs b/packages/cli/src/routeGeneration/templates/hapi/hapi.hbs index 9427c49dc..097b16004 100644 --- a/packages/cli/src/routeGeneration/templates/hapi/hapi.hbs +++ b/packages/cli/src/routeGeneration/templates/hapi/hapi.hbs @@ -16,7 +16,7 @@ import { iocContainer } from '{{iocModule}}'; import type { IocContainer, IocContainerFactory } from '@tsoa/runtime'; {{/if}} import { boomify, isBoom, type Payload } from '@hapi/boom'; -import type { Request, RouteOptionsPreAllOptions } from '@hapi/hapi'; +import type { Request, ResponseToolkit, RouteOptionsPreAllOptions } from '@hapi/hapi'; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa @@ -93,7 +93,7 @@ export function RegisterRoutes(server: any) { allow: 'multipart/form-data' }, {{/if}} - handler: {{#if ../../iocModule}}async {{/if}}function {{../name}}_{{name}}(request: any, h: any) { + handler: {{#if ../../iocModule}}async {{/if}}function {{../name}}_{{name}}(request: Request, h: ResponseToolkit) { const args = { {{#each parameters}} {{@key}}: {{{json this}}}, diff --git a/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts b/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts index 9a4714101..f458f20d6 100644 --- a/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts +++ b/packages/cli/src/routeGeneration/templates/hapi/hapiTemplateService.ts @@ -1,9 +1,10 @@ +import { ResponseToolkit as HReponse } from '@hapi/hapi'; import { boomify, isBoom, type Payload } from '@hapi/boom'; -import { Controller, TsoaResponse, HttpStatusCodeLiteral, FieldErrors, ValidationService, ValidateError } from "@tsoa/runtime"; +import { TsoaResponse, HttpStatusCodeLiteral, FieldErrors, ValidationService, ValidateError } from "@tsoa/runtime"; -import { TemplateService } from "../templateService"; +import { isController, TemplateService } from "../templateService"; -export class HapiTemplateService implements TemplateService { +export class HapiTemplateService implements TemplateService { private readonly validationService: ValidationService; constructor( @@ -13,17 +14,13 @@ export class HapiTemplateService implements TemplateService { this.validationService = new ValidationService(models); } - isController(object: any): object is Controller { - return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; - } - promiseHandler(controllerObj: any, promise: any, request: any, successStatus: any, h: any) { return Promise.resolve(promise) .then((data: any) => { let statusCode = successStatus; let header; - if (this.isController(controllerObj)) { + if (isController(controllerObj)) { header = controllerObj.getHeaders(); statusCode = controllerObj.getStatus() || statusCode; } @@ -44,9 +41,9 @@ export class HapiTemplateService implements TemplateService { }); } - returnHandler(h: any, headers: any = {}, statusCode?: number | undefined, data?: any) { - if (h.__isTsoaResponded) { - return h.__isTsoaResponded; + returnHandler(h: HReponse, headers: any = {}, statusCode?: number | undefined, data?: any) { + if ((h as any).__isTsoaResponded) { + return (h as any).__isTsoaResponded; } const response = data !== null && data !== undefined @@ -61,18 +58,18 @@ export class HapiTemplateService implements TemplateService { response.code(statusCode); } - h.__isTsoaResponded = response; + (h as any).__isTsoaResponded = response; return response; } - responder(h: any): TsoaResponse { + responder(h: HReponse): TsoaResponse { return (status, data, headers) => { this.returnHandler(h, headers, status, data); }; } - getValidatedArgs(args: any, request: any, h: any): any[] { + getValidatedArgs(args: any, request: any, h: HReponse): any[] { const errorFields: FieldErrors = {}; const values = Object.keys(args).map(key => { const name = args[key].name; diff --git a/packages/cli/src/routeGeneration/templates/koa/koa.hbs b/packages/cli/src/routeGeneration/templates/koa/koa.hbs index 77537af30..0d7ba7502 100644 --- a/packages/cli/src/routeGeneration/templates/koa/koa.hbs +++ b/packages/cli/src/routeGeneration/templates/koa/koa.hbs @@ -15,7 +15,7 @@ import { koaAuthentication } from '{{authenticationModule}}'; import { iocContainer } from '{{iocModule}}'; import type { IocContainer, IocContainerFactory } from '@tsoa/runtime'; {{/if}} -import type { Middleware } from 'koa'; +import type { Context, Next, Middleware } from 'koa'; import type * as KoaRouter from '@koa/router'; {{#if useFileUploads}} {{#if esm}} @@ -76,7 +76,7 @@ export function RegisterRoutes(router: KoaRouter) { ...(fetchMiddlewares({{../name}})), ...(fetchMiddlewares({{../name}}.prototype.{{name}})), - async function {{../name}}_{{name}}(context: any, next: any) { + async function {{../name}}_{{name}}(context: Context, next: Next) { const args = { {{#each parameters}} {{@key}}: {{{json this}}}, @@ -85,7 +85,7 @@ export function RegisterRoutes(router: KoaRouter) { let validatedArgs: any[] = []; try { - validatedArgs = templateService.getValidatedArgs(args, context, next); + validatedArgs = templateService.getValidatedArgs(args, undefined, context, next); } catch (err) { const error = err as any; error.message ||= JSON.stringify({ fields: error.fields }); diff --git a/packages/cli/src/routeGeneration/templates/koa/koaTemplateService.ts b/packages/cli/src/routeGeneration/templates/koa/koaTemplateService.ts index 959d760d7..84bf14b98 100644 --- a/packages/cli/src/routeGeneration/templates/koa/koaTemplateService.ts +++ b/packages/cli/src/routeGeneration/templates/koa/koaTemplateService.ts @@ -1,7 +1,8 @@ -import { Controller, TsoaResponse, HttpStatusCodeLiteral, FieldErrors, ValidationService, ValidateError } from "@tsoa/runtime"; -import { TemplateService } from "../templateService"; +import type { Context } from 'koa'; +import { TsoaResponse, HttpStatusCodeLiteral, FieldErrors, ValidationService, ValidateError } from "@tsoa/runtime"; +import { TemplateService, isController } from "../templateService"; -export class KoaTemplateService implements TemplateService { +export class KoaTemplateService implements TemplateService { private readonly validationService: ValidationService; constructor( @@ -11,19 +12,13 @@ export class KoaTemplateService implements TemplateService { this.validationService = new ValidationService(models); } - isController(object: any): object is Controller { - return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; - } - - promiseHandler(controllerObj: any, promise: any, context: any, successStatus: any, next: any) { + promiseHandler(controllerObj: any, promise: any, context: Context, successStatus: any, next: any) { return Promise.resolve(promise) .then((data: any) => { let statusCode = successStatus; let headers; - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - if (this.isController(controllerObj)) { + if (isController(controllerObj)) { headers = controllerObj.getHeaders(); statusCode = controllerObj.getStatus() || statusCode; } @@ -35,8 +30,8 @@ export class KoaTemplateService implements TemplateService { }); } - returnHandler(context: any, headers: any, statusCode?: number | undefined, data?: any, next?: any) { - if (!context.headerSent && !context.response.__tsoaResponded) { + returnHandler(context: Context, headers: any, statusCode?: number | undefined, data?: any, next?: any) { + if (!context.headerSent && !(context.response as any).__tsoaResponded) { if (data !== null && data !== undefined) { context.body = data; context.status = 200; @@ -49,7 +44,7 @@ export class KoaTemplateService implements TemplateService { } context.set(headers); - context.response.__tsoaResponded = true; + (context.response as any).__tsoaResponded = true; return next ? next() : context; } } @@ -60,7 +55,7 @@ export class KoaTemplateService implements TemplateService { }; } - getValidatedArgs(args: any, context: any, next: () => any): any[] { + getValidatedArgs(args: any, request: any, context: Context, next: () => any): any[] { const errorFields: FieldErrors = {}; const values = Object.keys(args).map(key => { const name = args[key].name; @@ -76,16 +71,16 @@ export class KoaTemplateService implements TemplateService { case 'header': return this.validationService.ValidateParam(args[key], context.request.headers[name], name, errorFields, undefined, this.minimalSwaggerConfig); case 'body': - return this.validationService.ValidateParam(args[key], context.request.body, name, errorFields, undefined, this.minimalSwaggerConfig); + return this.validationService.ValidateParam(args[key], (context.request as any).body, name, errorFields, undefined, this.minimalSwaggerConfig); case 'body-prop': - return this.validationService.ValidateParam(args[key], context.request.body[name], name, errorFields, 'body.', this.minimalSwaggerConfig); + return this.validationService.ValidateParam(args[key], (context.request as any).body[name], name, errorFields, 'body.', this.minimalSwaggerConfig); case 'formData': if (args[key].dataType === 'file') { - return this.validationService.ValidateParam(args[key], context.request.file, name, errorFields, undefined, this.minimalSwaggerConfig); + return this.validationService.ValidateParam(args[key], (context.request as any).file, name, errorFields, undefined, this.minimalSwaggerConfig); } else if (args[key].dataType === 'array' && args[key].array.dataType === 'file') { - return this.validationService.ValidateParam(args[key], context.request.files, name, errorFields, undefined, this.minimalSwaggerConfig); + return this.validationService.ValidateParam(args[key], (context.request as any).files, name, errorFields, undefined, this.minimalSwaggerConfig); } else { - return this.validationService.ValidateParam(args[key], context.request.body[name], name, errorFields, undefined, this.minimalSwaggerConfig); + return this.validationService.ValidateParam(args[key], (context.request as any).body[name], name, errorFields, undefined, this.minimalSwaggerConfig); } case 'res': return this.responder(context, next); diff --git a/packages/cli/src/routeGeneration/templates/templateService.ts b/packages/cli/src/routeGeneration/templates/templateService.ts index 5a72d2b3b..d774aa1c0 100644 --- a/packages/cli/src/routeGeneration/templates/templateService.ts +++ b/packages/cli/src/routeGeneration/templates/templateService.ts @@ -1,13 +1,15 @@ import { Controller, HttpStatusCodeLiteral, TsoaResponse } from "@tsoa/runtime"; -export interface TemplateService { - isController(object: any): object is Controller; +export interface TemplateService { + promiseHandler(controllerObj: any, promise: any, response: Response, successStatus: any, next: any): any; - promiseHandler(controllerObj: any, promise: any, response: any, successStatus: any, next: any): any; + returnHandler(response: Response, header: any, statusCode?: number, data?: any, next?: any): any; - returnHandler(response: any, header: any, statusCode?: number, data?: any, next?: any): any; + responder(response: Response, next?: any): TsoaResponse; - responder(response: any, next?: any): TsoaResponse; + getValidatedArgs(args: any, request: Request, response: Response, next?: any): any[]; +} - getValidatedArgs(args: any, request: any, response: any): any[]; +export function isController(object: any): object is Controller { + return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object; } diff --git a/tests/package.json b/tests/package.json index 826777691..6b7745ffb 100644 --- a/tests/package.json +++ b/tests/package.json @@ -56,6 +56,7 @@ "husky": "^8.0.3", "inversify": "^6.0.2", "inversify-binding-decorators": "^4.0.0", + "joi": "^17.12.0", "koa": "^2.14.1", "koa-bodyparser": "^4.4.0", "lerna": "^8.0.1", diff --git a/yarn.lock b/yarn.lock index 9563881f2..9ebfe3acd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -407,6 +407,11 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-11.0.4.tgz#42a7f244fd3dd777792bfb74b8c6340ae9182f37" integrity sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ== +"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": + version "9.3.0" + resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + "@hapi/iron@^7.0.1": version "7.0.1" resolved "https://registry.yarnpkg.com/@hapi/iron/-/iron-7.0.1.tgz#f74bace8dad9340c7c012c27c078504f070f14b5" @@ -501,6 +506,13 @@ resolved "https://registry.yarnpkg.com/@hapi/teamwork/-/teamwork-6.0.0.tgz#b3a173cf811ba59fc6ee22318a1b51f4561f06e0" integrity sha512-05HumSy3LWfXpmJ9cr6HzwhAavrHkJ1ZRCmNE2qJMihdM5YcWreWPfyN0yKT2ZjCM92au3ZkuodjBxOibxM67A== +"@hapi/topo@^5.1.0": + version "5.1.0" + resolved "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo@^6.0.1": version "6.0.2" resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-6.0.2.tgz#f219c1c60da8430228af4c1f2e40c32a0d84bbb4" @@ -986,6 +998,23 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@sideway/address@^4.1.4": + version "4.1.4" + resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sigstore/bundle@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1" @@ -4054,6 +4083,17 @@ jest-get-type@^29.6.3: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== +joi@^17.12.0: + version "17.12.0" + resolved "https://registry.npmjs.org/joi/-/joi-17.12.0.tgz#a3fb5715f198beb0471cd551dd26792089c308d5" + integrity sha512-HSLsmSmXz+PV9PYoi3p7cgIbj06WnEBNT28n+bbBNcPZXZFqCzzvGqpTBPujx/Z0nh1+KNQPDrNgdmQ8dq0qYw== + dependencies: + "@hapi/hoek" "^9.3.0" + "@hapi/topo" "^5.1.0" + "@sideway/address" "^4.1.4" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6686,7 +6726,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -"typescript@>=3 < 6", typescript@^5.2.2, typescript@^5.3.3: +"typescript@>=3 < 6", typescript@^5.3.3: version "5.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==