@tsed/mikro-orm 8.11.0 → 8.11.2-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/lib/esm/MikroOrmModule.js +0 -1
  2. package/lib/esm/constants.js +0 -1
  3. package/lib/esm/decorators/entityManager.js +0 -1
  4. package/lib/esm/decorators/orm.js +0 -1
  5. package/lib/esm/decorators/subscriber.js +0 -1
  6. package/lib/esm/decorators/transactional.js +0 -1
  7. package/lib/esm/filters/OptimisticLockErrorFilter.js +0 -1
  8. package/lib/esm/index.js +0 -1
  9. package/lib/esm/interceptors/TransactionalInterceptor.js +0 -1
  10. package/lib/esm/interfaces/AfterTransactionCommit.js +0 -1
  11. package/lib/esm/interfaces/BeforeTransactionCommit.js +0 -1
  12. package/lib/esm/interfaces/RetryStrategy.js +0 -1
  13. package/lib/esm/services/MikroOrmContext.js +0 -1
  14. package/lib/esm/services/MikroOrmFactory.js +0 -1
  15. package/lib/esm/services/MikroOrmRegistry.js +0 -1
  16. package/package.json +13 -10
  17. package/lib/esm/MikroOrmModule.js.map +0 -1
  18. package/lib/esm/constants.js.map +0 -1
  19. package/lib/esm/decorators/entityManager.js.map +0 -1
  20. package/lib/esm/decorators/orm.js.map +0 -1
  21. package/lib/esm/decorators/subscriber.js.map +0 -1
  22. package/lib/esm/decorators/transactional.js.map +0 -1
  23. package/lib/esm/filters/OptimisticLockErrorFilter.js.map +0 -1
  24. package/lib/esm/index.js.map +0 -1
  25. package/lib/esm/interceptors/TransactionalInterceptor.js.map +0 -1
  26. package/lib/esm/interfaces/AfterTransactionCommit.js.map +0 -1
  27. package/lib/esm/interfaces/BeforeTransactionCommit.js.map +0 -1
  28. package/lib/esm/interfaces/RetryStrategy.js.map +0 -1
  29. package/lib/esm/services/MikroOrmContext.js.map +0 -1
  30. package/lib/esm/services/MikroOrmFactory.js.map +0 -1
  31. package/lib/esm/services/MikroOrmRegistry.js.map +0 -1
  32. package/src/MikroOrmModule.spec.ts +0 -110
  33. package/src/MikroOrmModule.ts +0 -104
  34. package/src/constants.ts +0 -2
  35. package/src/decorators/entityManager.spec.ts +0 -98
  36. package/src/decorators/entityManager.ts +0 -25
  37. package/src/decorators/orm.spec.ts +0 -97
  38. package/src/decorators/orm.ts +0 -19
  39. package/src/decorators/subscriber.spec.ts +0 -27
  40. package/src/decorators/subscriber.ts +0 -12
  41. package/src/decorators/transactional.spec.ts +0 -34
  42. package/src/decorators/transactional.ts +0 -10
  43. package/src/filters/OptimisticLockErrorFilter.spec.ts +0 -33
  44. package/src/filters/OptimisticLockErrorFilter.ts +0 -19
  45. package/src/index.ts +0 -17
  46. package/src/interceptors/TransactionalInterceptor.spec.ts +0 -248
  47. package/src/interceptors/TransactionalInterceptor.ts +0 -130
  48. package/src/interfaces/AfterTransactionCommit.ts +0 -5
  49. package/src/interfaces/BeforeTransactionCommit.ts +0 -5
  50. package/src/interfaces/RetryStrategy.ts +0 -5
  51. package/src/services/MikroOrmContext.spec.ts +0 -95
  52. package/src/services/MikroOrmContext.ts +0 -29
  53. package/src/services/MikroOrmFactory.ts +0 -16
  54. package/src/services/MikroOrmRegistry.spec.ts +0 -121
  55. package/src/services/MikroOrmRegistry.ts +0 -62
  56. package/vitest.config.mts +0 -25
@@ -1,19 +0,0 @@
1
- import {MikroORM} from "@mikro-orm/core";
2
- import {Inject, inject} from "@tsed/di";
3
-
4
- import {MikroOrmRegistry} from "../services/MikroOrmRegistry.js";
5
-
6
- /**
7
- * Get the ORM for the given context name using new inject() function.
8
- * @param contextName
9
- */
10
- export function orm(contextName?: string): MikroORM | undefined {
11
- return inject(MikroOrmRegistry).get(contextName);
12
- }
13
-
14
- /**
15
- * Get the ORM for the given context name.
16
- * @param {String} contextName
17
- */
18
- export const Orm = (contextName?: string): PropertyDecorator =>
19
- Inject(MikroOrmRegistry, {transform: (registry: MikroOrmRegistry) => registry.get(contextName)}) as PropertyDecorator;
@@ -1,27 +0,0 @@
1
- import {EventSubscriber} from "@mikro-orm/core";
2
- import {Store} from "@tsed/core";
3
-
4
- import {DEFAULT_CONTEXT_NAME, SUBSCRIBER_INJECTION_TYPE} from "../constants.js";
5
- import {Subscriber} from "./subscriber.js";
6
-
7
- @Subscriber()
8
- export class Subscriber1 implements EventSubscriber {}
9
-
10
- @Subscriber({contextName: "non-default"})
11
- export class Subscriber2 implements EventSubscriber {}
12
-
13
- describe("@Subscriber", () => {
14
- it("should decorate the class", () => {
15
- // act
16
- const result = Store.from(Subscriber1).get(SUBSCRIBER_INJECTION_TYPE);
17
- // assert
18
- expect(result).toEqual({contextName: DEFAULT_CONTEXT_NAME});
19
- });
20
-
21
- it("should decorate the class tying a context name to it", () => {
22
- // act
23
- const result = Store.from(Subscriber2).get(SUBSCRIBER_INJECTION_TYPE);
24
- // assert
25
- expect(result).toEqual({contextName: "non-default"});
26
- });
27
- });
@@ -1,12 +0,0 @@
1
- import {StoreSet, useDecorators} from "@tsed/core";
2
- import {Injectable} from "@tsed/di";
3
-
4
- import {DEFAULT_CONTEXT_NAME, SUBSCRIBER_INJECTION_TYPE} from "../constants.js";
5
-
6
- /**
7
- * Register a new subscriber for the given context name.
8
- * @decorator
9
- * @mikroOrm
10
- */
11
- export const Subscriber = (options: {contextName: string} = {contextName: DEFAULT_CONTEXT_NAME}) =>
12
- useDecorators(Injectable({type: SUBSCRIBER_INJECTION_TYPE}), StoreSet(SUBSCRIBER_INJECTION_TYPE, options));
@@ -1,34 +0,0 @@
1
- import {Controller, DITest} from "@tsed/di";
2
- import {Post} from "@tsed/schema";
3
- import {afterEach, beforeEach} from "vitest";
4
-
5
- import {TransactionalInterceptor} from "../interceptors/TransactionalInterceptor.js";
6
- import {Transactional} from "./transactional.js";
7
-
8
- @Controller("/users")
9
- export class UsersCtrl {
10
- @Post("/")
11
- @Transactional()
12
- create(): any {}
13
- }
14
-
15
- describe("@Transactional", () => {
16
- beforeEach(() => DITest.create());
17
- afterEach(() => DITest.reset());
18
- it("should decorate method", async () => {
19
- const interceptor = {
20
- intercept: vi.fn().mockResolvedValue({})
21
- };
22
-
23
- const usersService = await DITest.invoke<UsersCtrl>(UsersCtrl, [
24
- {
25
- token: TransactionalInterceptor,
26
- use: interceptor
27
- }
28
- ]);
29
-
30
- const result = await usersService.create();
31
-
32
- expect(result).toEqual({});
33
- });
34
- });
@@ -1,10 +0,0 @@
1
- import {Intercept} from "@tsed/di";
2
-
3
- import {TransactionalInterceptor, TransactionOptions} from "../interceptors/TransactionalInterceptor.js";
4
-
5
- /**
6
- * Register a new request context for your method and execute it inside the context.
7
- * @param {String | TransactionOptions} contextNameOrOptions
8
- */
9
- export const Transactional = (contextNameOrOptions?: string | TransactionOptions): MethodDecorator =>
10
- Intercept(TransactionalInterceptor, contextNameOrOptions);
@@ -1,33 +0,0 @@
1
- import {OptimisticLockError} from "@mikro-orm/core";
2
- import {PlatformTest} from "@tsed/platform-http/testing";
3
-
4
- import {OptimisticLockErrorFilter} from "./OptimisticLockErrorFilter.js";
5
-
6
- describe("OptimisticLockErrorFilter", () => {
7
- beforeEach(() => PlatformTest.create());
8
- afterEach(() => PlatformTest.reset());
9
-
10
- describe("catch", () => {
11
- it("should set HTTP status to 409", async () => {
12
- const optimisticLockErrorFilter = await PlatformTest.invoke(OptimisticLockErrorFilter);
13
-
14
- // arrange
15
- const exception = OptimisticLockError.lockFailed("entity");
16
- const context = PlatformTest.createRequestContext();
17
-
18
- vi.spyOn(context.logger, "error").mockReturnThis();
19
-
20
- // act
21
- optimisticLockErrorFilter.catch(exception, context);
22
-
23
- expect(context.logger.error).toHaveBeenCalledWith({
24
- event: "MIKRO_ORM_OPTIMISTIC_LOCK_ERROR",
25
- error_name: exception.name,
26
- error_message: exception.message,
27
- stack: exception.stack
28
- });
29
- expect(context.response.getBody()).toEqual("Update refused. The resource has changed on the server. Please try again later");
30
- expect(context.response.statusCode).toEqual(409);
31
- });
32
- });
33
- });
@@ -1,19 +0,0 @@
1
- import {OptimisticLockError} from "@mikro-orm/core";
2
- import {Catch, type ExceptionFilterMethods} from "@tsed/platform-exceptions";
3
- import {PlatformContext} from "@tsed/platform-http";
4
-
5
- @Catch(OptimisticLockError)
6
- export class OptimisticLockErrorFilter implements ExceptionFilterMethods<OptimisticLockError> {
7
- public catch(exception: OptimisticLockError, ctx: PlatformContext): void {
8
- const {response, logger} = ctx;
9
-
10
- logger.error({
11
- event: "MIKRO_ORM_OPTIMISTIC_LOCK_ERROR",
12
- error_name: exception.name,
13
- error_message: exception.message,
14
- stack: exception.stack
15
- });
16
-
17
- response.status(409).body(`Update refused. The resource has changed on the server. Please try again later`);
18
- }
19
- }
package/src/index.ts DELETED
@@ -1,17 +0,0 @@
1
- /**
2
- * @file Automatically generated by @tsed/barrels.
3
- */
4
- export * from "./constants.js";
5
- export * from "./decorators/entityManager.js";
6
- export * from "./decorators/orm.js";
7
- export * from "./decorators/subscriber.js";
8
- export * from "./decorators/transactional.js";
9
- export * from "./filters/OptimisticLockErrorFilter.js";
10
- export * from "./interceptors/TransactionalInterceptor.js";
11
- export * from "./interfaces/AfterTransactionCommit.js";
12
- export * from "./interfaces/BeforeTransactionCommit.js";
13
- export * from "./interfaces/RetryStrategy.js";
14
- export * from "./MikroOrmModule.js";
15
- export * from "./services/MikroOrmContext.js";
16
- export * from "./services/MikroOrmFactory.js";
17
- export * from "./services/MikroOrmRegistry.js";
@@ -1,248 +0,0 @@
1
- import {EntityManager, IsolationLevel, MikroORM, OptimisticLockError} from "@mikro-orm/core";
2
- import {InjectorService, InterceptorContext} from "@tsed/di";
3
- import {Logger} from "@tsed/logger";
4
- import {anyFunction, anything, deepEqual, instance, mock, objectContaining, reset, verify, when} from "ts-mockito";
5
-
6
- import {RetryStrategy} from "../interfaces/RetryStrategy.js";
7
- import {MikroOrmContext} from "../services/MikroOrmContext.js";
8
- import {MikroOrmRegistry} from "../services/MikroOrmRegistry.js";
9
- import {TransactionalInterceptor} from "./TransactionalInterceptor.js";
10
-
11
- // AHDOC: https://github.com/NagRock/ts-mockito/issues/191
12
- // TODO: avoid using ts-mockito
13
- const resolvableInstance = <T extends object>(m: T): T =>
14
- new Proxy<T>(instance(m), {
15
- get(target, prop, receiver) {
16
- if (["Symbol(Symbol.toPrimitive)", "then", "catch"].includes(prop.toString())) {
17
- return undefined;
18
- }
19
-
20
- return Reflect.get(target, prop, receiver);
21
- }
22
- });
23
-
24
- describe("TransactionalInterceptor", () => {
25
- const mockedInjectorService = mock<InjectorService>();
26
- const mockedMikroOrmRegistry = mock<MikroOrmRegistry>();
27
- const mockedMikroOrm = mock<MikroORM>();
28
- const mockedEntityManager = mock<EntityManager>();
29
- const mockedLogger = mock<Logger>();
30
- const mockedRetryStrategy = mock<RetryStrategy>();
31
- const mockedMikroOrmContext = mock<MikroOrmContext>();
32
- const next = vi.fn();
33
-
34
- let entityManger!: EntityManager;
35
- let mikroOrm!: MikroORM;
36
- let transactionalInterceptor!: TransactionalInterceptor;
37
-
38
- afterEach(() => {
39
- next.mockReset();
40
- reset<InjectorService | MikroOrmRegistry | MikroORM | EntityManager | Logger | RetryStrategy | MikroOrmContext>(
41
- mockedInjectorService,
42
- mockedMikroOrmRegistry,
43
- mockedMikroOrm,
44
- mockedEntityManager,
45
- mockedLogger,
46
- mockedMikroOrmContext,
47
- mockedRetryStrategy
48
- );
49
- });
50
- beforeEach(() => {
51
- entityManger = resolvableInstance(mockedEntityManager);
52
- mikroOrm = resolvableInstance(mockedMikroOrm);
53
- when(mockedMikroOrm.em).thenReturn(entityManger);
54
-
55
- transactionalInterceptor = new TransactionalInterceptor(
56
- instance(mockedInjectorService),
57
- instance(mockedMikroOrmRegistry),
58
- instance(mockedMikroOrmContext),
59
- instance(mockedLogger)
60
- );
61
- });
62
-
63
- describe("intercept", () => {
64
- it("should run within a existing context", async () => {
65
- // arrange
66
- const context = {} as InterceptorContext;
67
-
68
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
69
- when(mockedMikroOrmContext.has(anything())).thenReturn(true);
70
- when(mockedMikroOrmContext.get(anything())).thenReturn(entityManger);
71
-
72
- // act
73
- await transactionalInterceptor.intercept(context, next);
74
-
75
- // assert
76
- verify(mockedEntityManager.transactional(anyFunction(), objectContaining({}))).once();
77
- });
78
-
79
- it("should set an isolation level", async () => {
80
- // arrange
81
- const context = {options: {isolationLevel: IsolationLevel.SERIALIZABLE}} as InterceptorContext;
82
-
83
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
84
- when(mockedMikroOrmContext.has(anything())).thenReturn(true);
85
- when(mockedMikroOrmContext.get(anything())).thenReturn(entityManger);
86
-
87
- // act
88
- await transactionalInterceptor.intercept(context, next);
89
-
90
- // assert
91
- verify(mockedEntityManager.transactional(anyFunction(), objectContaining({isolationLevel: IsolationLevel.SERIALIZABLE}))).once();
92
- });
93
-
94
- it("should run within a new context", async () => {
95
- // arrange
96
- const context = {} as InterceptorContext;
97
-
98
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
99
- when(mockedMikroOrmContext.has(anything())).thenReturn(false);
100
- when(mockedMikroOrmContext.get(anything())).thenReturn(entityManger);
101
- when(mockedMikroOrmContext.run(anything(), anything())).thenCall((_: EntityManager[], func: (...args: unknown[]) => unknown) =>
102
- func()
103
- );
104
-
105
- // act
106
- await transactionalInterceptor.intercept(context, next);
107
-
108
- // assert
109
- verify(mockedMikroOrmContext.run(deepEqual([entityManger]), anyFunction())).twice();
110
- verify(mockedEntityManager.transactional(anyFunction(), objectContaining({}))).once();
111
- });
112
-
113
- it("should perform a task within a transaction", async () => {
114
- // arrange
115
- const context = {} as InterceptorContext;
116
-
117
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
118
- when(mockedMikroOrmContext.has(anything())).thenReturn(true);
119
- when(mockedMikroOrmContext.get(anything())).thenReturn(entityManger);
120
- when(mockedEntityManager.transactional(anything(), anything())).thenCall((func: (...args: unknown[]) => unknown) => func());
121
-
122
- // act
123
- await transactionalInterceptor.intercept(context, next);
124
-
125
- // assert
126
- expect(next).toHaveBeenCalled();
127
- });
128
-
129
- it("should emit events in the right order", async () => {
130
- // arrange
131
- const context = {} as InterceptorContext;
132
-
133
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
134
- when(mockedMikroOrmContext.has(anything())).thenReturn(true);
135
- when(mockedMikroOrmContext.get(anything())).thenReturn(entityManger);
136
- when(mockedEntityManager.transactional(anything(), anything())).thenCall((func: (...args: unknown[]) => unknown) => func());
137
- when(mockedMikroOrmContext.run(anything(), anything())).thenCall((_: EntityManager[], func: (...args: unknown[]) => unknown) =>
138
- func()
139
- );
140
-
141
- // act
142
- await transactionalInterceptor.intercept(context, next);
143
-
144
- // assert
145
- verify(mockedInjectorService.alterAsync("$beforeTransactionCommit", entityManger)).calledBefore(
146
- mockedInjectorService.alterAsync("$afterTransactionCommit", entityManger)
147
- );
148
- });
149
-
150
- it("should disable an explicit transaction", async () => {
151
- // arrange
152
- const context = {options: {disabled: true}} as InterceptorContext;
153
-
154
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
155
- when(mockedMikroOrmContext.has(anything())).thenReturn(true);
156
- when(mockedMikroOrmContext.get(anything())).thenReturn(entityManger);
157
-
158
- // act
159
- await transactionalInterceptor.intercept(context, next);
160
-
161
- // assert
162
- expect(next).toHaveBeenCalled();
163
- verify(mockedEntityManager.transactional(anything(), anything())).never();
164
- });
165
-
166
- it("should throw an error if context is lost", async () => {
167
- // arrange
168
- const context = {} as InterceptorContext;
169
-
170
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
171
- when(mockedMikroOrmContext.has(anything())).thenReturn(true);
172
- when(mockedEntityManager.transactional(anything(), anything())).thenCall((func: (...args: unknown[]) => unknown) => func());
173
-
174
- // act
175
- const result = transactionalInterceptor.intercept(context, next);
176
-
177
- // assert
178
- await expect(result).rejects.toThrow("No such context");
179
- });
180
-
181
- it("should throw an error if no such context", async () => {
182
- // arrange
183
- const context = {} as InterceptorContext;
184
-
185
- when(mockedMikroOrmRegistry.get(anything())).thenReturn();
186
-
187
- // act
188
- const result = transactionalInterceptor.intercept(context, next);
189
-
190
- // assert
191
- await expect(result).rejects.toThrow("No such context");
192
- });
193
-
194
- it("should throw an optimistic lock exception immediately if no retry strategy", async () => {
195
- // arrange
196
- const context = {} as InterceptorContext;
197
-
198
- when(mockedMikroOrmContext.has(anything())).thenReturn(true);
199
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
200
- when(mockedEntityManager.transactional(anything(), anything())).thenReject(OptimisticLockError.lockFailed("Lock"));
201
- when(mockedMikroOrmContext.get(anything())).thenReturn(entityManger);
202
-
203
- // act
204
- const result = transactionalInterceptor.intercept(context, next);
205
-
206
- // assert
207
- await expect(result).rejects.toThrow("Lock");
208
- verify(mockedEntityManager.transactional(anyFunction(), objectContaining({}))).times(1);
209
- });
210
-
211
- it("should apply a retry mechanism if retry is enabled", async () => {
212
- // arrange
213
- transactionalInterceptor = new TransactionalInterceptor(
214
- instance(mockedInjectorService),
215
- instance(mockedMikroOrmRegistry),
216
- instance(mockedMikroOrmContext),
217
- instance(mockedLogger),
218
- instance(mockedRetryStrategy)
219
- );
220
- const context = {options: {retry: true}} as InterceptorContext;
221
-
222
- when(mockedMikroOrmContext.has(anything())).thenReturn(true);
223
- when(mockedRetryStrategy.acquire(anything())).thenResolve();
224
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(mikroOrm);
225
- when(mockedEntityManager.transactional(anything(), anything())).thenReject(OptimisticLockError.lockFailed("Lock"));
226
- when(mockedMikroOrmContext.get(anything())).thenReturn(entityManger);
227
-
228
- // act
229
- await transactionalInterceptor.intercept(context, next);
230
-
231
- // assert
232
- verify(mockedRetryStrategy.acquire(anything())).once();
233
- });
234
-
235
- it("should log a warning if the retry is enabled, while a retry strategy is not resolved", async () => {
236
- // arrange
237
- const context = {options: {retry: true}} as InterceptorContext;
238
-
239
- when(mockedMikroOrmRegistry.get(anything())).thenReturn(instance(mockedMikroOrm));
240
-
241
- // act
242
- await transactionalInterceptor.intercept(context, next);
243
-
244
- // assert
245
- verify(mockedLogger.warn(`To retry a transaction you have to implement a "${RetryStrategy.description}" interface`)).once();
246
- });
247
- });
248
- });
@@ -1,130 +0,0 @@
1
- import {EntityManager, FlushMode, IsolationLevel} from "@mikro-orm/core";
2
- import {Inject, InjectorService, Interceptor, InterceptorContext, InterceptorMethods, InterceptorNext} from "@tsed/di";
3
- import {Logger} from "@tsed/logger";
4
-
5
- import {DEFAULT_CONTEXT_NAME} from "../constants.js";
6
- import {RetryStrategy} from "../interfaces/RetryStrategy.js";
7
- import {MikroOrmContext} from "../services/MikroOrmContext.js";
8
- import {MikroOrmRegistry} from "../services/MikroOrmRegistry.js";
9
-
10
- export interface TransactionOptions {
11
- retry?: boolean;
12
- disabled?: boolean;
13
- isolationLevel?: IsolationLevel;
14
- flushMode?: FlushMode;
15
- contextName?: string;
16
- }
17
-
18
- type TransactionSettings = Omit<TransactionOptions, "contextName"> & Required<Pick<TransactionOptions, "contextName">>;
19
-
20
- @Interceptor()
21
- export class TransactionalInterceptor implements InterceptorMethods {
22
- constructor(
23
- @Inject() private readonly injector: InjectorService,
24
- @Inject() private readonly registry: MikroOrmRegistry,
25
- @Inject() private readonly context: MikroOrmContext,
26
- @Inject() private readonly logger: Logger,
27
- @Inject(RetryStrategy)
28
- private readonly retryStrategy?: RetryStrategy
29
- ) {}
30
-
31
- public intercept(context: InterceptorContext<unknown>, next: InterceptorNext) {
32
- const options = this.extractContextName(context.options);
33
-
34
- if (options.retry && !this.retryStrategy) {
35
- this.logger.warn(`To retry a transaction you have to implement a "${RetryStrategy.description}" interface`);
36
- }
37
-
38
- return this.context.has(options.contextName) ? this.runWithinCtx(next, options) : this.runWithinNewCtx(next, options);
39
- }
40
-
41
- private runWithinCtx(next: InterceptorNext, options: TransactionSettings): Promise<unknown> {
42
- const callback = () => this.executeInTransaction(next, options);
43
-
44
- return options.retry && this.retryStrategy ? this.retryStrategy.acquire(callback) : callback();
45
- }
46
-
47
- private async runWithinNewCtx(next: InterceptorNext, options: TransactionSettings): Promise<unknown> {
48
- const em = await this.getGlobalEntityManager(options.contextName);
49
-
50
- return this.context.run([em], () => this.runWithinCtx(next, options));
51
- }
52
-
53
- private getGlobalEntityManager(contextName: string = DEFAULT_CONTEXT_NAME): EntityManager {
54
- const orm = this.registry.get(contextName);
55
-
56
- if (!orm) {
57
- throw new Error(
58
- `No such context: ${contextName}. Please register a corresponding MikroOrm instance using '@Configuration()' decorator.`
59
- );
60
- }
61
-
62
- return orm.em;
63
- }
64
-
65
- private extractContextName(options: TransactionOptions | string | undefined): TransactionSettings {
66
- if (typeof options === "string") {
67
- return {
68
- contextName: options
69
- };
70
- }
71
-
72
- const {flushMode, isolationLevel, contextName = DEFAULT_CONTEXT_NAME, retry = false, disabled = false} = options ?? {};
73
-
74
- return {
75
- contextName,
76
- retry,
77
- disabled,
78
- flushMode,
79
- isolationLevel
80
- };
81
- }
82
-
83
- private async executeInTransaction(next: InterceptorNext, options: TransactionSettings): Promise<unknown> {
84
- const em = this.getEntityManager(options.contextName);
85
-
86
- if (options.disabled) {
87
- return next();
88
- }
89
-
90
- const result = await em.transactional(() => this.executeHandlerDelegate(next, options), {
91
- flushMode: options.flushMode,
92
- isolationLevel: options.isolationLevel
93
- });
94
-
95
- await this.afterTransactionCommit(options);
96
-
97
- return result;
98
- }
99
-
100
- private async executeHandlerDelegate(next: InterceptorNext, options: TransactionSettings): Promise<unknown> {
101
- const result = await next();
102
- await this.beforeTransactionCommit(options);
103
- return result;
104
- }
105
-
106
- private beforeTransactionCommit(options: TransactionSettings): Promise<unknown> {
107
- return this.injector.alterAsync("$beforeTransactionCommit", this.getEntityManager(options.contextName));
108
- }
109
-
110
- private afterTransactionCommit(options: TransactionSettings): Promise<unknown> {
111
- const em = this.getGlobalEntityManager(options.contextName);
112
-
113
- return this.context.run([em], () => {
114
- const forkedEm = this.getEntityManager(options.contextName);
115
- forkedEm.resetTransactionContext();
116
-
117
- return this.injector.alterAsync("$afterTransactionCommit", forkedEm);
118
- });
119
- }
120
-
121
- private getEntityManager(contextName: string = DEFAULT_CONTEXT_NAME): EntityManager {
122
- const em = this.context.get(contextName);
123
-
124
- if (!em) {
125
- throw new Error(`No such context: ${contextName}. Please check if the async context is lost in one of the asynchronous operations.`);
126
- }
127
-
128
- return em;
129
- }
130
- }
@@ -1,5 +0,0 @@
1
- import {EntityManager} from "@mikro-orm/core";
2
-
3
- export interface AfterTransactionCommit {
4
- $afterTransactionCommit(em: EntityManager): Promise<unknown> | unknown;
5
- }
@@ -1,5 +0,0 @@
1
- import {EntityManager} from "@mikro-orm/core";
2
-
3
- export interface BeforeTransactionCommit {
4
- $beforeTransactionCommit(em: EntityManager): Promise<unknown> | unknown;
5
- }
@@ -1,5 +0,0 @@
1
- export interface RetryStrategy {
2
- acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T> extends Promise<infer V> ? V : ReturnType<T>>;
3
- }
4
-
5
- export const RetryStrategy: unique symbol = Symbol("RetryStrategy");
@@ -1,95 +0,0 @@
1
- import {EntityManager, RequestContext} from "@mikro-orm/core";
2
- import {deepEqual, instance as originalInstance, mock, reset, spy, verify, when} from "ts-mockito";
3
-
4
- import {MikroOrmContext} from "./MikroOrmContext.js";
5
-
6
- const instance = <T extends object>(m: T): T =>
7
- new Proxy<T>(originalInstance(m), {
8
- get(target, prop, receiver) {
9
- if (["Symbol(Symbol.toPrimitive)", "Symbol(Symbol.isConcatSpreadable)", "length"].includes(prop.toString())) {
10
- return undefined;
11
- }
12
- return Reflect.get(target, prop, receiver);
13
- }
14
- });
15
-
16
- describe("MikroOrmContext", () => {
17
- const mockedEntityManager = mock<EntityManager>();
18
-
19
- let spiedRequestContext: typeof RequestContext;
20
- let entityManager!: EntityManager;
21
- let context!: MikroOrmContext;
22
-
23
- beforeEach(() => {
24
- spiedRequestContext = spy(RequestContext);
25
- entityManager = instance(mockedEntityManager);
26
- context = new MikroOrmContext();
27
- });
28
- afterEach(() => reset<typeof RequestContext | EntityManager>(mockedEntityManager, spiedRequestContext));
29
-
30
- describe("get", () => {
31
- it("should return undefined if not such context", () => {
32
- // act
33
- const result = context.get("unknown");
34
-
35
- // assert
36
- expect(result).toBeUndefined();
37
- });
38
-
39
- it("should return entity manager if context is initialized", () => {
40
- // arrange
41
- when(spiedRequestContext.getEntityManager("context1")).thenReturn(entityManager);
42
-
43
- // act
44
- const result = context.get("context1");
45
-
46
- // assert
47
- expect(result).toBe(entityManager);
48
- });
49
- });
50
-
51
- describe("wrap", () => {
52
- it("should run a function within a mikro-orm context", async () => {
53
- // arrange
54
- const task = vi.fn();
55
-
56
- // act
57
- await context.run([entityManager], task);
58
-
59
- // assert
60
- verify(spiedRequestContext.create(deepEqual([entityManager]), task)).once();
61
- });
62
-
63
- it("should return a result of function execution", async () => {
64
- // arrange
65
- const task = vi.fn().mockResolvedValue("string");
66
-
67
- // act
68
- const result = await context.run([entityManager], task);
69
-
70
- // assert
71
- expect(result).toEqual("string");
72
- });
73
- });
74
-
75
- describe("has", () => {
76
- it("should return false if not such context", () => {
77
- // act
78
- const result = context.has("unknown");
79
-
80
- // assert
81
- expect(result).toBeFalsy();
82
- });
83
-
84
- it("should return true if context is initialized", () => {
85
- // arrange
86
- when(spiedRequestContext.getEntityManager("context1")).thenReturn(entityManager);
87
-
88
- // act
89
- const result = context.has("context1");
90
-
91
- // assert
92
- expect(result).toBe(true);
93
- });
94
- });
95
- });