Skip to content

Commit

Permalink
fix: rollback Subscriber decorator usage and allow subscriber to inje…
Browse files Browse the repository at this point in the history
…ct service
  • Loading branch information
Romakita committed Apr 8, 2023
1 parent 8114796 commit 3f7edc1
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 64 deletions.
9 changes: 5 additions & 4 deletions packages/orm/mikro-orm/src/MikroOrmModule.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import {EntityManager, EventSubscriber, MikroORM, Options} from "@mikro-orm/core";
import {EntityManager, EventSubscriber, MikroORM, Options, Subscriber} from "@mikro-orm/core";
import {PlatformTest} from "@tsed/common";
import {anyOfClass, anything, deepEqual, instance, mock, reset, verify, when} from "ts-mockito";
import {MikroOrmModule} from "./MikroOrmModule";
import {MikroOrmContext} from "./services/MikroOrmContext";
import {MikroOrmRegistry} from "./services/MikroOrmRegistry";
import {Subscriber} from "./decorators/subscriber";

export class Subscriber1 implements EventSubscriber {}

@Subscriber()
export class Subscriber2 implements EventSubscriber {}

@Subscriber()
export class Subscriber3 implements EventSubscriber {}

describe("MikroOrmModule", () => {
const config: Options = {
type: "mongo",
entities: [],
clientUrl: "mongo://localhost",
subscribers: [new Subscriber1()]
subscribers: [new Subscriber1(), Subscriber2]
};
const mockedMikroOrmRegistry = mock<MikroOrmRegistry>();
const mockedMikroOrmContext = mock<MikroOrmContext>();
Expand Down
69 changes: 51 additions & 18 deletions packages/orm/mikro-orm/src/MikroOrmModule.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import {EventSubscriber, MetadataStorage, Options} from "@mikro-orm/core";
import {isClass, isFunction, Type} from "@tsed/core";
import {
AlterRunInContext,
Constant,
Inject,
InjectorService,
LocalsContainer,
Module,
OnDestroy,
OnInit,
ProviderScope,
registerProvider
} from "@tsed/di";
import {instance} from "ts-mockito";
import {OptimisticLockErrorFilter} from "./filters/OptimisticLockErrorFilter";
import {MikroOrmContext} from "./services/MikroOrmContext";
import "./services/MikroOrmFactory";
import {AlterRunInContext, Constant, Inject, Module, OnDestroy, OnInit, registerProvider} from "@tsed/di";
import {EventSubscriber, Options} from "@mikro-orm/core";
import {MikroOrmRegistry} from "./services/MikroOrmRegistry";
import {RetryStrategy} from "./services/RetryStrategy";
import {OptimisticLockErrorFilter} from "./filters/OptimisticLockErrorFilter";
import {MikroOrmContext} from "./services/MikroOrmContext";
import {classOf, Store} from "@tsed/core";
import {DEFAULT_CONTEXT_NAME, SUBSCRIBER_INJECTION_TYPE} from "./constants";

export interface MikroORMOptions extends Omit<Options, "subscribers"> {
subscribers?: (EventSubscriber | Type<EventSubscriber>)[];
}

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
Expand All @@ -16,7 +31,7 @@ declare global {
* The ORM configuration, entity metadata.
* If you omit the `options` parameter, your CLI config will be used.
*/
mikroOrm?: Options[];
mikroOrm?: MikroORMOptions[];
}
}
}
Expand All @@ -26,24 +41,46 @@ declare global {
})
export class MikroOrmModule implements OnDestroy, OnInit, AlterRunInContext {
@Constant("mikroOrm", [])
private readonly settings!: Options[];
private readonly settings!: MikroORMOptions[];

@Inject()
private readonly registry!: MikroOrmRegistry;

@Inject()
private readonly context!: MikroOrmContext;

constructor(@Inject(SUBSCRIBER_INJECTION_TYPE) private subscribers: EventSubscriber[]) {}
@Inject()
private readonly injector: InjectorService;

public async $onInit(): Promise<void> {
const subscribers = MetadataStorage.getSubscriberMetadata();
const container = new LocalsContainer();
const diOpts = {scope: ProviderScope.INSTANCE};

Object.values(subscribers).forEach((instance) => {
// try to make all subscribers decorated with @Subscriber to be injectable (in this case,
this.injector.bindInjectableProperties(instance, container, diOpts);
});

await Promise.all(
this.settings.map(async (opts) =>
this.registry.register({
this.settings.map(async (opts) => {
opts.subscribers = opts.subscribers ?? [];

const subscribers = opts.subscribers.map((subscriber) => {
if (isFunction(subscriber)) {
return this.injector.invoke(subscriber, container, diOpts);
}

this.injector.bindInjectableProperties(instance, container, diOpts);

return subscriber;
});

return this.registry.register({
...opts,
subscribers: [...(opts.subscribers ?? []), ...this.getSubscribers(opts.contextName)]
})
)
subscribers
});
})
);
}

Expand All @@ -55,10 +92,6 @@ export class MikroOrmModule implements OnDestroy, OnInit, AlterRunInContext {
return () => this.createContext(next);
}

private getSubscribers(contextName: string = DEFAULT_CONTEXT_NAME): EventSubscriber[] {
return this.subscribers.filter((instance) => Store.from(classOf(instance)).get(SUBSCRIBER_INJECTION_TYPE)?.contextName === contextName);
}

private createContext(next: (...args: unknown[]) => unknown): unknown {
const instances = [...this.registry.values()];
const managers = instances.map((orm) => orm.em);
Expand Down
26 changes: 0 additions & 26 deletions packages/orm/mikro-orm/src/decorators/subscriber.spec.ts

This file was deleted.

11 changes: 0 additions & 11 deletions packages/orm/mikro-orm/src/decorators/subscriber.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/orm/mikro-orm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export * from "./MikroOrmModule";
export * from "./constants";
export * from "./decorators/entityManager";
export * from "./decorators/orm";
export * from "./decorators/subscriber";
export * from "./decorators/transactional";
export * from "./filters/OptimisticLockErrorFilter";
export * from "./interceptors/TransactionalInterceptor";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import {EventSubscriber, TransactionEventArgs} from "@mikro-orm/core";
import {Inject} from "@tsed/di";
import {Logger} from "@tsed/logger";
import {Subscriber} from "../../../src";

@Subscriber()
export class EventSubscriber1 implements EventSubscriber {
constructor(@Inject() private readonly logger: Logger) {}

Expand Down
14 changes: 14 additions & 0 deletions packages/orm/mikro-orm/test/helpers/services/EventSubscriber3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {EventSubscriber, Subscriber, TransactionEventArgs} from "@mikro-orm/core";
import {Inject} from "@tsed/di";
import {Logger} from "@tsed/logger";

@Subscriber()
export class EventSubscriber1 implements EventSubscriber {
// NOTE: In this case injection on constructor isn't possible the class is already built by @Subscriber
@Inject()
private readonly logger: Logger;

public async afterFlush(_: TransactionEventArgs): Promise<void> {
this.logger.info("Changes has been flushed.");
}
}
6 changes: 4 additions & 2 deletions packages/orm/mikro-orm/test/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import {Logger} from "@tsed/logger";
import {TestMongooseContext} from "@tsed/testing-mongoose";
import {User} from "./helpers/entity/User";
import {Server} from "./helpers/Server";
import {EventSubscriber1} from "./helpers/services/EventSubscriber1";
import {UserService} from "./helpers/services/UserService";
import {MikroORM} from "@mikro-orm/core";
import {anything, spy, verify} from "ts-mockito";
import {EventSubscriber2} from "./helpers/services/EventSubscriber2";
import "./helpers/services/EventSubscriber3";
import {MikroOrmModule, TransactionalInterceptor} from "../src";

describe("MikroOrm integration", () => {
Expand All @@ -21,7 +23,7 @@ describe("MikroOrm integration", () => {
clientUrl,
type: "mongo",
entities: [User],
subscribers: [new EventSubscriber2()]
subscribers: [EventSubscriber1, new EventSubscriber2()]
},
{
clientUrl,
Expand Down Expand Up @@ -78,6 +80,6 @@ describe("MikroOrm integration", () => {

await service.create({email: "test@example.com"});

verify(spiedLogger.info("Changes has been flushed.")).twice();
verify(spiedLogger.info("Changes has been flushed.")).thrice();
});
});

0 comments on commit 3f7edc1

Please sign in to comment.