Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/hbs template logics extract to classes with implement interface #1545

Merged
merged 4 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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';
jackey8616 marked this conversation as resolved.
Show resolved Hide resolved
{{#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
Loading