@tsed/mikro-orm 6.96.0 → 6.97.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.
- package/jest.config.js +3 -3
- package/lib/cjs/MikroOrmModule.js +2 -0
- package/lib/cjs/MikroOrmModule.js.map +1 -1
- package/lib/cjs/decorators/entityManager.d.ts +14 -0
- package/lib/cjs/decorators/entityManager.js +21 -0
- package/lib/cjs/decorators/entityManager.js.map +1 -0
- package/lib/cjs/decorators/index.d.ts +1 -0
- package/lib/cjs/decorators/index.js +1 -0
- package/lib/cjs/decorators/index.js.map +1 -1
- package/lib/cjs/interceptors/TransactionalInterceptor.d.ts +5 -2
- package/lib/cjs/interceptors/TransactionalInterceptor.js +19 -6
- package/lib/cjs/interceptors/TransactionalInterceptor.js.map +1 -1
- package/lib/cjs/services/MikroOrmRegistry.d.ts +3 -3
- package/lib/cjs/services/MikroOrmRegistry.js +7 -6
- package/lib/cjs/services/MikroOrmRegistry.js.map +1 -1
- package/lib/cjs/services/RetryStrategy.d.ts +1 -0
- package/lib/cjs/services/RetryStrategy.js +2 -0
- package/lib/cjs/services/RetryStrategy.js.map +1 -1
- package/lib/esm/MikroOrmModule.js +4 -2
- package/lib/esm/MikroOrmModule.js.map +1 -1
- package/lib/esm/decorators/entityManager.js +17 -0
- package/lib/esm/decorators/entityManager.js.map +1 -0
- package/lib/esm/decorators/index.js +1 -0
- package/lib/esm/decorators/index.js.map +1 -1
- package/lib/esm/interceptors/TransactionalInterceptor.js +20 -7
- package/lib/esm/interceptors/TransactionalInterceptor.js.map +1 -1
- package/lib/esm/services/MikroOrmRegistry.js +8 -7
- package/lib/esm/services/MikroOrmRegistry.js.map +1 -1
- package/lib/esm/services/RetryStrategy.js +1 -1
- package/lib/esm/services/RetryStrategy.js.map +1 -1
- package/lib/types/decorators/entityManager.d.ts +14 -0
- package/lib/types/decorators/index.d.ts +1 -0
- package/lib/types/interceptors/TransactionalInterceptor.d.ts +5 -2
- package/lib/types/services/MikroOrmRegistry.d.ts +3 -3
- package/lib/types/services/RetryStrategy.d.ts +1 -0
- package/package.json +12 -11
- package/readme.md +172 -12
package/jest.config.js
CHANGED
|
@@ -5,10 +5,10 @@ module.exports = {
|
|
|
5
5
|
...require("@tsed/jest-config")(__dirname, "mikro-orm"),
|
|
6
6
|
coverageThreshold: {
|
|
7
7
|
global: {
|
|
8
|
-
branches:
|
|
8
|
+
branches: 90.91,
|
|
9
9
|
functions: 100,
|
|
10
|
-
lines:
|
|
11
|
-
statements: 99.
|
|
10
|
+
lines: 99.06,
|
|
11
|
+
statements: 99.21
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
};
|
|
@@ -28,4 +28,6 @@ MikroOrmModule = (0, tslib_1.__decorate)([
|
|
|
28
28
|
})
|
|
29
29
|
], MikroOrmModule);
|
|
30
30
|
exports.MikroOrmModule = MikroOrmModule;
|
|
31
|
+
// TODO: the IoC container should provide null or undefined by default until tsedio/tsed#1694 is closed
|
|
32
|
+
(0, di_1.registerProvider)({ provide: services_1.RetryStrategy, useValue: null });
|
|
31
33
|
//# sourceMappingURL=MikroOrmModule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MikroOrmModule.js","sourceRoot":"","sources":["../../src/MikroOrmModule.ts"],"names":[],"mappings":";;;;AAAA,
|
|
1
|
+
{"version":3,"file":"MikroOrmModule.js","sourceRoot":"","sources":["../../src/MikroOrmModule.ts"],"names":[],"mappings":";;;;AAAA,yCAAsE;AACtE,iCAAsG;AAoBtG,IAAa,cAAc,GAA3B,MAAa,cAAc;IAOlB,KAAK,CAAC,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAE3F,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;IAClD,CAAC;CACF,CAAA;AAdC;IADC,IAAA,aAAQ,EAAC,UAAU,EAAE,EAAE,CAAC;;gDACa;AAGtC;IADC,IAAA,WAAM,GAAE;2CAC2B,2BAAgB;wDAAC;AAL1C,cAAc;IAJ1B,IAAA,WAAM,EAAC;QACN,OAAO,EAAE,CAAC,oBAAS,EAAE,2BAAgB,CAAC;QACtC,IAAI,EAAE,CAAC,kBAAa,EAAE,2BAAgB,CAAC;KACxC,CAAC;GACW,cAAc,CAgB1B;AAhBY,wCAAc;AAkB3B,uGAAuG;AACvG,IAAA,qBAAgB,EAAC,EAAC,OAAO,EAAE,wBAAa,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAC","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"./services\";\nimport {Configuration, Constant, Inject, Module, OnDestroy, OnInit, registerProvider} from \"@tsed/di\";\nimport {Options} from \"@mikro-orm/core\";\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace TsED {\n interface Configuration {\n /**\n * The the ORM configuration, entity metadata.\n * If you omit the `options` parameter, your CLI config will be used.\n */\n mikroOrm?: Options[];\n }\n }\n}\n\n@Module({\n imports: [DBContext, MikroOrmRegistry],\n deps: [Configuration, MikroOrmRegistry]\n})\nexport class MikroOrmModule implements OnDestroy, OnInit {\n @Constant(\"mikroOrm\", [])\n private readonly settings!: Options[];\n\n @Inject()\n private readonly mikroOrmRegistry!: MikroOrmRegistry;\n\n public async $onInit(): Promise<void> {\n const promises = this.settings.map((opts) => this.mikroOrmRegistry.createConnection(opts));\n\n await Promise.all(promises);\n }\n\n public $onDestroy(): Promise<void> {\n return this.mikroOrmRegistry.closeConnections();\n }\n}\n\n// TODO: the IoC container should provide null or undefined by default until tsedio/tsed#1694 is closed\nregisterProvider({provide: RetryStrategy, useValue: null});\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the entity manager for the given connection name.
|
|
3
|
+
* @param connectionName
|
|
4
|
+
* @decorator
|
|
5
|
+
* @mikroOrm
|
|
6
|
+
*/
|
|
7
|
+
export declare const EntityManager: (connectionName?: string | undefined) => PropertyDecorator;
|
|
8
|
+
/**
|
|
9
|
+
* Get the entity manager for the given connection name.
|
|
10
|
+
* @param connectionName
|
|
11
|
+
* @decorator
|
|
12
|
+
* @mikroOrm
|
|
13
|
+
*/
|
|
14
|
+
export declare const Em: (connectionName?: string | undefined) => PropertyDecorator;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Em = exports.EntityManager = void 0;
|
|
4
|
+
const di_1 = require("@tsed/di");
|
|
5
|
+
const services_1 = require("../services");
|
|
6
|
+
/**
|
|
7
|
+
* Get the entity manager for the given connection name.
|
|
8
|
+
* @param connectionName
|
|
9
|
+
* @decorator
|
|
10
|
+
* @mikroOrm
|
|
11
|
+
*/
|
|
12
|
+
const EntityManager = (connectionName) => (0, di_1.Inject)(services_1.MikroOrmRegistry, (registry) => { var _a; return (_a = registry.get(connectionName)) === null || _a === void 0 ? void 0 : _a.em; });
|
|
13
|
+
exports.EntityManager = EntityManager;
|
|
14
|
+
/**
|
|
15
|
+
* Get the entity manager for the given connection name.
|
|
16
|
+
* @param connectionName
|
|
17
|
+
* @decorator
|
|
18
|
+
* @mikroOrm
|
|
19
|
+
*/
|
|
20
|
+
exports.Em = exports.EntityManager;
|
|
21
|
+
//# sourceMappingURL=entityManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entityManager.js","sourceRoot":"","sources":["../../../src/decorators/entityManager.ts"],"names":[],"mappings":";;;AAAA,iCAAgC;AAChC,0CAA6C;AAE7C;;;;;GAKG;AACI,MAAM,aAAa,GAAG,CAAC,cAAuB,EAAqB,EAAE,CAC1E,IAAA,WAAM,EAAC,2BAAgB,EAAE,CAAC,QAA0B,EAAE,EAAE,WAAC,OAAA,MAAA,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,0CAAE,EAAE,CAAA,EAAA,CAAsB,CAAC;AADrG,QAAA,aAAa,iBACwF;AAElH;;;;;GAKG;AACU,QAAA,EAAE,GAAG,qBAAa,CAAC","sourcesContent":["import {Inject} from \"@tsed/di\";\nimport {MikroOrmRegistry} from \"../services\";\n\n/**\n * Get the entity manager for the given connection name.\n * @param connectionName\n * @decorator\n * @mikroOrm\n */\nexport const EntityManager = (connectionName?: string): PropertyDecorator =>\n Inject(MikroOrmRegistry, (registry: MikroOrmRegistry) => registry.get(connectionName)?.em) as PropertyDecorator;\n\n/**\n * Get the entity manager for the given connection name.\n * @param connectionName\n * @decorator\n * @mikroOrm\n */\nexport const Em = EntityManager;\n"]}
|
|
@@ -3,4 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
(0, tslib_1.__exportStar)(require("./transactional"), exports);
|
|
5
5
|
(0, tslib_1.__exportStar)(require("./connection"), exports);
|
|
6
|
+
(0, tslib_1.__exportStar)(require("./entityManager"), exports);
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":";;;AAAA,+DAAgC;AAChC,4DAA6B","sourcesContent":["export * from \"./transactional\";\nexport * from \"./connection\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":";;;AAAA,+DAAgC;AAChC,4DAA6B;AAC7B,+DAAgC","sourcesContent":["export * from \"./transactional\";\nexport * from \"./connection\";\nexport * from \"./entityManager\";\n"]}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { DBContext, MikroOrmRegistry, RetryStrategy } from "../services";
|
|
2
2
|
import { InterceptorContext, InterceptorMethods, InterceptorNext } from "@tsed/di";
|
|
3
|
+
import { Logger } from "@tsed/logger";
|
|
3
4
|
export interface TransactionOptions {
|
|
4
5
|
connectionName?: string;
|
|
5
|
-
|
|
6
|
+
retry?: boolean;
|
|
6
7
|
}
|
|
7
8
|
export declare class TransactionalInterceptor implements InterceptorMethods {
|
|
8
9
|
private readonly registry;
|
|
9
10
|
private readonly context;
|
|
10
|
-
|
|
11
|
+
private readonly logger;
|
|
12
|
+
private readonly retryStrategy?;
|
|
13
|
+
constructor(registry: MikroOrmRegistry, context: DBContext, logger: Logger, retryStrategy?: RetryStrategy | undefined);
|
|
11
14
|
intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown>;
|
|
12
15
|
private runWithinNewCtx;
|
|
13
16
|
private runUnderCtx;
|
|
@@ -4,13 +4,19 @@ exports.TransactionalInterceptor = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const services_1 = require("../services");
|
|
6
6
|
const di_1 = require("@tsed/di");
|
|
7
|
+
const logger_1 = require("@tsed/logger");
|
|
7
8
|
let TransactionalInterceptor = class TransactionalInterceptor {
|
|
8
|
-
constructor(registry, context) {
|
|
9
|
+
constructor(registry, context, logger, retryStrategy) {
|
|
9
10
|
this.registry = registry;
|
|
10
11
|
this.context = context;
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.retryStrategy = retryStrategy;
|
|
11
14
|
}
|
|
12
15
|
async intercept(context, next) {
|
|
13
16
|
const options = this.extractContextName(context);
|
|
17
|
+
if (options.retry && !this.retryStrategy) {
|
|
18
|
+
this.logger.warn(`To retry a transaction you have to implement a "${services_1.RetryStrategy.description}" interface`);
|
|
19
|
+
}
|
|
14
20
|
const ctx = this.context.getContext();
|
|
15
21
|
if (!ctx) {
|
|
16
22
|
return this.runWithinNewCtx(next, options);
|
|
@@ -29,23 +35,26 @@ let TransactionalInterceptor = class TransactionalInterceptor {
|
|
|
29
35
|
const em = orm.em.fork(true, true);
|
|
30
36
|
ctx.set(em.name, em);
|
|
31
37
|
const runInTransaction = () => this.executeInTransaction(options.connectionName, next);
|
|
32
|
-
return this.context.run(ctx, () => (options.
|
|
38
|
+
return this.context.run(ctx, () => { var _a; return (options.retry ? (_a = this.retryStrategy) === null || _a === void 0 ? void 0 : _a.acquire(runInTransaction) : runInTransaction()); });
|
|
33
39
|
}
|
|
34
40
|
extractContextName(context) {
|
|
35
41
|
const options = context.options || {};
|
|
36
42
|
let connectionName;
|
|
37
|
-
let
|
|
43
|
+
let retry;
|
|
38
44
|
if (typeof options === "string") {
|
|
39
45
|
connectionName = options;
|
|
40
46
|
}
|
|
41
47
|
else if (options) {
|
|
42
48
|
connectionName = options.connectionName;
|
|
43
|
-
|
|
49
|
+
retry = options.retry;
|
|
44
50
|
}
|
|
45
51
|
if (!connectionName) {
|
|
46
52
|
connectionName = "default";
|
|
47
53
|
}
|
|
48
|
-
|
|
54
|
+
if (!retry) {
|
|
55
|
+
retry = false;
|
|
56
|
+
}
|
|
57
|
+
return { connectionName, retry };
|
|
49
58
|
}
|
|
50
59
|
async executeInTransaction(connectionName, next) {
|
|
51
60
|
const em = this.context.get(connectionName);
|
|
@@ -58,7 +67,11 @@ TransactionalInterceptor = (0, tslib_1.__decorate)([
|
|
|
58
67
|
(0, di_1.Interceptor)(),
|
|
59
68
|
(0, tslib_1.__param)(0, (0, di_1.Inject)()),
|
|
60
69
|
(0, tslib_1.__param)(1, (0, di_1.Inject)()),
|
|
61
|
-
(0, tslib_1.
|
|
70
|
+
(0, tslib_1.__param)(2, (0, di_1.Inject)()),
|
|
71
|
+
(0, tslib_1.__param)(3, (0, di_1.Inject)(services_1.RetryStrategy)),
|
|
72
|
+
(0, tslib_1.__metadata)("design:paramtypes", [services_1.MikroOrmRegistry,
|
|
73
|
+
services_1.DBContext,
|
|
74
|
+
logger_1.Logger, Object])
|
|
62
75
|
], TransactionalInterceptor);
|
|
63
76
|
exports.TransactionalInterceptor = TransactionalInterceptor;
|
|
64
77
|
//# sourceMappingURL=TransactionalInterceptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionalInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/TransactionalInterceptor.ts"],"names":[],"mappings":";;;;AAAA,0CAAuE;AACvE,iCAAsG;
|
|
1
|
+
{"version":3,"file":"TransactionalInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/TransactionalInterceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"../services\";\nimport {Inject, Interceptor, InterceptorContext, InterceptorMethods, InterceptorNext} from \"@tsed/di\";\nimport {EntityManager} from \"@mikro-orm/core\";\nimport {Logger} from \"@tsed/logger\";\n\nexport interface TransactionOptions {\n connectionName?: string;\n retry?: boolean;\n}\n\n@Interceptor()\nexport class TransactionalInterceptor implements InterceptorMethods {\n constructor(\n @Inject() private readonly registry: MikroOrmRegistry,\n @Inject() private readonly context: DBContext,\n @Inject() private readonly logger: Logger,\n @Inject(RetryStrategy)\n private readonly retryStrategy?: RetryStrategy\n ) {}\n\n public async intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown> {\n const options = this.extractContextName(context);\n\n if (options.retry && !this.retryStrategy) {\n this.logger.warn(`To retry a transaction you have to implement a \"${RetryStrategy.description}\" interface`);\n }\n\n const ctx = this.context.getContext();\n\n if (!ctx) {\n return this.runWithinNewCtx(next, options);\n }\n\n if (ctx.has(options.connectionName!)) {\n return next();\n }\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runWithinNewCtx(next: InterceptorNext, options: TransactionOptions): Promise<unknown> | unknown {\n const ctx = new Map<string, EntityManager>();\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runUnderCtx(next: InterceptorNext, ctx: Map<string, EntityManager>, options: TransactionOptions): Promise<unknown> | unknown {\n const orm = this.registry.get(options.connectionName);\n const em = orm.em.fork(true, true);\n\n ctx.set(em.name, em);\n\n const runInTransaction = () => this.executeInTransaction(options.connectionName!, next);\n\n return this.context.run(ctx, () => (options.retry ? this.retryStrategy?.acquire(runInTransaction) : runInTransaction()));\n }\n\n private extractContextName(context: InterceptorContext<unknown>): TransactionOptions {\n const options = context.options || ({} as Partial<TransactionOptions> | string);\n\n let connectionName: string | undefined;\n let retry: boolean | undefined;\n\n if (typeof options === \"string\") {\n connectionName = options;\n } else if (options) {\n connectionName = options.connectionName;\n retry = options.retry;\n }\n\n if (!connectionName) {\n connectionName = \"default\";\n }\n\n if (!retry) {\n retry = false;\n }\n\n return {connectionName, retry};\n }\n\n private async executeInTransaction(connectionName: string, next: InterceptorNext): Promise<unknown> {\n const em = this.context.get(connectionName);\n\n const result = await next();\n\n await em?.flush();\n\n return result;\n }\n}\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { MikroOrmFactory } from "./MikroOrmFactory";
|
|
2
|
-
import { InjectorService } from "@tsed/di";
|
|
3
2
|
import { IDatabaseDriver as DatabaseDriver, MikroORM, Options } from "@mikro-orm/core";
|
|
3
|
+
import { Logger } from "@tsed/logger";
|
|
4
4
|
export declare class MikroOrmRegistry {
|
|
5
|
-
private readonly
|
|
5
|
+
private readonly logger;
|
|
6
6
|
private readonly mikroOrmFactory;
|
|
7
7
|
private readonly connections;
|
|
8
|
-
constructor(
|
|
8
|
+
constructor(logger: Logger, mikroOrmFactory: MikroOrmFactory);
|
|
9
9
|
createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM>;
|
|
10
10
|
get(id?: string): MikroORM;
|
|
11
11
|
has(id?: string): boolean;
|
|
@@ -5,9 +5,10 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const MikroOrmFactory_1 = require("./MikroOrmFactory");
|
|
6
6
|
const di_1 = require("@tsed/di");
|
|
7
7
|
const core_1 = require("@tsed/core");
|
|
8
|
+
const logger_1 = require("@tsed/logger");
|
|
8
9
|
let MikroOrmRegistry = class MikroOrmRegistry {
|
|
9
|
-
constructor(
|
|
10
|
-
this.
|
|
10
|
+
constructor(logger, mikroOrmFactory) {
|
|
11
|
+
this.logger = logger;
|
|
11
12
|
this.mikroOrmFactory = mikroOrmFactory;
|
|
12
13
|
this.connections = new Map();
|
|
13
14
|
}
|
|
@@ -16,11 +17,11 @@ let MikroOrmRegistry = class MikroOrmRegistry {
|
|
|
16
17
|
if (this.has(connectionName)) {
|
|
17
18
|
return this.get(connectionName);
|
|
18
19
|
}
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
20
|
+
this.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);
|
|
21
|
+
this.logger.debug(`options: %j`, connectionOptions);
|
|
21
22
|
const connection = await this.mikroOrmFactory.create(connectionOptions);
|
|
22
23
|
this.connections.set(connectionName, connection);
|
|
23
|
-
this.
|
|
24
|
+
this.logger.info(`Connected with MikroOrm to database: %s`, connectionName);
|
|
24
25
|
return connection;
|
|
25
26
|
}
|
|
26
27
|
get(id = "default") {
|
|
@@ -45,7 +46,7 @@ MikroOrmRegistry = (0, tslib_1.__decorate)([
|
|
|
45
46
|
(0, di_1.Injectable)(),
|
|
46
47
|
(0, tslib_1.__param)(0, (0, di_1.Inject)()),
|
|
47
48
|
(0, tslib_1.__param)(1, (0, di_1.Inject)()),
|
|
48
|
-
(0, tslib_1.__metadata)("design:paramtypes", [
|
|
49
|
+
(0, tslib_1.__metadata)("design:paramtypes", [logger_1.Logger, MikroOrmFactory_1.MikroOrmFactory])
|
|
49
50
|
], MikroOrmRegistry);
|
|
50
51
|
exports.MikroOrmRegistry = MikroOrmRegistry;
|
|
51
52
|
//# sourceMappingURL=MikroOrmRegistry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MikroOrmRegistry.js","sourceRoot":"","sources":["../../../src/services/MikroOrmRegistry.ts"],"names":[],"mappings":";;;;AAAA,uDAAkD;AAClD,
|
|
1
|
+
{"version":3,"file":"MikroOrmRegistry.js","sourceRoot":"","sources":["../../../src/services/MikroOrmRegistry.ts"],"names":[],"mappings":";;;;AAAA,uDAAkD;AAClD,iCAA4C;AAE5C,qCAAqD;AACrD,yCAAoC;AAGpC,IAAa,gBAAgB,GAA7B,MAAa,gBAAgB;IAG3B,YAAuC,MAAc,EAA6B,eAAgC;QAA3E,WAAM,GAAN,MAAM,CAAQ;QAA6B,oBAAe,GAAf,eAAe,CAAiB;QAFjG,gBAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE0D,CAAC;IAE/G,KAAK,CAAC,gBAAgB,CAA2B,iBAA6B;QACnF,MAAM,cAAc,GAAG,IAAA,eAAQ,EAAS,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAErF,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;SACjC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iDAAiD,EAAE,cAAc,CAAC,CAAC;QACpF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;QAEpD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAExE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAEjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,EAAE,cAAc,CAAC,CAAC;QAE5E,OAAO,UAAU,CAAC;IACpB,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,oEAAoE;QACpE,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACnC,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,gBAAgB;QAC3B,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QAEnD,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAoB,EAAE,EAAE,CAAC,IAAA,sBAAe,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9G,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,UAAoB;QACxC,IAAI,MAAM,UAAU,CAAC,WAAW,EAAE,EAAE;YAClC,MAAM,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAC/B;IACH,CAAC;CACF,CAAA;AA9CY,gBAAgB;IAD5B,IAAA,eAAU,GAAE;IAIE,wBAAA,IAAA,WAAM,GAAE,CAAA;IAAmC,wBAAA,IAAA,WAAM,GAAE,CAAA;kDAAjB,eAAM,EAA8C,iCAAe;GAHvG,gBAAgB,CA8C5B;AA9CY,4CAAgB","sourcesContent":["import {MikroOrmFactory} from \"./MikroOrmFactory\";\nimport {Inject, Injectable} from \"@tsed/di\";\nimport {IDatabaseDriver as DatabaseDriver, MikroORM, Options} from \"@mikro-orm/core\";\nimport {catchAsyncError, getValue} from \"@tsed/core\";\nimport {Logger} from \"@tsed/logger\";\n\n@Injectable()\nexport class MikroOrmRegistry {\n private readonly connections = new Map<string, MikroORM>();\n\n constructor(@Inject() private readonly logger: Logger, @Inject() private readonly mikroOrmFactory: MikroOrmFactory) {}\n\n public async createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM> {\n const connectionName = getValue<string>(connectionOptions, \"contextName\", \"default\");\n\n if (this.has(connectionName)) {\n return this.get(connectionName);\n }\n\n this.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);\n this.logger.debug(`options: %j`, connectionOptions);\n\n const connection = await this.mikroOrmFactory.create(connectionOptions);\n\n this.connections.set(connectionName, connection);\n\n this.logger.info(`Connected with MikroOrm to database: %s`, connectionName);\n\n return connection;\n }\n\n public get(id: string = \"default\"): MikroORM {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.connections.get(id)!;\n }\n\n public has(id: string = \"default\"): boolean {\n return this.connections.has(id);\n }\n\n public async closeConnections(): Promise<void> {\n const connections = [...this.connections.values()];\n\n await Promise.all(connections.map((connection: MikroORM) => catchAsyncError(() => this.dispose(connection))));\n\n this.connections.clear();\n }\n\n private async dispose(connection: MikroORM): Promise<void> {\n if (await connection.isConnected()) {\n await connection.close(false);\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RetryStrategy.js","sourceRoot":"","sources":["../../../src/services/RetryStrategy.ts"],"names":[],"mappings":"","sourcesContent":["export interface RetryStrategy {\n acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"RetryStrategy.js","sourceRoot":"","sources":["../../../src/services/RetryStrategy.ts"],"names":[],"mappings":";;;AAIa,QAAA,aAAa,GAAkB,MAAM,CAAC,eAAe,CAAC,CAAC","sourcesContent":["export interface RetryStrategy {\n acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;\n}\n\nexport const RetryStrategy: unique symbol = Symbol(\"RetryStrategy\");\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import { DBContext, MikroOrmRegistry } from "./services";
|
|
3
|
-
import { Configuration, Constant, Inject, Module } from "@tsed/di";
|
|
2
|
+
import { DBContext, MikroOrmRegistry, RetryStrategy } from "./services";
|
|
3
|
+
import { Configuration, Constant, Inject, Module, registerProvider } from "@tsed/di";
|
|
4
4
|
let MikroOrmModule = class MikroOrmModule {
|
|
5
5
|
async $onInit() {
|
|
6
6
|
const promises = this.settings.map((opts) => this.mikroOrmRegistry.createConnection(opts));
|
|
@@ -25,4 +25,6 @@ MikroOrmModule = __decorate([
|
|
|
25
25
|
})
|
|
26
26
|
], MikroOrmModule);
|
|
27
27
|
export { MikroOrmModule };
|
|
28
|
+
// TODO: the IoC container should provide null or undefined by default until tsedio/tsed#1694 is closed
|
|
29
|
+
registerProvider({ provide: RetryStrategy, useValue: null });
|
|
28
30
|
//# sourceMappingURL=MikroOrmModule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MikroOrmModule.js","sourceRoot":"","sources":["../../src/MikroOrmModule.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,SAAS,EAAE,gBAAgB,EAAC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"MikroOrmModule.js","sourceRoot":"","sources":["../../src/MikroOrmModule.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AACtE,OAAO,EAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAqB,gBAAgB,EAAC,MAAM,UAAU,CAAC;AAoBtG,IAAa,cAAc,GAA3B,MAAa,cAAc;IAOlB,KAAK,CAAC,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAE3F,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;IAClD,CAAC;CACF,CAAA;AAdC;IADC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC;;gDACa;AAGtC;IADC,MAAM,EAAE;8BAC2B,gBAAgB;wDAAC;AAL1C,cAAc;IAJ1B,MAAM,CAAC;QACN,OAAO,EAAE,CAAC,SAAS,EAAE,gBAAgB,CAAC;QACtC,IAAI,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC;KACxC,CAAC;GACW,cAAc,CAgB1B;SAhBY,cAAc;AAkB3B,uGAAuG;AACvG,gBAAgB,CAAC,EAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAC","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"./services\";\nimport {Configuration, Constant, Inject, Module, OnDestroy, OnInit, registerProvider} from \"@tsed/di\";\nimport {Options} from \"@mikro-orm/core\";\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace TsED {\n interface Configuration {\n /**\n * The the ORM configuration, entity metadata.\n * If you omit the `options` parameter, your CLI config will be used.\n */\n mikroOrm?: Options[];\n }\n }\n}\n\n@Module({\n imports: [DBContext, MikroOrmRegistry],\n deps: [Configuration, MikroOrmRegistry]\n})\nexport class MikroOrmModule implements OnDestroy, OnInit {\n @Constant(\"mikroOrm\", [])\n private readonly settings!: Options[];\n\n @Inject()\n private readonly mikroOrmRegistry!: MikroOrmRegistry;\n\n public async $onInit(): Promise<void> {\n const promises = this.settings.map((opts) => this.mikroOrmRegistry.createConnection(opts));\n\n await Promise.all(promises);\n }\n\n public $onDestroy(): Promise<void> {\n return this.mikroOrmRegistry.closeConnections();\n }\n}\n\n// TODO: the IoC container should provide null or undefined by default until tsedio/tsed#1694 is closed\nregisterProvider({provide: RetryStrategy, useValue: null});\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Inject } from "@tsed/di";
|
|
2
|
+
import { MikroOrmRegistry } from "../services";
|
|
3
|
+
/**
|
|
4
|
+
* Get the entity manager for the given connection name.
|
|
5
|
+
* @param connectionName
|
|
6
|
+
* @decorator
|
|
7
|
+
* @mikroOrm
|
|
8
|
+
*/
|
|
9
|
+
export const EntityManager = (connectionName) => Inject(MikroOrmRegistry, (registry) => { var _a; return (_a = registry.get(connectionName)) === null || _a === void 0 ? void 0 : _a.em; });
|
|
10
|
+
/**
|
|
11
|
+
* Get the entity manager for the given connection name.
|
|
12
|
+
* @param connectionName
|
|
13
|
+
* @decorator
|
|
14
|
+
* @mikroOrm
|
|
15
|
+
*/
|
|
16
|
+
export const Em = EntityManager;
|
|
17
|
+
//# sourceMappingURL=entityManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entityManager.js","sourceRoot":"","sources":["../../../src/decorators/entityManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,OAAO,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,cAAuB,EAAqB,EAAE,CAC1E,MAAM,CAAC,gBAAgB,EAAE,CAAC,QAA0B,EAAE,EAAE,WAAC,OAAA,MAAA,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,0CAAE,EAAE,CAAA,EAAA,CAAsB,CAAC;AAElH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,EAAE,GAAG,aAAa,CAAC","sourcesContent":["import {Inject} from \"@tsed/di\";\nimport {MikroOrmRegistry} from \"../services\";\n\n/**\n * Get the entity manager for the given connection name.\n * @param connectionName\n * @decorator\n * @mikroOrm\n */\nexport const EntityManager = (connectionName?: string): PropertyDecorator =>\n Inject(MikroOrmRegistry, (registry: MikroOrmRegistry) => registry.get(connectionName)?.em) as PropertyDecorator;\n\n/**\n * Get the entity manager for the given connection name.\n * @param connectionName\n * @decorator\n * @mikroOrm\n */\nexport const Em = EntityManager;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC","sourcesContent":["export * from \"./transactional\";\nexport * from \"./connection\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC","sourcesContent":["export * from \"./transactional\";\nexport * from \"./connection\";\nexport * from \"./entityManager\";\n"]}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { __decorate, __metadata, __param } from "tslib";
|
|
2
|
-
import { DBContext, MikroOrmRegistry } from "../services";
|
|
2
|
+
import { DBContext, MikroOrmRegistry, RetryStrategy } from "../services";
|
|
3
3
|
import { Inject, Interceptor } from "@tsed/di";
|
|
4
|
+
import { Logger } from "@tsed/logger";
|
|
4
5
|
let TransactionalInterceptor = class TransactionalInterceptor {
|
|
5
|
-
constructor(registry, context) {
|
|
6
|
+
constructor(registry, context, logger, retryStrategy) {
|
|
6
7
|
this.registry = registry;
|
|
7
8
|
this.context = context;
|
|
9
|
+
this.logger = logger;
|
|
10
|
+
this.retryStrategy = retryStrategy;
|
|
8
11
|
}
|
|
9
12
|
async intercept(context, next) {
|
|
10
13
|
const options = this.extractContextName(context);
|
|
14
|
+
if (options.retry && !this.retryStrategy) {
|
|
15
|
+
this.logger.warn(`To retry a transaction you have to implement a "${RetryStrategy.description}" interface`);
|
|
16
|
+
}
|
|
11
17
|
const ctx = this.context.getContext();
|
|
12
18
|
if (!ctx) {
|
|
13
19
|
return this.runWithinNewCtx(next, options);
|
|
@@ -26,23 +32,26 @@ let TransactionalInterceptor = class TransactionalInterceptor {
|
|
|
26
32
|
const em = orm.em.fork(true, true);
|
|
27
33
|
ctx.set(em.name, em);
|
|
28
34
|
const runInTransaction = () => this.executeInTransaction(options.connectionName, next);
|
|
29
|
-
return this.context.run(ctx, () => (options.
|
|
35
|
+
return this.context.run(ctx, () => { var _a; return (options.retry ? (_a = this.retryStrategy) === null || _a === void 0 ? void 0 : _a.acquire(runInTransaction) : runInTransaction()); });
|
|
30
36
|
}
|
|
31
37
|
extractContextName(context) {
|
|
32
38
|
const options = context.options || {};
|
|
33
39
|
let connectionName;
|
|
34
|
-
let
|
|
40
|
+
let retry;
|
|
35
41
|
if (typeof options === "string") {
|
|
36
42
|
connectionName = options;
|
|
37
43
|
}
|
|
38
44
|
else if (options) {
|
|
39
45
|
connectionName = options.connectionName;
|
|
40
|
-
|
|
46
|
+
retry = options.retry;
|
|
41
47
|
}
|
|
42
48
|
if (!connectionName) {
|
|
43
49
|
connectionName = "default";
|
|
44
50
|
}
|
|
45
|
-
|
|
51
|
+
if (!retry) {
|
|
52
|
+
retry = false;
|
|
53
|
+
}
|
|
54
|
+
return { connectionName, retry };
|
|
46
55
|
}
|
|
47
56
|
async executeInTransaction(connectionName, next) {
|
|
48
57
|
const em = this.context.get(connectionName);
|
|
@@ -55,7 +64,11 @@ TransactionalInterceptor = __decorate([
|
|
|
55
64
|
Interceptor(),
|
|
56
65
|
__param(0, Inject()),
|
|
57
66
|
__param(1, Inject()),
|
|
58
|
-
|
|
67
|
+
__param(2, Inject()),
|
|
68
|
+
__param(3, Inject(RetryStrategy)),
|
|
69
|
+
__metadata("design:paramtypes", [MikroOrmRegistry,
|
|
70
|
+
DBContext,
|
|
71
|
+
Logger, Object])
|
|
59
72
|
], TransactionalInterceptor);
|
|
60
73
|
export { TransactionalInterceptor };
|
|
61
74
|
//# sourceMappingURL=TransactionalInterceptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionalInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/TransactionalInterceptor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,SAAS,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"TransactionalInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/TransactionalInterceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"../services\";\nimport {Inject, Interceptor, InterceptorContext, InterceptorMethods, InterceptorNext} from \"@tsed/di\";\nimport {EntityManager} from \"@mikro-orm/core\";\nimport {Logger} from \"@tsed/logger\";\n\nexport interface TransactionOptions {\n connectionName?: string;\n retry?: boolean;\n}\n\n@Interceptor()\nexport class TransactionalInterceptor implements InterceptorMethods {\n constructor(\n @Inject() private readonly registry: MikroOrmRegistry,\n @Inject() private readonly context: DBContext,\n @Inject() private readonly logger: Logger,\n @Inject(RetryStrategy)\n private readonly retryStrategy?: RetryStrategy\n ) {}\n\n public async intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown> {\n const options = this.extractContextName(context);\n\n if (options.retry && !this.retryStrategy) {\n this.logger.warn(`To retry a transaction you have to implement a \"${RetryStrategy.description}\" interface`);\n }\n\n const ctx = this.context.getContext();\n\n if (!ctx) {\n return this.runWithinNewCtx(next, options);\n }\n\n if (ctx.has(options.connectionName!)) {\n return next();\n }\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runWithinNewCtx(next: InterceptorNext, options: TransactionOptions): Promise<unknown> | unknown {\n const ctx = new Map<string, EntityManager>();\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runUnderCtx(next: InterceptorNext, ctx: Map<string, EntityManager>, options: TransactionOptions): Promise<unknown> | unknown {\n const orm = this.registry.get(options.connectionName);\n const em = orm.em.fork(true, true);\n\n ctx.set(em.name, em);\n\n const runInTransaction = () => this.executeInTransaction(options.connectionName!, next);\n\n return this.context.run(ctx, () => (options.retry ? this.retryStrategy?.acquire(runInTransaction) : runInTransaction()));\n }\n\n private extractContextName(context: InterceptorContext<unknown>): TransactionOptions {\n const options = context.options || ({} as Partial<TransactionOptions> | string);\n\n let connectionName: string | undefined;\n let retry: boolean | undefined;\n\n if (typeof options === \"string\") {\n connectionName = options;\n } else if (options) {\n connectionName = options.connectionName;\n retry = options.retry;\n }\n\n if (!connectionName) {\n connectionName = \"default\";\n }\n\n if (!retry) {\n retry = false;\n }\n\n return {connectionName, retry};\n }\n\n private async executeInTransaction(connectionName: string, next: InterceptorNext): Promise<unknown> {\n const em = this.context.get(connectionName);\n\n const result = await next();\n\n await em?.flush();\n\n return result;\n }\n}\n"]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { __decorate, __metadata, __param } from "tslib";
|
|
2
2
|
import { MikroOrmFactory } from "./MikroOrmFactory";
|
|
3
|
-
import { Inject, Injectable
|
|
3
|
+
import { Inject, Injectable } from "@tsed/di";
|
|
4
4
|
import { catchAsyncError, getValue } from "@tsed/core";
|
|
5
|
+
import { Logger } from "@tsed/logger";
|
|
5
6
|
let MikroOrmRegistry = class MikroOrmRegistry {
|
|
6
|
-
constructor(
|
|
7
|
-
this.
|
|
7
|
+
constructor(logger, mikroOrmFactory) {
|
|
8
|
+
this.logger = logger;
|
|
8
9
|
this.mikroOrmFactory = mikroOrmFactory;
|
|
9
10
|
this.connections = new Map();
|
|
10
11
|
}
|
|
@@ -13,11 +14,11 @@ let MikroOrmRegistry = class MikroOrmRegistry {
|
|
|
13
14
|
if (this.has(connectionName)) {
|
|
14
15
|
return this.get(connectionName);
|
|
15
16
|
}
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
17
|
+
this.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);
|
|
18
|
+
this.logger.debug(`options: %j`, connectionOptions);
|
|
18
19
|
const connection = await this.mikroOrmFactory.create(connectionOptions);
|
|
19
20
|
this.connections.set(connectionName, connection);
|
|
20
|
-
this.
|
|
21
|
+
this.logger.info(`Connected with MikroOrm to database: %s`, connectionName);
|
|
21
22
|
return connection;
|
|
22
23
|
}
|
|
23
24
|
get(id = "default") {
|
|
@@ -42,7 +43,7 @@ MikroOrmRegistry = __decorate([
|
|
|
42
43
|
Injectable(),
|
|
43
44
|
__param(0, Inject()),
|
|
44
45
|
__param(1, Inject()),
|
|
45
|
-
__metadata("design:paramtypes", [
|
|
46
|
+
__metadata("design:paramtypes", [Logger, MikroOrmFactory])
|
|
46
47
|
], MikroOrmRegistry);
|
|
47
48
|
export { MikroOrmRegistry };
|
|
48
49
|
//# sourceMappingURL=MikroOrmRegistry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MikroOrmRegistry.js","sourceRoot":"","sources":["../../../src/services/MikroOrmRegistry.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAC,MAAM,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"MikroOrmRegistry.js","sourceRoot":"","sources":["../../../src/services/MikroOrmRegistry.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAC,MAAM,EAAE,UAAU,EAAC,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,YAAY,CAAC;AACrD,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AAGpC,IAAa,gBAAgB,GAA7B,MAAa,gBAAgB;IAG3B,YAAuC,MAAc,EAA6B,eAAgC;QAA3E,WAAM,GAAN,MAAM,CAAQ;QAA6B,oBAAe,GAAf,eAAe,CAAiB;QAFjG,gBAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE0D,CAAC;IAE/G,KAAK,CAAC,gBAAgB,CAA2B,iBAA6B;QACnF,MAAM,cAAc,GAAG,QAAQ,CAAS,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAErF,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;SACjC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iDAAiD,EAAE,cAAc,CAAC,CAAC;QACpF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;QAEpD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAExE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAEjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,EAAE,cAAc,CAAC,CAAC;QAE5E,OAAO,UAAU,CAAC;IACpB,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,oEAAoE;QACpE,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACnC,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,gBAAgB;QAC3B,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QAEnD,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAoB,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9G,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,UAAoB;QACxC,IAAI,MAAM,UAAU,CAAC,WAAW,EAAE,EAAE;YAClC,MAAM,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAC/B;IACH,CAAC;CACF,CAAA;AA9CY,gBAAgB;IAD5B,UAAU,EAAE;IAIE,WAAA,MAAM,EAAE,CAAA;IAAmC,WAAA,MAAM,EAAE,CAAA;qCAAjB,MAAM,EAA8C,eAAe;GAHvG,gBAAgB,CA8C5B;SA9CY,gBAAgB","sourcesContent":["import {MikroOrmFactory} from \"./MikroOrmFactory\";\nimport {Inject, Injectable} from \"@tsed/di\";\nimport {IDatabaseDriver as DatabaseDriver, MikroORM, Options} from \"@mikro-orm/core\";\nimport {catchAsyncError, getValue} from \"@tsed/core\";\nimport {Logger} from \"@tsed/logger\";\n\n@Injectable()\nexport class MikroOrmRegistry {\n private readonly connections = new Map<string, MikroORM>();\n\n constructor(@Inject() private readonly logger: Logger, @Inject() private readonly mikroOrmFactory: MikroOrmFactory) {}\n\n public async createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM> {\n const connectionName = getValue<string>(connectionOptions, \"contextName\", \"default\");\n\n if (this.has(connectionName)) {\n return this.get(connectionName);\n }\n\n this.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);\n this.logger.debug(`options: %j`, connectionOptions);\n\n const connection = await this.mikroOrmFactory.create(connectionOptions);\n\n this.connections.set(connectionName, connection);\n\n this.logger.info(`Connected with MikroOrm to database: %s`, connectionName);\n\n return connection;\n }\n\n public get(id: string = \"default\"): MikroORM {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.connections.get(id)!;\n }\n\n public has(id: string = \"default\"): boolean {\n return this.connections.has(id);\n }\n\n public async closeConnections(): Promise<void> {\n const connections = [...this.connections.values()];\n\n await Promise.all(connections.map((connection: MikroORM) => catchAsyncError(() => this.dispose(connection))));\n\n this.connections.clear();\n }\n\n private async dispose(connection: MikroORM): Promise<void> {\n if (await connection.isConnected()) {\n await connection.close(false);\n }\n }\n}\n"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const RetryStrategy = Symbol("RetryStrategy");
|
|
2
2
|
//# sourceMappingURL=RetryStrategy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RetryStrategy.js","sourceRoot":"","sources":["../../../src/services/RetryStrategy.ts"],"names":[],"mappings":"","sourcesContent":["export interface RetryStrategy {\n acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"RetryStrategy.js","sourceRoot":"","sources":["../../../src/services/RetryStrategy.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,aAAa,GAAkB,MAAM,CAAC,eAAe,CAAC,CAAC","sourcesContent":["export interface RetryStrategy {\n acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;\n}\n\nexport const RetryStrategy: unique symbol = Symbol(\"RetryStrategy\");\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the entity manager for the given connection name.
|
|
3
|
+
* @param connectionName
|
|
4
|
+
* @decorator
|
|
5
|
+
* @mikroOrm
|
|
6
|
+
*/
|
|
7
|
+
export declare const EntityManager: (connectionName?: string | undefined) => PropertyDecorator;
|
|
8
|
+
/**
|
|
9
|
+
* Get the entity manager for the given connection name.
|
|
10
|
+
* @param connectionName
|
|
11
|
+
* @decorator
|
|
12
|
+
* @mikroOrm
|
|
13
|
+
*/
|
|
14
|
+
export declare const Em: (connectionName?: string | undefined) => PropertyDecorator;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { DBContext, MikroOrmRegistry, RetryStrategy } from "../services";
|
|
2
2
|
import { InterceptorContext, InterceptorMethods, InterceptorNext } from "@tsed/di";
|
|
3
|
+
import { Logger } from "@tsed/logger";
|
|
3
4
|
export interface TransactionOptions {
|
|
4
5
|
connectionName?: string;
|
|
5
|
-
|
|
6
|
+
retry?: boolean;
|
|
6
7
|
}
|
|
7
8
|
export declare class TransactionalInterceptor implements InterceptorMethods {
|
|
8
9
|
private readonly registry;
|
|
9
10
|
private readonly context;
|
|
10
|
-
|
|
11
|
+
private readonly logger;
|
|
12
|
+
private readonly retryStrategy?;
|
|
13
|
+
constructor(registry: MikroOrmRegistry, context: DBContext, logger: Logger, retryStrategy?: RetryStrategy | undefined);
|
|
11
14
|
intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown>;
|
|
12
15
|
private runWithinNewCtx;
|
|
13
16
|
private runUnderCtx;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { MikroOrmFactory } from "./MikroOrmFactory";
|
|
2
|
-
import { InjectorService } from "@tsed/di";
|
|
3
2
|
import { IDatabaseDriver as DatabaseDriver, MikroORM, Options } from "@mikro-orm/core";
|
|
3
|
+
import { Logger } from "@tsed/logger";
|
|
4
4
|
export declare class MikroOrmRegistry {
|
|
5
|
-
private readonly
|
|
5
|
+
private readonly logger;
|
|
6
6
|
private readonly mikroOrmFactory;
|
|
7
7
|
private readonly connections;
|
|
8
|
-
constructor(
|
|
8
|
+
constructor(logger: Logger, mikroOrmFactory: MikroOrmFactory);
|
|
9
9
|
createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM>;
|
|
10
10
|
get(id?: string): MikroORM;
|
|
11
11
|
has(id?: string): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsed/mikro-orm",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.97.1",
|
|
4
4
|
"description": "MikroORM package for Ts.ED framework",
|
|
5
5
|
"private": false,
|
|
6
6
|
"source": "./src/index.ts",
|
|
@@ -24,20 +24,21 @@
|
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@mikro-orm/core": "^4.5.9",
|
|
26
26
|
"@mikro-orm/mongodb": "^4.5.9",
|
|
27
|
-
"@tsed/common": "6.
|
|
28
|
-
"@tsed/core": "6.
|
|
29
|
-
"@tsed/di": "6.
|
|
30
|
-
"@tsed/json-mapper": "6.
|
|
31
|
-
"@tsed/
|
|
27
|
+
"@tsed/common": "6.97.1",
|
|
28
|
+
"@tsed/core": "6.97.1",
|
|
29
|
+
"@tsed/di": "6.97.1",
|
|
30
|
+
"@tsed/json-mapper": "6.97.1",
|
|
31
|
+
"@tsed/logger": "^6.0.0",
|
|
32
|
+
"@tsed/schema": "6.97.1",
|
|
33
|
+
"@tsed/testing-mongoose": "6.97.1",
|
|
32
34
|
"ts-mockito": "^2.6.1"
|
|
33
35
|
},
|
|
34
36
|
"peerDependencies": {
|
|
35
37
|
"@mikro-orm/core": ">=4.5.9",
|
|
36
|
-
"@tsed/common": "^6.
|
|
37
|
-
"@tsed/core": "^6.
|
|
38
|
-
"@tsed/di": "^6.
|
|
39
|
-
"@tsed/
|
|
40
|
-
"@tsed/schema": "^6.96.0"
|
|
38
|
+
"@tsed/common": "^6.97.1",
|
|
39
|
+
"@tsed/core": "^6.97.1",
|
|
40
|
+
"@tsed/di": "^6.97.1",
|
|
41
|
+
"@tsed/logger": "^6.0.0"
|
|
41
42
|
},
|
|
42
43
|
"repository": "https://github.com/tsedio/tsed",
|
|
43
44
|
"bugs": {
|
package/readme.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
|
-
<h1>
|
|
6
|
+
<h1>MikroOrm</h1>
|
|
7
7
|
|
|
8
8
|
[](https://github.com/tsedio/tsed/actions?query=workflow%3A%22Build+%26+Release%22)
|
|
9
9
|
[](https://github.com/tsedio/tsed/blob/master/CONTRIBUTING.md)
|
|
@@ -33,7 +33,7 @@ A package of Ts.ED framework. See website: https://tsed.io/tutorials/mikro-orm
|
|
|
33
33
|
|
|
34
34
|
Currently, `@tsed/mikro-orm` allows you:
|
|
35
35
|
|
|
36
|
-
- Configure one or more MikroOrm connections via the `@
|
|
36
|
+
- Configure one or more MikroOrm connections via the `@Configuration` decorator. All databases will be initialized
|
|
37
37
|
when the server starts during the server's `OnInit` phase.
|
|
38
38
|
- Use the Entity MikroOrm as Model for Controllers, AJV Validation and Swagger.
|
|
39
39
|
|
|
@@ -41,15 +41,35 @@ Currently, `@tsed/mikro-orm` allows you:
|
|
|
41
41
|
|
|
42
42
|
To begin, install the MikroOrm module for TS.ED:
|
|
43
43
|
|
|
44
|
+
<Tabs class="-code">
|
|
45
|
+
<Tab label="NPM">
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/mongodb # for mongo
|
|
49
|
+
npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/mysql # for mysql/mariadb
|
|
50
|
+
npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/mariadb # for mysql/mariadb
|
|
51
|
+
npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/postgresql # for postgresql
|
|
52
|
+
npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/sqlite # for sqlite
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
</Tab>
|
|
56
|
+
<Tab label="Yarn">
|
|
57
|
+
|
|
44
58
|
```bash
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/mongodb # for mongo
|
|
60
|
+
yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/mysql # for mysql/mariadb
|
|
61
|
+
yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/mariadb # for mysql/mariadb
|
|
62
|
+
yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/postgresql # for postgresql
|
|
63
|
+
yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/sqlite # for sqlite
|
|
47
64
|
```
|
|
48
65
|
|
|
49
|
-
|
|
66
|
+
</Tab>
|
|
67
|
+
</Tabs>
|
|
68
|
+
|
|
69
|
+
Once the installation process is completed, we can import the `MikroOrmModule` into the `Server` configuration:
|
|
50
70
|
|
|
51
71
|
```typescript
|
|
52
|
-
import {Configuration} from "@tsed/
|
|
72
|
+
import {Configuration} from "@tsed/di";
|
|
53
73
|
import {MikroOrmModule} from "@tsed/mikro-orm";
|
|
54
74
|
|
|
55
75
|
@Configuration({
|
|
@@ -71,16 +91,14 @@ import {MikroOrmModule} from "@tsed/mikro-orm";
|
|
|
71
91
|
}
|
|
72
92
|
]
|
|
73
93
|
})
|
|
74
|
-
export class Server {
|
|
75
|
-
|
|
76
|
-
}
|
|
94
|
+
export class Server {}
|
|
77
95
|
```
|
|
78
96
|
|
|
79
|
-
|
|
97
|
+
The `mikroOrm` options accepts the same configuration object as `init()` from the MikroORM package. Check [this page](https://mikro-orm.io/docs/configuration) for the complete configuration documentation.
|
|
80
98
|
|
|
81
99
|
## Obtain a connection
|
|
82
100
|
|
|
83
|
-
|
|
101
|
+
`@Connection` decorator lets you retrieve an instance of MikroOrm Connection.
|
|
84
102
|
|
|
85
103
|
```typescript
|
|
86
104
|
import {Injectable, AfterRoutesInit} from "@tsed/common";
|
|
@@ -93,7 +111,6 @@ export class UsersService {
|
|
|
93
111
|
private readonly connection!: MikroORM;
|
|
94
112
|
|
|
95
113
|
async create(user: User): Promise<User> {
|
|
96
|
-
|
|
97
114
|
// do something
|
|
98
115
|
// ...
|
|
99
116
|
// Then save
|
|
@@ -112,6 +129,62 @@ export class UsersService {
|
|
|
112
129
|
}
|
|
113
130
|
```
|
|
114
131
|
|
|
132
|
+
It's also possible to inject a connection by his name:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import {Injectable} from "@tsed/di";
|
|
136
|
+
|
|
137
|
+
@Injectable()
|
|
138
|
+
export class MyService {
|
|
139
|
+
@Connection("mongo")
|
|
140
|
+
private readonly connection!: MikroORM;
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Obtain EntityManager
|
|
145
|
+
|
|
146
|
+
`@EntityManager` and `@Em` decorators lets you retrieve an instance of EntityManager.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import {Injectable, AfterRoutesInit} from "@tsed/common";
|
|
150
|
+
import {Em} from "@tsed/mikro-orm";
|
|
151
|
+
import {EntityManager} from '@mikro-orm/mysql'; // Import EntityManager from your driver package or `@mikro-orm/knex`
|
|
152
|
+
|
|
153
|
+
@Injectable()
|
|
154
|
+
export class UsersService {
|
|
155
|
+
@Em()
|
|
156
|
+
private readonly em!: EntityManager;
|
|
157
|
+
|
|
158
|
+
async create(user: User): Promise<User> {
|
|
159
|
+
await this.em.persistAndFlush(user);
|
|
160
|
+
console.log("Saved a new user with id: " + user.id);
|
|
161
|
+
|
|
162
|
+
return user;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
It's also possible to inject Entity manager by his connection name:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import {Injectable, AfterRoutesInit} from "@tsed/common";
|
|
171
|
+
import {Em} from "@tsed/mikro-orm";
|
|
172
|
+
import {EntityManager} from '@mikro-orm/mysql'; // Import EntityManager from your driver package or `@mikro-orm/knex`
|
|
173
|
+
|
|
174
|
+
@Injectable()
|
|
175
|
+
export class UsersService {
|
|
176
|
+
@Em("connectionName")
|
|
177
|
+
private readonly em!: EntityManager;
|
|
178
|
+
|
|
179
|
+
async create(user: User): Promise<User> {
|
|
180
|
+
await this.em.persistAndFlush(user);
|
|
181
|
+
console.log("Saved a new user with id: " + user.id);
|
|
182
|
+
|
|
183
|
+
return user;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
115
188
|
## Use Entity with Controller
|
|
116
189
|
|
|
117
190
|
To begin, we need to define an Entity MikroOrm like this and use Ts.ED Decorator to define the JSON Schema.
|
|
@@ -200,6 +273,93 @@ export class UsersCtrl {
|
|
|
200
273
|
}
|
|
201
274
|
```
|
|
202
275
|
|
|
276
|
+
By default, the automatic retry policy is disabled. You can implement your own to match the business requirements and the nature of the failure. For some noncritical operations, it is better to fail as soon as possible rather than retry a coupe of times. For example, in an interactive web application, it is better to fail right after a smaller number of retries with only a short delay between retry attempts, and display a message to the user (for example, "please try again later").
|
|
277
|
+
|
|
278
|
+
The `@Transactional()` decorator allows you to enable a retry policy for the particular resources. You just need to implement the `RetryStrategy` interface and use `registerProvider()` or `@OverrideProvider()` to register it in the IoC container. Below you can find an example to handle occurred optimistic locks based on [an exponential backoff retry strategy](https://en.wikipedia.org/wiki/Exponential_backoff).
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
import { OptimisticLockError } from '@mikro-orm/core';
|
|
282
|
+
import { RetryStrategy } from '@tsed/mikro-orm';
|
|
283
|
+
|
|
284
|
+
export interface ExponentialBackoffOptions {
|
|
285
|
+
maxDepth: number;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export class ExponentialBackoff implements RetryStrategy {
|
|
289
|
+
private depth = 0;
|
|
290
|
+
|
|
291
|
+
constructor(private readonly options: ExponentialBackoffOptions) {}
|
|
292
|
+
|
|
293
|
+
public async acquire<T extends (...args: unknown[]) => unknown>(
|
|
294
|
+
task: T
|
|
295
|
+
): Promise<ReturnType<T>> {
|
|
296
|
+
try {
|
|
297
|
+
return (await task()) as ReturnType<T>;
|
|
298
|
+
} catch (e) {
|
|
299
|
+
if (
|
|
300
|
+
this.shouldRetry(e as Error) &&
|
|
301
|
+
this.depth < this.options.maxDepth
|
|
302
|
+
) {
|
|
303
|
+
return this.retry(task);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
throw e;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private shouldRetry(error: Error): boolean {
|
|
311
|
+
return error instanceof OptimisticLockError;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private async retry<T extends (...args: unknown[]) => unknown>(
|
|
315
|
+
task: T
|
|
316
|
+
): Promise<ReturnType<T>> {
|
|
317
|
+
await this.sleep(2 ** this.depth * 50);
|
|
318
|
+
|
|
319
|
+
this.depth += 1;
|
|
320
|
+
|
|
321
|
+
return this.acquire(task);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private sleep(milliseconds: number): Promise<void> {
|
|
325
|
+
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
registerProvider({
|
|
330
|
+
provide: RetryStrategy,
|
|
331
|
+
useFactory(): ExponentialBackoff {
|
|
332
|
+
return new ExponentialBackoff({maxDepth: 3})
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
`ExponentialBackoff` invokes passed function recursively is contained in a try/catch block. The method returns control to the interceptor if the call to the `task` function succeeds without throwing an exception. If the `task` method fails, the catch block examines the reason for the failure. If it's optimistic locking the code waits for a short delay before retrying the operation.
|
|
338
|
+
|
|
339
|
+
Once a retry strategy is implemented, you can enable an automatic retry mechanism using the `@Transactional` decorator like that:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import {Controller, Post, BodyParams, Inject, Get} from "@tsed/common";
|
|
343
|
+
import {Transactional} from "@tsed/mikro-orm";
|
|
344
|
+
|
|
345
|
+
@Controller("/users")
|
|
346
|
+
export class UsersCtrl {
|
|
347
|
+
@Inject()
|
|
348
|
+
private usersService: UsersService;
|
|
349
|
+
|
|
350
|
+
@Post("/")
|
|
351
|
+
@Transactional({retry: true})
|
|
352
|
+
create(@BodyParams() user: User): Promise<User> {
|
|
353
|
+
return this.usersService.create(user);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
@Get("/")
|
|
357
|
+
getList(): Promise<User[]> {
|
|
358
|
+
return this.usersService.find();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
203
363
|
## Contributors
|
|
204
364
|
|
|
205
365
|
Please read [contributing guidelines here](https://tsed.io/CONTRIBUTING.html)
|