Skip to content

Commit

Permalink
feat(mikro-orm): add entityManager() and orm() functions to inject re…
Browse files Browse the repository at this point in the history
…spectively ORM and EM instance
  • Loading branch information
Romakita committed Sep 8, 2024
1 parent 46cc085 commit 28c04ec
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 48 deletions.
2 changes: 0 additions & 2 deletions packages/orm/mikro-orm/src/MikroOrmModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
}
Expand Down
109 changes: 92 additions & 17 deletions packages/orm/mikro-orm/src/decorators/entityManager.spec.ts
Original file line number Diff line number Diff line change
@@ -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>(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>(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>(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>(UsersService, [
{
token: MikroOrmRegistry,
use: ormRegistry
}
]);

expect(ormRegistry.get).toHaveBeenCalledWith("context");
expect(usersService.em).toEqual({id: "id"});
});
});
});
7 changes: 6 additions & 1 deletion packages/orm/mikro-orm/src/decorators/entityManager.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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.
Expand Down
106 changes: 90 additions & 16 deletions packages/orm/mikro-orm/src/decorators/orm.spec.ts
Original file line number Diff line number Diff line change
@@ -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>(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>(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>(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>(UsersService, [
{
token: MikroOrmRegistry,
use: ormRegistry
}
]);

expect(ormRegistry.get).toHaveBeenCalledWith("context");
expect(usersService.orm).toEqual({id: "id"});
});
});
});
13 changes: 11 additions & 2 deletions packages/orm/mikro-orm/src/decorators/orm.ts
Original file line number Diff line number Diff line change
@@ -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;
29 changes: 19 additions & 10 deletions packages/orm/mikro-orm/src/decorators/transactional.spec.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
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";

@Controller("/users")
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>(UsersCtrl, [
{
token: TransactionalInterceptor,
use: interceptor
}
});
]);

const result = await usersService.create();

expect(result).toEqual({});
});
});

0 comments on commit 28c04ec

Please sign in to comment.