From d35a703ba5861a6602d28de04e702ce3028fad73 Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Sun, 8 Sep 2024 11:00:53 +0200 Subject: [PATCH] feat(mikro-orm): add entityManager() and orm() functions to inject respectively ORM and EM instance --- packages/orm/mikro-orm/src/MikroOrmModule.ts | 2 - .../src/decorators/entityManager.spec.ts | 109 +++++++++++++++--- .../mikro-orm/src/decorators/entityManager.ts | 7 +- .../orm/mikro-orm/src/decorators/orm.spec.ts | 106 ++++++++++++++--- packages/orm/mikro-orm/src/decorators/orm.ts | 13 ++- .../src/decorators/transactional.spec.ts | 29 +++-- 6 files changed, 218 insertions(+), 48 deletions(-) diff --git a/packages/orm/mikro-orm/src/MikroOrmModule.ts b/packages/orm/mikro-orm/src/MikroOrmModule.ts index 5ec1aeed109..3e6494c88cb 100644 --- a/packages/orm/mikro-orm/src/MikroOrmModule.ts +++ b/packages/orm/mikro-orm/src/MikroOrmModule.ts @@ -78,8 +78,6 @@ export class MikroOrmModule implements OnDestroy, OnInit, AlterRunInContext { return this.injector.invoke(subscriber, container, diOpts); } - this.injector.bindInjectableProperties(subscriber, container, diOpts); - return subscriber; }); } diff --git a/packages/orm/mikro-orm/src/decorators/entityManager.spec.ts b/packages/orm/mikro-orm/src/decorators/entityManager.spec.ts index d3d636dcc0e..f6c8d28fc05 100644 --- a/packages/orm/mikro-orm/src/decorators/entityManager.spec.ts +++ b/packages/orm/mikro-orm/src/decorators/entityManager.spec.ts @@ -1,22 +1,97 @@ -import {DecoratorTypes, Store} from "@tsed/core"; -import {Controller, INJECTABLE_PROP} from "@tsed/di"; +import {MikroORM} from "@mikro-orm/core"; import {MongoEntityManager} from "@mikro-orm/mongodb"; -import {EntityManager} from "./entityManager.js"; - -@Controller("/users") -export class UsersCtrl { - @EntityManager() - public readonly em!: MongoEntityManager; -} - -describe("@Orm", () => { - it("should decorate property", () => { - expect(Store.from(UsersCtrl).get(INJECTABLE_PROP)).toEqual({ - em: { - propertyKey: "em", - bindingType: DecoratorTypes.PROP, - resolver: expect.any(Function) +import {DITest, Injectable} from "@tsed/di"; +import {afterEach, beforeEach} from "vitest"; +import {MikroOrmRegistry} from "../services/MikroOrmRegistry.js"; +import {Em, entityManager, EntityManager} from "./entityManager.js"; + +describe("@EntityManager()", () => { + beforeEach(() => DITest.create()); + afterEach(() => DITest.reset()); + + describe("decorator", () => { + it("should decorate property (without context)", async () => { + @Injectable() + class UsersService { + @Em() + public readonly em!: MongoEntityManager; + } + + const ormRegistry = { + get: vi.fn().mockReturnValue({em: {id: "id"}} as never) + }; + + const usersService = await DITest.invoke(UsersService, [ + { + token: MikroOrmRegistry, + use: ormRegistry + } + ]); + expect(ormRegistry.get).toHaveBeenCalledWith(undefined); + expect(usersService.em).toEqual({id: "id"}); + }); + it("should decorate property (with context)", async () => { + @Injectable() + class UsersService { + @EntityManager("context") + public readonly orm!: MikroORM; + } + + const ormRegistry = { + get: vi.fn().mockReturnValue({em: {id: "id"}} as never) + }; + + const usersService = await DITest.invoke(UsersService, [ + { + token: MikroOrmRegistry, + use: ormRegistry + } + ]); + + expect(ormRegistry.get).toHaveBeenCalledWith("context"); + expect(usersService.orm).toEqual({id: "id"}); + }); + }); + + describe("prop function", () => { + it("should inject property (without context)", async () => { + @Injectable() + class UsersService { + public readonly em = entityManager(); + } + + const ormRegistry = { + get: vi.fn().mockReturnValue({em: {id: "id"}} as never) + }; + + const usersService = await DITest.invoke(UsersService, [ + { + token: MikroOrmRegistry, + use: ormRegistry + } + ]); + expect(ormRegistry.get).toHaveBeenCalledWith(undefined); + expect(usersService.em).toEqual({id: "id"}); + }); + it("should inject property (with context)", async () => { + @Injectable() + class UsersService { + public readonly em = entityManager("context"); } + + const ormRegistry = { + get: vi.fn().mockReturnValue({em: {id: "id"}} as never) + }; + + const usersService = await DITest.invoke(UsersService, [ + { + token: MikroOrmRegistry, + use: ormRegistry + } + ]); + + expect(ormRegistry.get).toHaveBeenCalledWith("context"); + expect(usersService.em).toEqual({id: "id"}); }); }); }); diff --git a/packages/orm/mikro-orm/src/decorators/entityManager.ts b/packages/orm/mikro-orm/src/decorators/entityManager.ts index 2899d6d498f..e29cf64d703 100644 --- a/packages/orm/mikro-orm/src/decorators/entityManager.ts +++ b/packages/orm/mikro-orm/src/decorators/entityManager.ts @@ -1,5 +1,10 @@ import {Inject} from "@tsed/di"; import {MikroOrmRegistry} from "../services/MikroOrmRegistry.js"; +import {orm} from "./orm.js"; + +export function entityManager(contextName?: string) { + return orm(contextName)?.em; +} /** * Get the entity manager for the given context name. @@ -8,7 +13,7 @@ import {MikroOrmRegistry} from "../services/MikroOrmRegistry.js"; * @mikroOrm */ export const EntityManager = (contextName?: string): PropertyDecorator => - Inject(MikroOrmRegistry, (registry: MikroOrmRegistry) => registry.get(contextName)?.em) as PropertyDecorator; + Inject(MikroOrmRegistry, {transform: (registry: MikroOrmRegistry) => registry.get(contextName)?.em}) as PropertyDecorator; /** * Get the entity manager for the given context name. diff --git a/packages/orm/mikro-orm/src/decorators/orm.spec.ts b/packages/orm/mikro-orm/src/decorators/orm.spec.ts index e9c0a3b6f89..2e0a753b9bc 100644 --- a/packages/orm/mikro-orm/src/decorators/orm.spec.ts +++ b/packages/orm/mikro-orm/src/decorators/orm.spec.ts @@ -1,22 +1,96 @@ -import {DecoratorTypes, Store} from "@tsed/core"; -import {Controller, INJECTABLE_PROP} from "@tsed/di"; -import {Orm} from "./orm.js"; import {MikroORM} from "@mikro-orm/core"; +import {DITest, Injectable} from "@tsed/di"; +import {afterEach, beforeEach} from "vitest"; +import {MikroOrmRegistry} from "../services/MikroOrmRegistry.js"; +import {orm, Orm} from "./orm.js"; -@Controller("/users") -export class UsersCtrl { - @Orm() - public readonly orm!: MikroORM; -} - -describe("@Orm", () => { - it("should decorate property", () => { - expect(Store.from(UsersCtrl).get(INJECTABLE_PROP)).toEqual({ - orm: { - propertyKey: "orm", - bindingType: DecoratorTypes.PROP, - resolver: expect.any(Function) +describe("@Orm()", () => { + beforeEach(() => DITest.create()); + afterEach(() => DITest.reset()); + + describe("decorator", () => { + it("should decorate property (without context)", async () => { + @Injectable() + class UsersService { + @Orm() + public readonly orm!: MikroORM; + } + + const ormRegistry = { + get: vi.fn().mockReturnValue({id: "id"} as never) + }; + + const usersService = await DITest.invoke(UsersService, [ + { + token: MikroOrmRegistry, + use: ormRegistry + } + ]); + expect(ormRegistry.get).toHaveBeenCalledWith(undefined); + expect(usersService.orm).toEqual({id: "id"}); + }); + it("should decorate property (with context)", async () => { + @Injectable() + class UsersService { + @Orm("context") + public readonly orm!: MikroORM; } + + const ormRegistry = { + get: vi.fn().mockReturnValue({id: "id"} as never) + }; + + const usersService = await DITest.invoke(UsersService, [ + { + token: MikroOrmRegistry, + use: ormRegistry + } + ]); + + expect(ormRegistry.get).toHaveBeenCalledWith("context"); + expect(usersService.orm).toEqual({id: "id"}); + }); + }); + + describe("prop function", () => { + it("should inject property (without context)", async () => { + @Injectable() + class UsersService { + public readonly orm = orm(); + } + + const ormRegistry = { + get: vi.fn().mockReturnValue({id: "id"} as never) + }; + + const usersService = await DITest.invoke(UsersService, [ + { + token: MikroOrmRegistry, + use: ormRegistry + } + ]); + expect(ormRegistry.get).toHaveBeenCalledWith(undefined); + expect(usersService.orm).toEqual({id: "id"}); + }); + it("should inject property (with context)", async () => { + @Injectable() + class UsersService { + public readonly orm = orm("context"); + } + + const ormRegistry = { + get: vi.fn().mockReturnValue({id: "id"} as never) + }; + + const usersService = await DITest.invoke(UsersService, [ + { + token: MikroOrmRegistry, + use: ormRegistry + } + ]); + + expect(ormRegistry.get).toHaveBeenCalledWith("context"); + expect(usersService.orm).toEqual({id: "id"}); }); }); }); diff --git a/packages/orm/mikro-orm/src/decorators/orm.ts b/packages/orm/mikro-orm/src/decorators/orm.ts index 320829be58f..9b7ae8104aa 100644 --- a/packages/orm/mikro-orm/src/decorators/orm.ts +++ b/packages/orm/mikro-orm/src/decorators/orm.ts @@ -1,9 +1,18 @@ +import {MikroORM} from "@mikro-orm/core"; +import {inject, Inject} from "@tsed/di"; import {MikroOrmRegistry} from "../services/MikroOrmRegistry.js"; -import {Inject} from "@tsed/di"; + +/** + * Get the ORM for the given context name using new inject() function. + * @param contextName + */ +export function orm(contextName?: string): MikroORM | undefined { + return inject(MikroOrmRegistry).get(contextName); +} /** * Get the ORM for the given context name. * @param {String} contextName */ export const Orm = (contextName?: string): PropertyDecorator => - Inject(MikroOrmRegistry, (registry: MikroOrmRegistry) => registry.get(contextName)) as PropertyDecorator; + Inject(MikroOrmRegistry, {transform: (registry: MikroOrmRegistry) => registry.get(contextName)}) as PropertyDecorator; diff --git a/packages/orm/mikro-orm/src/decorators/transactional.spec.ts b/packages/orm/mikro-orm/src/decorators/transactional.spec.ts index 5c5c1061845..275e6e74474 100644 --- a/packages/orm/mikro-orm/src/decorators/transactional.spec.ts +++ b/packages/orm/mikro-orm/src/decorators/transactional.spec.ts @@ -1,6 +1,6 @@ import {Post} from "@tsed/common"; -import {Store} from "@tsed/core"; -import {Controller, INJECTABLE_PROP} from "@tsed/di"; +import {Controller, DITest} from "@tsed/di"; +import {afterEach, beforeEach} from "vitest"; import {TransactionalInterceptor} from "../interceptors/TransactionalInterceptor.js"; import {Transactional} from "./transactional.js"; @@ -8,17 +8,26 @@ import {Transactional} from "./transactional.js"; export class UsersCtrl { @Post("/") @Transactional() - create() {} + create(): any {} } describe("@Transactional", () => { - it("should decorate method", () => { - expect(Store.from(UsersCtrl).get(INJECTABLE_PROP)).toEqual({ - create: { - bindingType: "interceptor", - propertyKey: "create", - useType: TransactionalInterceptor + beforeEach(() => DITest.create()); + afterEach(() => DITest.reset()); + it("should decorate method", async () => { + const interceptor = { + intercept: vi.fn().mockResolvedValue({}) + }; + + const usersService = await DITest.invoke(UsersCtrl, [ + { + token: TransactionalInterceptor, + use: interceptor } - }); + ]); + + const result = await usersService.create(); + + expect(result).toEqual({}); }); });