diff --git a/packages/di/src/common/decorators/inject.spec.ts b/packages/di/src/common/decorators/inject.spec.ts index dcd64dc2864..a1c3f9f9c9d 100644 --- a/packages/di/src/common/decorators/inject.spec.ts +++ b/packages/di/src/common/decorators/inject.spec.ts @@ -1,12 +1,26 @@ import {catchAsyncError} from "@tsed/core"; import {DITest} from "../../node/index.js"; +import {inject} from "../fn/inject.js"; import {injector} from "../fn/injector.js"; import {registerProvider} from "../registries/ProviderRegistry.js"; import {InjectorService} from "../services/InjectorService.js"; import {Inject} from "./inject.js"; import {Injectable} from "./injectable.js"; +@Injectable() +class ProvidersList extends Map {} + +@Injectable() +class MyService { + @Inject(ProvidersList) + providersList: ProvidersList; + + getValue() { + return this.providersList.get("key"); + } +} + describe("@Inject()", () => { beforeEach(() => DITest.create()); afterEach(() => DITest.reset()); @@ -279,4 +293,16 @@ describe("@Inject()", () => { expect(error?.message).toContain("Object isn't a valid token. Please check the token set on Test.test"); }); }); + it("should rebuild all dependencies using invoke", async () => { + const providersList = inject(ProvidersList); + const myService = inject(MyService); + providersList.set("key", "value"); + + expect(inject(ProvidersList).get("key")).toEqual("value"); + expect(myService.getValue()).toEqual("value"); + + const newMyService = await DITest.invoke(MyService, []); + expect(newMyService.getValue()).toEqual(undefined); + expect(myService.getValue()).toEqual("value"); + }); }); diff --git a/packages/di/src/common/fn/inject.spec.ts b/packages/di/src/common/fn/inject.spec.ts index 532bb89728d..7dfff62187f 100644 --- a/packages/di/src/common/fn/inject.spec.ts +++ b/packages/di/src/common/fn/inject.spec.ts @@ -1,7 +1,20 @@ import {DITest} from "../../node/index.js"; +import {Injectable} from "../decorators/injectable.js"; import {InjectorService} from "../services/InjectorService.js"; import {inject} from "./inject.js"; +@Injectable() +class ProvidersList extends Map {} + +@Injectable() +class MyService { + readonly providersList = inject(ProvidersList); + + getValue() { + return this.providersList.get("key"); + } +} + describe("inject()", () => { beforeEach(() => DITest.create()); afterEach(() => DITest.reset()); @@ -26,5 +39,16 @@ describe("inject()", () => { } ]); }); - it("should rebuild all dependencies using invoke", async () => {}); + it("should rebuild all dependencies using invoke", async () => { + const providersList = inject(ProvidersList); + const myService = inject(MyService); + providersList.set("key", "value"); + + expect(inject(ProvidersList).get("key")).toEqual("value"); + expect(myService.getValue()).toEqual("value"); + + const newMyService = await DITest.invoke(MyService, []); + expect(newMyService.getValue()).toEqual(undefined); + expect(myService.getValue()).toEqual("value"); + }); }); diff --git a/packages/di/src/common/fn/inject.ts b/packages/di/src/common/fn/inject.ts index e248569c9f6..6394c16d855 100644 --- a/packages/di/src/common/fn/inject.ts +++ b/packages/di/src/common/fn/inject.ts @@ -1,7 +1,7 @@ import type {InvokeOptions} from "../interfaces/InvokeOptions.js"; import {TokenProvider} from "../interfaces/TokenProvider.js"; import {injector} from "./injector.js"; -import {localsContainer} from "./localsContainer.js"; +import {invokeOptions, localsContainer} from "./localsContainer.js"; /** * Inject a provider to another provider. @@ -21,5 +21,8 @@ import {localsContainer} from "./localsContainer.js"; * @decorator */ export function inject(token: TokenProvider, opts?: Partial>): T { - return injector().invoke(token, opts?.locals || localsContainer(), opts); + return injector().invoke(token, opts?.locals || localsContainer(), { + ...opts, + ...invokeOptions() + }); } diff --git a/packages/di/src/common/fn/localsContainer.ts b/packages/di/src/common/fn/localsContainer.ts index c594f2a624b..9640a3719c8 100644 --- a/packages/di/src/common/fn/localsContainer.ts +++ b/packages/di/src/common/fn/localsContainer.ts @@ -4,12 +4,19 @@ import {InjectorService} from "../services/InjectorService.js"; import {injector} from "./injector.js"; let globalLocals: LocalsContainer | undefined; +let globalInvOpts: any = {}; const stagedLocals: LocalsContainer[] = []; /** * Get the locals container initiated by DITest or .bootstrap() method. */ -export function localsContainer({providers}: {providers?: UseImportTokenProviderOpts[]; rebuild?: boolean} = {}) { +export function localsContainer({ + providers, + rebuild +}: { + providers?: UseImportTokenProviderOpts[]; + rebuild?: boolean; +} = {}) { if (!globalLocals || providers) { globalLocals = new LocalsContainer(); @@ -20,17 +27,26 @@ export function localsContainer({providers}: {providers?: UseImportTokenProvider globalLocals.set(InjectorService, injector()); } + + if (rebuild) { + globalInvOpts.rebuild = rebuild; + } } return globalLocals; } +export function invokeOptions() { + return {...globalInvOpts}; +} + /** * Reset the locals container. */ export function detachLocalsContainer() { globalLocals && stagedLocals.push(globalLocals); globalLocals = undefined; + globalInvOpts = {}; } export function cleanAllLocalsContainer() { diff --git a/packages/di/src/common/services/InjectorService.ts b/packages/di/src/common/services/InjectorService.ts index 1b9780abed9..f619fe2df78 100644 --- a/packages/di/src/common/services/InjectorService.ts +++ b/packages/di/src/common/services/InjectorService.ts @@ -507,7 +507,6 @@ export class InjectorService extends Container { Reflect.defineProperty(instance, DI_INVOKE_OPTIONS, { get: () => ({rebuild: options.rebuild, locals}) }); - // TODO add a way to notify DI consumer when a class instance is build } return instance; diff --git a/packages/platform/platform-serverless-testing/src/PlatformServerlessTest.ts b/packages/platform/platform-serverless-testing/src/PlatformServerlessTest.ts index d33203121ea..14aaab58df9 100644 --- a/packages/platform/platform-serverless-testing/src/PlatformServerlessTest.ts +++ b/packages/platform/platform-serverless-testing/src/PlatformServerlessTest.ts @@ -1,6 +1,6 @@ -import {PlatformBuilder, PlatformBuilderSettings} from "@tsed/common"; +import type {PlatformBuilder, PlatformBuilderSettings} from "@tsed/common"; import {nameOf, Type} from "@tsed/core"; -import {DITest} from "@tsed/di"; +import {destroyInjector, DITest, hasInjector} from "@tsed/di"; import {APIGatewayEventDefaultAuthorizerContext, APIGatewayProxyEventBase, APIGatewayProxyHandler} from "aws-lambda"; import {APIGatewayProxyResult} from "aws-lambda/trigger/api-gateway-proxy.js"; @@ -153,7 +153,7 @@ export class PlatformServerlessTest extends DITest { static request = LambdaClientRequest; static bootstrap( - serverless: {bootstrap: (server: Type, settings: PlatformBuilderSettings) => PlatformBuilder}, + serverless: {bootstrap: (server: Type, settings: TsED.Configuration) => PlatformBuilder}, {server, ...settings}: PlatformBuilderSettings & {server: Type} ): () => Promise; static bootstrap( @@ -177,8 +177,6 @@ export class PlatformServerlessTest extends DITest { } PlatformServerlessTest.callbacks.handler = instance.handler(); - // used by inject method - DITest.injector = instance.injector; return instance.promise; }; @@ -191,9 +189,8 @@ export class PlatformServerlessTest extends DITest { if (PlatformServerlessTest.instance) { await PlatformServerlessTest.instance.stop(); } - if (DITest.hasInjector()) { - await DITest.injector.destroy(); - DITest._injector = null; + if (hasInjector()) { + await destroyInjector(); } } }