Skip to content

Commit

Permalink
Merge pull request #1545 from jackey8616/refactor/hbs-template-interface
Browse files Browse the repository at this point in the history
Refactor/hbs template logics extract to classes with implement interface
  • Loading branch information
WoH authored Jan 23, 2024
2 parents 4ebc2ad + aaa2806 commit 579dc5e
Show file tree
Hide file tree
Showing 13 changed files with 377 additions and 315 deletions.
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/routeGeneration/defaultRouteGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ export class DefaultRouteGenerator extends AbstractRouteGenerator<ExtendedRoutes

switch (options.middleware) {
case 'express':
this.template = path.join(__dirname, '..', 'routeGeneration/templates/express.hbs');
this.template = path.join(__dirname, '..', 'routeGeneration/templates/express/express.hbs');
break;
case 'hapi':
this.template = path.join(__dirname, '..', 'routeGeneration/templates/hapi.hbs');
this.template = path.join(__dirname, '..', 'routeGeneration/templates/hapi/hapi.hbs');
this.pathTransformerFn = (path: string) => 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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}}';
Expand All @@ -14,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';
Expand Down Expand Up @@ -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

Expand All @@ -74,7 +75,7 @@ export function RegisterRoutes(app: Router) {
...(fetchMiddlewares<RequestHandler>({{../name}})),
...(fetchMiddlewares<RequestHandler>({{../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}}},
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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<HttpStatusCodeLiteral, unknown> {
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Request as ExRequest, Response as ExResponse } from 'express';
import { FieldErrors, HttpStatusCodeLiteral, TsoaResponse, ValidateError, ValidationService } from "@tsoa/runtime";

import { TemplateService, isController } from '../templateService';

export class ExpressTemplateService implements TemplateService<ExRequest, ExResponse> {
private readonly validationService: ValidationService;

constructor(
readonly models: any,
private readonly minimalSwaggerConfig: any,
) {
this.validationService = new ValidationService(models);
}

promiseHandler(controllerObj: any, promise: any, response: ExResponse, 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

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<HttpStatusCodeLiteral, unknown> {
return (status, data, headers) => {
this.returnHandler(response, headers, status, data);
};
}

getValidatedArgs(args: any, request: ExRequest, response: ExResponse): 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;
}
}
Loading

0 comments on commit 579dc5e

Please sign in to comment.