From 3576fc762b6f5ab5f9b5bf9defcc118f37b12b4a Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Fri, 19 Nov 2021 16:24:19 +0100 Subject: [PATCH 1/2] chore: Move platform init --- .../src/builder/PlatformBuilder.spec.ts | 2 +- .../common/src/builder/PlatformBuilder.ts | 20 +++++++++++++------ .../src/components/PlatformExpress.spec.ts | 2 +- .../src/components/PlatformExpress.ts | 2 +- .../src/components/PlatformKoa.spec.ts | 2 +- .../src/components/PlatformKoa.ts | 2 +- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/platform/common/src/builder/PlatformBuilder.spec.ts b/packages/platform/common/src/builder/PlatformBuilder.spec.ts index c73a8243aed..f6e4bc2aada 100644 --- a/packages/platform/common/src/builder/PlatformBuilder.spec.ts +++ b/packages/platform/common/src/builder/PlatformBuilder.spec.ts @@ -33,7 +33,7 @@ describe("PlatformBuilder", () => { ]; static async bootstrap(module: Type, settings: Partial = {}) { - return PlatformBuilder.build(this).bootstrap(module, settings); + return PlatformBuilder.build(this, module, settings).bootstrap(module, settings); } async loadStatics(): Promise { diff --git a/packages/platform/common/src/builder/PlatformBuilder.ts b/packages/platform/common/src/builder/PlatformBuilder.ts index 9a3af73d469..fbbc701de15 100644 --- a/packages/platform/common/src/builder/PlatformBuilder.ts +++ b/packages/platform/common/src/builder/PlatformBuilder.ts @@ -1,5 +1,5 @@ -import {deepMerge, Env, nameOf, Store, toMap, Type} from "@tsed/core"; -import {colors, Container, createContainer, InjectorService, IProvider, ProviderScope, ProviderType, setLoggerLevel} from "@tsed/di"; +import {nameOf, toMap, Type} from "@tsed/core"; +import {colors, Container, createContainer, InjectorService, IProvider, ProviderScope, setLoggerLevel} from "@tsed/di"; import {importProviders} from "@tsed/components-scan"; import {getMiddlewaresForHook} from "@tsed/platform-middlewares"; import {GlobalAcceptMimesMiddleware} from "../middlewares"; @@ -51,12 +51,15 @@ export abstract class PlatformBuilder; #rootModule: Type; - constructor({name, providers}: {name: string; providers: IProvider[]}) { + constructor({name, providers, settings, module}: {name: string; providers: IProvider[]; settings: any; module: Type}) { this.name = name; this.#providers = toMap(providers, "provide"); this.locals = new Container(); + this.#rootModule = module; + const configuration = getConfiguration(settings, module); + this.useProvider(PlatformHandler, this.#providers.get(PlatformHandler)) .useProvider(PlatformResponse, this.#providers.get(PlatformResponse)) .useProvider(PlatformRequest, this.#providers.get(PlatformRequest)) @@ -114,9 +117,15 @@ export abstract class PlatformBuilder>(platformBuildClass: PlatformType): T { + static build>( + platformBuildClass: PlatformType, + module: Type, + settings: Partial = {} + ): T { return new platformBuildClass({ name: nameOf(platformBuildClass).replace("Platform", "").toLowerCase(), + module, + settings, providers: platformBuildClass.providers }); } @@ -334,13 +343,12 @@ export abstract class PlatformBuilder, settings: any) { + // configure locals providers this.#rootModule = module; - const configuration = getConfiguration(settings, module); this.#injector = createInjector(configuration); - // configure locals providers this.locals.forEach((provider) => { this.injector.addProvider(provider.token, provider); }); diff --git a/packages/platform/platform-express/src/components/PlatformExpress.spec.ts b/packages/platform/platform-express/src/components/PlatformExpress.spec.ts index 6a648fcd401..00cde27bf28 100644 --- a/packages/platform/platform-express/src/components/PlatformExpress.spec.ts +++ b/packages/platform/platform-express/src/components/PlatformExpress.spec.ts @@ -30,7 +30,7 @@ describe("PlatformExpress", () => { await PlatformExpress.bootstrap(Test, {}); await PlatformExpress.bootstrap(Test); - expect(PlatformExpress.build).to.have.been.calledWithExactly(PlatformExpress); + expect(PlatformExpress.build).to.have.been.calledWithExactly(PlatformExpress, Test, {}); expect(platform.bootstrap).to.have.been.calledWithExactly(Test, {}); expect(PlatformExpress.providers).to.deep.equal([ { diff --git a/packages/platform/platform-express/src/components/PlatformExpress.ts b/packages/platform/platform-express/src/components/PlatformExpress.ts index 71517a96e18..b7964351d29 100644 --- a/packages/platform/platform-express/src/components/PlatformExpress.ts +++ b/packages/platform/platform-express/src/components/PlatformExpress.ts @@ -49,7 +49,7 @@ export class PlatformExpress extends PlatformBuilder, settings: Partial = {}): Promise { - return this.build(PlatformExpress).bootstrap(module, settings); + return this.build(PlatformExpress, module, settings).bootstrap(module, settings); } protected useRouter(): this { diff --git a/packages/platform/platform-koa/src/components/PlatformKoa.spec.ts b/packages/platform/platform-koa/src/components/PlatformKoa.spec.ts index 50f3a9f821c..c37037f1bf3 100644 --- a/packages/platform/platform-koa/src/components/PlatformKoa.spec.ts +++ b/packages/platform/platform-koa/src/components/PlatformKoa.spec.ts @@ -24,7 +24,7 @@ describe("PlatformKoa", () => { await PlatformKoa.bootstrap(Test, {}); await PlatformKoa.bootstrap(Test); - expect(PlatformKoa.build).to.have.been.calledWithExactly(PlatformKoa); + expect(PlatformKoa.build).to.have.been.calledWithExactly(PlatformKoa, Test, {}); expect(platform.bootstrap).to.have.been.calledWithExactly(Test, {}); expect(PlatformKoa.providers).to.deep.equal([ { diff --git a/packages/platform/platform-koa/src/components/PlatformKoa.ts b/packages/platform/platform-koa/src/components/PlatformKoa.ts index 6fae6ec29cc..265b13e0952 100644 --- a/packages/platform/platform-koa/src/components/PlatformKoa.ts +++ b/packages/platform/platform-koa/src/components/PlatformKoa.ts @@ -44,7 +44,7 @@ export class PlatformKoa extends PlatformBuilder { ]; static async bootstrap(module: Type, settings: Partial = {}): Promise { - return this.build(PlatformKoa).bootstrap(module, settings); + return this.build(PlatformKoa, module, settings).bootstrap(module, settings); } protected createInjector(module: Type, settings: any) { From e3234766f118f78aba4f798ca4b9c3fc3b675f0a Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Fri, 19 Nov 2021 16:57:09 +0100 Subject: [PATCH 2/2] fix(common): Allow a serverless usage --- .nycrc | 2 +- .../src/builder/PlatformBuilder.spec.ts | 40 +++++++++- .../common/src/builder/PlatformBuilder.ts | 76 +++++++++++-------- .../common/src/services/PlatformTest.ts | 3 +- .../src/components/PlatformAws.ts | 6 +- .../platform/platform-express/package.json | 5 +- .../src/components/PlatformExpress.spec.ts | 2 +- .../src/components/PlatformExpress.ts | 21 ++++- .../platform-express/test/app/emulate.ts | 27 +++++++ .../src/components/PlatformKoa.spec.ts | 2 +- .../src/components/PlatformKoa.ts | 29 +++++-- 11 files changed, 165 insertions(+), 48 deletions(-) create mode 100644 packages/platform/platform-express/test/app/emulate.ts diff --git a/.nycrc b/.nycrc index b1e92046673..905248e339e 100644 --- a/.nycrc +++ b/.nycrc @@ -39,5 +39,5 @@ "lines": 99.55, "statements": 99.52, "functions": 99.17, - "branches": 89.36 + "branches": 89.3 } diff --git a/packages/platform/common/src/builder/PlatformBuilder.spec.ts b/packages/platform/common/src/builder/PlatformBuilder.spec.ts index f6e4bc2aada..6b6909d2fa6 100644 --- a/packages/platform/common/src/builder/PlatformBuilder.spec.ts +++ b/packages/platform/common/src/builder/PlatformBuilder.spec.ts @@ -6,6 +6,7 @@ import { BeforeListen, BeforeRoutesInit, Controller, + Injectable, InjectorService, Module, normalizePath, @@ -32,8 +33,12 @@ describe("PlatformBuilder", () => { } ]; + static create(module: Type, settings: Partial = {}) { + return PlatformBuilder.build(this, module, settings); + } + static async bootstrap(module: Type, settings: Partial = {}) { - return PlatformBuilder.build(this, module, settings).bootstrap(module, settings); + return PlatformBuilder.build(this, module, settings).bootstrap(); } async loadStatics(): Promise { @@ -146,6 +151,39 @@ describe("PlatformBuilder", () => { expect(server.injector.emit).to.have.been.calledWithExactly("$onDestroy"); }); }); + + describe("callback()", () => { + it("should return the callback", async () => { + // WHEN + const server = await PlatformCustom.bootstrap(ServerModule, { + httpPort: false, + httpsPort: false + }); + + expect(server.callback()).to.deep.eq(server.app.raw); + + server.callback({} as any, {} as any); + }); + }); + + describe("useProvider()", () => { + it("should add provider", async () => { + // WHEN + const server = PlatformCustom.create(ServerModule, { + httpPort: false, + httpsPort: false + }); + + @Injectable() + class Token {} + + server.useProvider(Token, {}); + + await server.bootstrap(); + + expect(server.injector.get(Token)).to.be.instanceof(Token); + }); + }); describe("addComponents", () => { it("should add components", async () => { // GIVEN diff --git a/packages/platform/common/src/builder/PlatformBuilder.ts b/packages/platform/common/src/builder/PlatformBuilder.ts index fbbc701de15..df7d8cf0fb1 100644 --- a/packages/platform/common/src/builder/PlatformBuilder.ts +++ b/packages/platform/common/src/builder/PlatformBuilder.ts @@ -1,5 +1,5 @@ import {nameOf, toMap, Type} from "@tsed/core"; -import {colors, Container, createContainer, InjectorService, IProvider, ProviderScope, setLoggerLevel} from "@tsed/di"; +import {colors, createContainer, InjectorService, IProvider, ProviderScope, setLoggerLevel} from "@tsed/di"; import {importProviders} from "@tsed/components-scan"; import {getMiddlewaresForHook} from "@tsed/platform-middlewares"; import {GlobalAcceptMimesMiddleware} from "../middlewares"; @@ -22,6 +22,16 @@ import {PlatformStaticsSettings} from "../config/interfaces/PlatformStaticsSetti import {getStaticsOptions} from "../utils/getStaticsOptions"; import {Route} from "../interfaces/Route"; import {getConfiguration} from "../utils/getConfiguration"; +import {IncomingMessage, ServerResponse} from "http"; + +const DEFAULT_PROVIDERS = [ + {provide: PlatformHandler}, + {provide: PlatformResponse}, + {provide: PlatformRequest}, + {provide: PlatformRouter}, + {provide: PlatformApplication}, + {provide: Platform} +]; /** * @ignore @@ -34,38 +44,36 @@ export interface PlatformType extends Type { * @ignore */ export interface PlatformBootstrap { + create(module: Type, settings?: Partial): PlatformBuilder; + bootstrap(module: Type, settings?: Partial): Promise; } +export interface PlatformBuilderOptions { + name: string; + providers: IProvider[]; + settings: any; + module: Type; +} + /** * @platform */ export abstract class PlatformBuilder { static currentPlatform: Type> & PlatformBootstrap; + readonly name: string = ""; protected startedAt = new Date(); protected current = new Date(); - protected locals: Container; #injector: InjectorService; - #providers: Map; #rootModule: Type; - constructor({name, providers, settings, module}: {name: string; providers: IProvider[]; settings: any; module: Type}) { + constructor({name, providers, settings, module}: PlatformBuilderOptions) { this.name = name; - this.#providers = toMap(providers, "provide"); - - this.locals = new Container(); - this.#rootModule = module; - const configuration = getConfiguration(settings, module); - this.useProvider(PlatformHandler, this.#providers.get(PlatformHandler)) - .useProvider(PlatformResponse, this.#providers.get(PlatformResponse)) - .useProvider(PlatformRequest, this.#providers.get(PlatformRequest)) - .useProvider(PlatformRouter, this.#providers.get(PlatformRouter)) - .useProvider(PlatformApplication, this.#providers.get(PlatformApplication)) - .useProvider(Platform, this.#providers.get(Platform)); + this.createInjector(module, providers, settings); } get injector(): InjectorService { @@ -130,6 +138,16 @@ export abstract class PlatformBuilder void; + callback(req: IncomingMessage, res: ServerResponse): void; + callback(req?: IncomingMessage, res?: ServerResponse) { + if (req && res) { + return this.app.callback()(req, res); + } + + return this.app.callback(); + } + log(...data: any[]) { return !this.disableBootstrapLog && this.logger.info(...data, this.diff()); } @@ -250,11 +268,15 @@ export abstract class PlatformBuilder, settings?: Partial) { - this.locals.addProvider(token, settings); + this.injector.addProvider(token, settings); return this; } + async bootstrap() { + return this.runLifecycle(); + } + protected diff() { const ms = colors.yellow(`+${new Date().getTime() - this.current.getTime()}ms`); this.current = new Date(); @@ -290,17 +312,6 @@ export abstract class PlatformBuilder, settings: Partial = {}) { - this.createInjector(module, { - ...settings, - PLATFORM_NAME: this.name - }); - - await this.runLifecycle(); - - return this; - } - protected async listenServers(): Promise { await Promise.all([listenHttpServer(this.injector), listenHttpsServer(this.injector)]); } @@ -342,15 +353,16 @@ export abstract class PlatformBuilder, settings: any) { - // configure locals providers - this.#rootModule = module; + protected createInjector(module: Type, providers: IProvider[], settings: any) { const configuration = getConfiguration(settings, module); + configuration.PLATFORM_NAME = this.name; this.#injector = createInjector(configuration); - this.locals.forEach((provider) => { - this.injector.addProvider(provider.token, provider); + providers = [...DEFAULT_PROVIDERS, ...providers]; + + toMap(providers, "provide").forEach((provider, token) => { + this.injector.addProvider(token, provider); }); createPlatformApplication(this.injector); diff --git a/packages/platform/common/src/services/PlatformTest.ts b/packages/platform/common/src/services/PlatformTest.ts index f5885e82207..dc038b296c2 100644 --- a/packages/platform/common/src/services/PlatformTest.ts +++ b/packages/platform/common/src/services/PlatformTest.ts @@ -47,7 +47,8 @@ export class PlatformTest extends DITest { } // @ts-ignore - instance = await PlatformBuilder.build(platform).bootstrap(mod, DITest.configure(settings)); + settings = DITest.configure(settings); + instance = await PlatformBuilder.build(platform, mod, settings).bootstrap(); if (!settings.listen) { await instance.callHook("$beforeListen"); diff --git a/packages/platform/platform-aws/src/components/PlatformAws.ts b/packages/platform/platform-aws/src/components/PlatformAws.ts index 725e61ce001..ec584c781bb 100644 --- a/packages/platform/platform-aws/src/components/PlatformAws.ts +++ b/packages/platform/platform-aws/src/components/PlatformAws.ts @@ -45,7 +45,9 @@ export class PlatformAws { // istanbul ignore next PlatformBuilder.currentPlatform = settings.platform || PlatformBuilder.currentPlatform; - this.promise = PlatformBuilder.currentPlatform.bootstrap(module, {...settings, disableComponentScan: true}).then(PlatformAws.onInit); + const platform = PlatformBuilder.currentPlatform.create(module, settings); + + this.promise = platform.bootstrap().then(PlatformAws.onInit); return PlatformAws; } @@ -66,7 +68,7 @@ export class PlatformAws { await platform.callHook("$beforeListen"); // create Aws server - PlatformAws.awsServer = createServer(platform.app.callback(), PlatformAws.onListen, binaryMimeTypes); + PlatformAws.awsServer = createServer(platform.callback(), PlatformAws.onListen, binaryMimeTypes); } protected static async onListen() { diff --git a/packages/platform/platform-express/package.json b/packages/platform/platform-express/package.json index cb81a09e122..695deff0f3f 100644 --- a/packages/platform/platform-express/package.json +++ b/packages/platform/platform-express/package.json @@ -42,7 +42,8 @@ }, "scripts": { "build": "tsc --build tsconfig.compile.json", - "start": "ts-node -r tsconfig-paths/register test/app/index.ts" + "start": "ts-node -r tsconfig-paths/register test/app/index.ts", + "start:emulate": "ts-node -r tsconfig-paths/register test/app/emulate.ts" }, "dependencies": { "express": "^4.17.1", @@ -79,4 +80,4 @@ "@types/multer": "^1.4.5", "body-parser": "1.19.0" } -} \ No newline at end of file +} diff --git a/packages/platform/platform-express/src/components/PlatformExpress.spec.ts b/packages/platform/platform-express/src/components/PlatformExpress.spec.ts index 00cde27bf28..42c350c7d2f 100644 --- a/packages/platform/platform-express/src/components/PlatformExpress.spec.ts +++ b/packages/platform/platform-express/src/components/PlatformExpress.spec.ts @@ -31,7 +31,7 @@ describe("PlatformExpress", () => { await PlatformExpress.bootstrap(Test); expect(PlatformExpress.build).to.have.been.calledWithExactly(PlatformExpress, Test, {}); - expect(platform.bootstrap).to.have.been.calledWithExactly(Test, {}); + expect(platform.bootstrap).to.have.been.calledWithExactly(); expect(PlatformExpress.providers).to.deep.equal([ { provide: PlatformApplication, diff --git a/packages/platform/platform-express/src/components/PlatformExpress.ts b/packages/platform/platform-express/src/components/PlatformExpress.ts index b7964351d29..cc5f0fd55d8 100644 --- a/packages/platform/platform-express/src/components/PlatformExpress.ts +++ b/packages/platform/platform-express/src/components/PlatformExpress.ts @@ -48,8 +48,27 @@ export class PlatformExpress extends PlatformBuilder, settings: Partial = {}) { + return this.build(PlatformExpress, module, { + httpsPort: false, + httpPort: false, + ...settings, + disableComponentsScan: true + }); + } + + /** + * Bootstrap a server application + * @param module + * @param settings + */ static async bootstrap(module: Type, settings: Partial = {}): Promise { - return this.build(PlatformExpress, module, settings).bootstrap(module, settings); + return this.build(PlatformExpress, module, settings).bootstrap(); } protected useRouter(): this { diff --git a/packages/platform/platform-express/test/app/emulate.ts b/packages/platform/platform-express/test/app/emulate.ts new file mode 100644 index 00000000000..7eb7e306b70 --- /dev/null +++ b/packages/platform/platform-express/test/app/emulate.ts @@ -0,0 +1,27 @@ +import http from "http"; +import {PlatformExpress} from "@tsed/platform-express"; +import {Server} from "./Server"; +import {Controller} from "@tsed/di"; +import {Get} from "@tsed/schema"; +import {QueryParams} from "@tsed/platform-params"; + +@Controller("/hello") +class HelloWorld { + @Get("/") + get(@QueryParams("q") query: string[]) { + return { test: "Hello world" }; + } +} + +const platform = PlatformExpress.create(Server, { + mount: { "/rest": [HelloWorld] } +}); + +const promise = platform.bootstrap() + +const server = http.createServer(async (req, res) => { + await promise; + platform.callback(req, res); +}); + +server.listen(3002); diff --git a/packages/platform/platform-koa/src/components/PlatformKoa.spec.ts b/packages/platform/platform-koa/src/components/PlatformKoa.spec.ts index c37037f1bf3..13d812c3fea 100644 --- a/packages/platform/platform-koa/src/components/PlatformKoa.spec.ts +++ b/packages/platform/platform-koa/src/components/PlatformKoa.spec.ts @@ -25,7 +25,7 @@ describe("PlatformKoa", () => { await PlatformKoa.bootstrap(Test); expect(PlatformKoa.build).to.have.been.calledWithExactly(PlatformKoa, Test, {}); - expect(platform.bootstrap).to.have.been.calledWithExactly(Test, {}); + expect(platform.bootstrap).to.have.been.calledWithExactly(); expect(PlatformKoa.providers).to.deep.equal([ { provide: PlatformResponse, diff --git a/packages/platform/platform-koa/src/components/PlatformKoa.ts b/packages/platform/platform-koa/src/components/PlatformKoa.ts index 265b13e0952..17d1afb7157 100644 --- a/packages/platform/platform-koa/src/components/PlatformKoa.ts +++ b/packages/platform/platform-koa/src/components/PlatformKoa.ts @@ -1,8 +1,10 @@ import KoaRouter from "@koa/router"; import { createContext, + IProvider, PlatformApplication, PlatformBuilder, + PlatformBuilderOptions, PlatformExceptions, PlatformHandler, PlatformRequest, @@ -14,6 +16,7 @@ import {Type} from "@tsed/core"; import Koa, {Context, Next} from "koa"; import {resourceNotFoundMiddleware} from "../middlewares/resourceNotFoundMiddleware"; import {PlatformKoaApplication, PlatformKoaHandler, PlatformKoaRequest, PlatformKoaResponse, PlatformKoaRouter} from "../services"; +import {getConfiguration} from "@tsed/common/src/utils/getConfiguration"; /** * @platform @@ -43,12 +46,8 @@ export class PlatformKoa extends PlatformBuilder { } ]; - static async bootstrap(module: Type, settings: Partial = {}): Promise { - return this.build(PlatformKoa, module, settings).bootstrap(module, settings); - } - - protected createInjector(module: Type, settings: any) { - super.createInjector(module, settings); + constructor(options: PlatformBuilderOptions) { + super(options); const listener: any = (error: any, ctx: Koa.Context) => { this.injector.get(PlatformExceptions)?.catch(error, ctx.request.$ctx); @@ -58,6 +57,24 @@ export class PlatformKoa extends PlatformBuilder { this.app.getApp().on("error", listener); } + /** + * Create new serverless application. In this mode, the component scan are disabled. + * @param module + * @param settings + */ + static create(module: Type, settings: Partial = {}) { + return this.build(PlatformKoa, module, { + httpsPort: false, + httpPort: false, + ...settings, + disableComponentsScan: true + }); + } + + static async bootstrap(module: Type, settings: Partial = {}): Promise { + return this.build(PlatformKoa, module, settings).bootstrap(); + } + protected useRouter(): this { this.app.getApp().use(resourceNotFoundMiddleware).use(this.app.getRouter().routes()).use(this.app.getRouter().allowedMethods());