@companix/xeo-server 0.0.2
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/.eslintrc +54 -0
- package/dist/common/decorators.d.ts +3 -0
- package/dist/common/decorators.js +14 -0
- package/dist/common/decorators.js.map +1 -0
- package/dist/common/index.d.ts +3 -0
- package/dist/common/index.js +20 -0
- package/dist/common/index.js.map +1 -0
- package/dist/common/tokens.d.ts +3 -0
- package/dist/common/tokens.js +14 -0
- package/dist/common/tokens.js.map +1 -0
- package/dist/common/utils.d.ts +2 -0
- package/dist/common/utils.js +19 -0
- package/dist/common/utils.js.map +1 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/driver.module.d.ts +7 -0
- package/dist/driver.module.js +41 -0
- package/dist/driver.module.js.map +1 -0
- package/dist/drivers/collection.driver.d.ts +21 -0
- package/dist/drivers/collection.driver.js +101 -0
- package/dist/drivers/collection.driver.js.map +1 -0
- package/dist/drivers/table.driver.d.ts +13 -0
- package/dist/drivers/table.driver.js +79 -0
- package/dist/drivers/table.driver.js.map +1 -0
- package/dist/factories/definitions.factory.d.ts +11 -0
- package/dist/factories/definitions.factory.js +116 -0
- package/dist/factories/definitions.factory.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/mongoose-options.interface.d.ts +12 -0
- package/dist/mongoose-options.interface.js +3 -0
- package/dist/mongoose-options.interface.js.map +1 -0
- package/dist/mongoose.module.d.ts +11 -0
- package/dist/mongoose.module.js +108 -0
- package/dist/mongoose.module.js.map +1 -0
- package/dist/storages/data-source.d.ts +10 -0
- package/dist/storages/data-source.js +34 -0
- package/dist/storages/data-source.js.map +1 -0
- package/jest.cases.config.cjs +14 -0
- package/lib/common/decorators.ts +17 -0
- package/lib/common/index.ts +3 -0
- package/lib/common/tokens.ts +17 -0
- package/lib/common/utils.ts +29 -0
- package/lib/constants.ts +4 -0
- package/lib/driver.module.ts +37 -0
- package/lib/drivers/collection.driver.ts +157 -0
- package/lib/drivers/table.driver.ts +109 -0
- package/lib/factories/definitions.factory.ts +129 -0
- package/lib/index.ts +3 -0
- package/lib/mongoose-options.interface.ts +19 -0
- package/lib/mongoose.module.ts +95 -0
- package/lib/storages/data-source.ts +37 -0
- package/package.json +42 -0
- package/tests/app/bootstrap.ts +16 -0
- package/tests/app/db.ts +22 -0
- package/tests/app/filters.ts +25 -0
- package/tests/app/helpers/is-one-of.ts +58 -0
- package/tests/app/main.ts +3 -0
- package/tests/app/module/app.controller.ts +67 -0
- package/tests/app/module/app.dto.ts +394 -0
- package/tests/app/module/app.module.ts +12 -0
- package/tests/app/module/app.service.ts +76 -0
- package/tests/app/root.module.ts +12 -0
- package/tests/globals.d.ts +6 -0
- package/tests/integrations/cases.test.ts +154 -0
- package/tests/integrations/custom.test.ts +69 -0
- package/tests/unit/definitions.test.ts +31 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +17 -0
- package/tsconfig.test-app.json +10 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ConnectOptions, Connection, MongooseError } from 'mongoose';
|
|
2
|
+
export interface MongooseModuleOptions extends ConnectOptions {
|
|
3
|
+
uri?: string;
|
|
4
|
+
retryAttempts?: number;
|
|
5
|
+
retryDelay?: number;
|
|
6
|
+
connectionName?: string;
|
|
7
|
+
connectionFactory?: (connection: any, name: string) => any;
|
|
8
|
+
connectionErrorFactory?: (error: MongooseError) => MongooseError;
|
|
9
|
+
lazyConnection?: boolean;
|
|
10
|
+
onConnectionCreate?: (connection: Connection) => void;
|
|
11
|
+
verboseRetryLog?: boolean;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongoose-options.interface.js","sourceRoot":"","sources":["../lib/mongoose-options.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DynamicModule, OnApplicationShutdown } from '@nestjs/common';
|
|
2
|
+
import { ModuleRef } from '@nestjs/core';
|
|
3
|
+
import { MongooseModuleOptions } from './mongoose-options.interface';
|
|
4
|
+
export declare class MongooseCoreModule implements OnApplicationShutdown {
|
|
5
|
+
private readonly connectionName;
|
|
6
|
+
private readonly moduleRef;
|
|
7
|
+
constructor(connectionName: string, moduleRef: ModuleRef);
|
|
8
|
+
static forRoot(uri: string, options?: MongooseModuleOptions): DynamicModule;
|
|
9
|
+
private static createMongooseConnection;
|
|
10
|
+
onApplicationShutdown(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
47
|
+
var MongooseCoreModule_1;
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.MongooseCoreModule = void 0;
|
|
50
|
+
const mongoose = __importStar(require("mongoose"));
|
|
51
|
+
const common_1 = require("@nestjs/common");
|
|
52
|
+
const core_1 = require("@nestjs/core");
|
|
53
|
+
const rxjs_1 = require("rxjs");
|
|
54
|
+
const operators_1 = require("rxjs/operators");
|
|
55
|
+
const constants_1 = require("./constants");
|
|
56
|
+
const common_2 = require("./common");
|
|
57
|
+
let MongooseCoreModule = MongooseCoreModule_1 = class MongooseCoreModule {
|
|
58
|
+
constructor(connectionName, moduleRef) {
|
|
59
|
+
this.connectionName = connectionName;
|
|
60
|
+
this.moduleRef = moduleRef;
|
|
61
|
+
}
|
|
62
|
+
static forRoot(uri, options = {}) {
|
|
63
|
+
const { retryAttempts, retryDelay, connectionName, connectionFactory, connectionErrorFactory, lazyConnection, onConnectionCreate, verboseRetryLog, ...mongooseOptions } = options;
|
|
64
|
+
const mongooseConnectionFactory = connectionFactory || ((connection) => connection);
|
|
65
|
+
const mongooseConnectionError = connectionErrorFactory || ((error) => error);
|
|
66
|
+
const mongooseConnectionName = (0, common_2.getConnectionToken)(connectionName);
|
|
67
|
+
const mongooseConnectionNameProvider = {
|
|
68
|
+
provide: constants_1.MONGOOSE_CONNECTION_NAME,
|
|
69
|
+
useValue: mongooseConnectionName
|
|
70
|
+
};
|
|
71
|
+
const connectionProvider = {
|
|
72
|
+
provide: mongooseConnectionName,
|
|
73
|
+
useFactory: async () => await (0, rxjs_1.lastValueFrom)((0, rxjs_1.defer)(async () => mongooseConnectionFactory(await this.createMongooseConnection(uri, mongooseOptions, {
|
|
74
|
+
lazyConnection,
|
|
75
|
+
onConnectionCreate
|
|
76
|
+
}), mongooseConnectionName)).pipe((0, common_2.handleRetry)(retryAttempts, retryDelay, verboseRetryLog), (0, operators_1.catchError)((error) => {
|
|
77
|
+
throw mongooseConnectionError(error);
|
|
78
|
+
})))
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
module: MongooseCoreModule_1,
|
|
82
|
+
providers: [connectionProvider, mongooseConnectionNameProvider],
|
|
83
|
+
exports: [connectionProvider]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
static async createMongooseConnection(uri, mongooseOptions, factoryOptions) {
|
|
87
|
+
const connection = mongoose.createConnection(uri, mongooseOptions);
|
|
88
|
+
if (factoryOptions?.lazyConnection) {
|
|
89
|
+
return connection;
|
|
90
|
+
}
|
|
91
|
+
factoryOptions?.onConnectionCreate?.(connection);
|
|
92
|
+
return connection.asPromise();
|
|
93
|
+
}
|
|
94
|
+
async onApplicationShutdown() {
|
|
95
|
+
const connection = this.moduleRef.get(this.connectionName);
|
|
96
|
+
if (connection) {
|
|
97
|
+
await connection.close();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
exports.MongooseCoreModule = MongooseCoreModule;
|
|
102
|
+
exports.MongooseCoreModule = MongooseCoreModule = MongooseCoreModule_1 = __decorate([
|
|
103
|
+
(0, common_1.Global)(),
|
|
104
|
+
(0, common_1.Module)({}),
|
|
105
|
+
__param(0, (0, common_1.Inject)(constants_1.MONGOOSE_CONNECTION_NAME)),
|
|
106
|
+
__metadata("design:paramtypes", [String, core_1.ModuleRef])
|
|
107
|
+
], MongooseCoreModule);
|
|
108
|
+
//# sourceMappingURL=mongoose.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongoose.module.js","sourceRoot":"","sources":["../lib/mongoose.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,mDAAoC;AACpC,2CAA6F;AAC7F,uCAAwC;AAExC,+BAA2C;AAC3C,8CAA2C;AAC3C,2CAAsD;AACtD,qCAA0D;AAKnD,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAC7B,YACqD,cAAsB,EACxD,SAAoB;QADc,mBAAc,GAAd,cAAc,CAAQ;QACxD,cAAS,GAAT,SAAS,CAAW;IACpC,CAAC;IAEJ,MAAM,CAAC,OAAO,CAAC,GAAW,EAAE,UAAiC,EAAE;QAC7D,MAAM,EACJ,aAAa,EACb,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,GAAG,eAAe,EACnB,GAAG,OAAO,CAAA;QAEX,MAAM,yBAAyB,GAAG,iBAAiB,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,CAAA;QAEnF,MAAM,uBAAuB,GAAG,sBAAsB,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAA;QAE5E,MAAM,sBAAsB,GAAG,IAAA,2BAAkB,EAAC,cAAc,CAAC,CAAA;QAEjE,MAAM,8BAA8B,GAAG;YACrC,OAAO,EAAE,oCAAwB;YACjC,QAAQ,EAAE,sBAAsB;SACjC,CAAA;QAED,MAAM,kBAAkB,GAAG;YACzB,OAAO,EAAE,sBAAsB;YAC/B,UAAU,EAAE,KAAK,IAAkB,EAAE,CACnC,MAAM,IAAA,oBAAa,EACjB,IAAA,YAAK,EAAC,KAAK,IAAI,EAAE,CACf,yBAAyB,CACvB,MAAM,IAAI,CAAC,wBAAwB,CAAC,GAAG,EAAE,eAAe,EAAE;gBACxD,cAAc;gBACd,kBAAkB;aACnB,CAAC,EACF,sBAAsB,CACvB,CACF,CAAC,IAAI,CACJ,IAAA,oBAAW,EAAC,aAAa,EAAE,UAAU,EAAE,eAAe,CAAC,EACvD,IAAA,sBAAU,EAAC,CAAC,KAAK,EAAE,EAAE;gBACnB,MAAM,uBAAuB,CAAC,KAAK,CAAC,CAAA;YACtC,CAAC,CAAC,CACH,CACF;SACJ,CAAA;QACD,OAAO;YACL,MAAM,EAAE,oBAAkB;YAC1B,SAAS,EAAE,CAAC,kBAAkB,EAAE,8BAA8B,CAAC;YAC/D,OAAO,EAAE,CAAC,kBAAkB,CAAC;SAC9B,CAAA;IACH,CAAC;IAEO,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAC3C,GAAW,EACX,eAA+B,EAC/B,cAGC;QAED,MAAM,UAAU,GAAG,QAAQ,CAAC,gBAAgB,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;QAElE,IAAI,cAAc,EAAE,cAAc,EAAE,CAAC;YACnC,OAAO,UAAU,CAAA;QACnB,CAAC;QAED,cAAc,EAAE,kBAAkB,EAAE,CAAC,UAAU,CAAC,CAAA;QAEhD,OAAO,UAAU,CAAC,SAAS,EAAE,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAM,IAAI,CAAC,cAAc,CAAC,CAAA;QAC/D,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,UAAU,CAAC,KAAK,EAAE,CAAA;QAC1B,CAAC;IACH,CAAC;CACF,CAAA;AAlFY,gDAAkB;6BAAlB,kBAAkB;IAF9B,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC,EAAE,CAAC;IAGN,WAAA,IAAA,eAAM,EAAC,oCAAwB,CAAC,CAAA;6CACL,gBAAS;GAH5B,kBAAkB,CAkF9B"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CollectionScheme, DataScheme, DataSource } from '@companix/xeo-scheme';
|
|
2
|
+
import { Connection } from 'mongoose';
|
|
3
|
+
declare class DataSourceStorageService {
|
|
4
|
+
private dataSource;
|
|
5
|
+
private dataScheme;
|
|
6
|
+
getProviderToken(dataScheme: DataScheme<CollectionScheme>): string;
|
|
7
|
+
getSource(dataScheme: DataScheme<CollectionScheme>, connection: Connection): DataSource<CollectionScheme, import("@companix/xeo-scheme").CollectionDriver>;
|
|
8
|
+
}
|
|
9
|
+
export declare const DataSourceStorage: DataSourceStorageService;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DataSourceStorage = void 0;
|
|
4
|
+
const xeo_scheme_1 = require("@companix/xeo-scheme");
|
|
5
|
+
const collection_driver_1 = require("../drivers/collection.driver");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
7
|
+
class DataSourceStorageService {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.dataSource = null;
|
|
10
|
+
this.dataScheme = null;
|
|
11
|
+
}
|
|
12
|
+
getProviderToken(dataScheme) {
|
|
13
|
+
if (this.dataScheme === null) {
|
|
14
|
+
this.dataScheme = dataScheme;
|
|
15
|
+
}
|
|
16
|
+
if (this.dataScheme !== dataScheme) {
|
|
17
|
+
throw new Error(`[MongoDriver] driver cannot work with several dataSchemes`);
|
|
18
|
+
}
|
|
19
|
+
return constants_1.DATA_SOURCE_TOKEN;
|
|
20
|
+
}
|
|
21
|
+
getSource(dataScheme, connection) {
|
|
22
|
+
if (this.dataScheme !== dataScheme) {
|
|
23
|
+
throw new Error(`[MongoDriver] driver cannot work with several dataSchemes`);
|
|
24
|
+
}
|
|
25
|
+
if (!this.dataSource) {
|
|
26
|
+
this.dataSource = new xeo_scheme_1.DataSource(dataScheme, {
|
|
27
|
+
createDriver: (0, collection_driver_1.createMongoDriver)(connection)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return this.dataSource;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.DataSourceStorage = new DataSourceStorageService();
|
|
34
|
+
//# sourceMappingURL=data-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-source.js","sourceRoot":"","sources":["../../lib/storages/data-source.ts"],"names":[],"mappings":";;;AAAA,qDAA+E;AAE/E,oEAAgE;AAChE,4CAAgD;AAEhD,MAAM,wBAAwB;IAA9B;QACU,eAAU,GAAwC,IAAI,CAAA;QACtD,eAAU,GAAwC,IAAI,CAAA;IA2BhE,CAAC;IAzBC,gBAAgB,CAAC,UAAwC;QACvD,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC9B,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAA;QAC9E,CAAC;QAED,OAAO,6BAAiB,CAAA;IAC1B,CAAC;IAED,SAAS,CAAC,UAAwC,EAAE,UAAsB;QACxE,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAA;QAC9E,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,GAAG,IAAI,uBAAU,CAAC,UAAU,EAAE;gBAC3C,YAAY,EAAE,IAAA,qCAAiB,EAAC,UAAU,CAAC;aAC5C,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,UAAU,CAAA;IACxB,CAAC;CACF;AAEY,QAAA,iBAAiB,GAAG,IAAI,wBAAwB,EAAE,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
roots: ['<rootDir>/tests/integrations'],
|
|
4
|
+
testMatch: ['**/cases.test.ts'],
|
|
5
|
+
moduleFileExtensions: ['ts', 'js', 'json'],
|
|
6
|
+
transform: {
|
|
7
|
+
'^.+\\.ts$': [
|
|
8
|
+
'ts-jest',
|
|
9
|
+
{
|
|
10
|
+
tsconfig: '<rootDir>/tsconfig.test-app.json'
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Inject } from '@nestjs/common'
|
|
2
|
+
import { getConnectionToken, getDataSourceToken } from './tokens'
|
|
3
|
+
import { CollectionScheme, DataScheme } from '@companix/xeo-scheme'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @publicApi
|
|
7
|
+
*/
|
|
8
|
+
export const InjectDataSource = (dataSource: DataScheme<CollectionScheme>) => {
|
|
9
|
+
return Inject(getDataSourceToken(dataSource))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @publicApi
|
|
14
|
+
*/
|
|
15
|
+
export const InjectConnection = () => {
|
|
16
|
+
return Inject(getConnectionToken())
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DEFAULT_DB_CONNECTION } from '../constants'
|
|
2
|
+
import { CollectionScheme, DataScheme } from '@companix/xeo-scheme'
|
|
3
|
+
import { DataSourceStorage } from '../storages/data-source'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @publicApi
|
|
7
|
+
*/
|
|
8
|
+
export const getConnectionToken = (name?: string) => {
|
|
9
|
+
return name && name !== DEFAULT_DB_CONNECTION ? `${name}Connection` : DEFAULT_DB_CONNECTION
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @publicApi
|
|
14
|
+
*/
|
|
15
|
+
export const getDataSourceToken = (dataScheme: DataScheme<CollectionScheme>) => {
|
|
16
|
+
return DataSourceStorage.getProviderToken(dataScheme)
|
|
17
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common'
|
|
2
|
+
import { Observable } from 'rxjs'
|
|
3
|
+
import { delay, retryWhen, scan } from 'rxjs/operators'
|
|
4
|
+
|
|
5
|
+
export const handleRetry = (retryAttempts = 9, retryDelay = 3000, verboseRetryLog = false) => {
|
|
6
|
+
const logger = new Logger('MongooseModule')
|
|
7
|
+
|
|
8
|
+
return <T>(source: Observable<T>) =>
|
|
9
|
+
source.pipe(
|
|
10
|
+
retryWhen((e) =>
|
|
11
|
+
e.pipe(
|
|
12
|
+
scan((errorCount, error) => {
|
|
13
|
+
const verboseMessage = verboseRetryLog ? ` Message: ${error.message}.` : ''
|
|
14
|
+
const retryMessage = retryAttempts > 0 ? ` Retrying (${errorCount + 1})...` : ''
|
|
15
|
+
|
|
16
|
+
logger.error(
|
|
17
|
+
['Unable to connect to the database.', verboseMessage, retryMessage].join(''),
|
|
18
|
+
error.stack
|
|
19
|
+
)
|
|
20
|
+
if (errorCount + 1 >= retryAttempts) {
|
|
21
|
+
throw error
|
|
22
|
+
}
|
|
23
|
+
return errorCount + 1
|
|
24
|
+
}, 0),
|
|
25
|
+
delay(retryDelay)
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
}
|
package/lib/constants.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Provider } from '@nestjs/common'
|
|
2
|
+
import { DynamicModule, Module } from '@nestjs/common'
|
|
3
|
+
import { getConnectionToken, getDataSourceToken } from './common/tokens'
|
|
4
|
+
import { Connection } from 'mongoose'
|
|
5
|
+
import { CollectionScheme, DataScheme, DataSource } from '@companix/xeo-scheme'
|
|
6
|
+
import { MongooseCoreModule } from './mongoose.module'
|
|
7
|
+
import { MongooseModuleOptions } from './mongoose-options.interface'
|
|
8
|
+
import { DataSourceStorage } from './storages/data-source'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @publicApi
|
|
12
|
+
*/
|
|
13
|
+
@Module({})
|
|
14
|
+
export class MongooseDriverModule {
|
|
15
|
+
static forRoot(uri: string, options: MongooseModuleOptions = {}): DynamicModule {
|
|
16
|
+
return {
|
|
17
|
+
module: MongooseDriverModule,
|
|
18
|
+
imports: [MongooseCoreModule.forRoot(uri, options)]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static forFeature(dataSource: DataScheme<CollectionScheme>, connectionName?: string): DynamicModule {
|
|
23
|
+
const provider: Provider = {
|
|
24
|
+
provide: getDataSourceToken(dataSource),
|
|
25
|
+
useFactory: (connection: Connection): DataSource<CollectionScheme> => {
|
|
26
|
+
return DataSourceStorage.getSource(dataSource, connection)
|
|
27
|
+
},
|
|
28
|
+
inject: [getConnectionToken(connectionName)]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
module: MongooseDriverModule,
|
|
33
|
+
providers: [provider],
|
|
34
|
+
exports: [provider]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CollectionDriver,
|
|
3
|
+
CollectionDriverParams,
|
|
4
|
+
CollectionScheme,
|
|
5
|
+
DataScheme
|
|
6
|
+
} from '@companix/xeo-scheme'
|
|
7
|
+
import { MongoRelationsTable } from './table.driver'
|
|
8
|
+
import { Connection, Model, Schema } from 'mongoose'
|
|
9
|
+
import { DefinitionsFactory } from '../factories/definitions.factory'
|
|
10
|
+
import { Logger } from '@nestjs/common'
|
|
11
|
+
|
|
12
|
+
interface DiscriminatedModels {
|
|
13
|
+
[model: string]: {
|
|
14
|
+
discriminatorKey: string
|
|
15
|
+
models: {
|
|
16
|
+
[value: string]: Model<any>
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class MongoCollectionDriver<T extends CollectionScheme> implements CollectionDriver {
|
|
22
|
+
private collections: { [model: string]: Model<any> } = {}
|
|
23
|
+
private discriminatedModels: DiscriminatedModels = {}
|
|
24
|
+
|
|
25
|
+
public tables: MongoRelationsTable<T>
|
|
26
|
+
|
|
27
|
+
constructor(private dataScheme: DataScheme<T>, private connection: Connection) {
|
|
28
|
+
Logger.log('Driver bootstrap', 'MongoDriver')
|
|
29
|
+
|
|
30
|
+
this.tables = new MongoRelationsTable(dataScheme, connection)
|
|
31
|
+
|
|
32
|
+
const factory = new DefinitionsFactory(dataScheme)
|
|
33
|
+
|
|
34
|
+
for (const name in dataScheme.collections) {
|
|
35
|
+
const model = dataScheme.collections[name].name
|
|
36
|
+
const scheme = dataScheme.models[model].scheme
|
|
37
|
+
|
|
38
|
+
const baseDefinition = factory.createForScheme(scheme)
|
|
39
|
+
|
|
40
|
+
if (scheme.type === 'base') {
|
|
41
|
+
this.collections[model] = this.useModel(model, new Schema(baseDefinition))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (scheme.type === 'discriminated') {
|
|
45
|
+
this.collections[model] = this.useModel(
|
|
46
|
+
model,
|
|
47
|
+
new Schema(baseDefinition, {
|
|
48
|
+
discriminatorKey: scheme.discriminatorKey
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
this.discriminatedModels[model] = {
|
|
53
|
+
discriminatorKey: scheme.discriminatorKey,
|
|
54
|
+
models: {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const discriminator of scheme.discriminators) {
|
|
58
|
+
const discriminatedModel = this.collections[model].discriminator(
|
|
59
|
+
discriminator.name,
|
|
60
|
+
new Schema(factory.createDefinitionScheme(discriminator)),
|
|
61
|
+
discriminator.value
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
this.discriminatedModels[model].models[discriminator.value] = discriminatedModel
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getAll({ model }: CollectionDriverParams.Model) {
|
|
71
|
+
return this.collections[model].find().lean().exec()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async get({ model, id }: CollectionDriverParams.Record) {
|
|
75
|
+
return this.collections[model].findOne({ [this.getIdentifierKey(model)]: id }).lean()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async create({ model, data }: CollectionDriverParams.Create) {
|
|
79
|
+
await new this.collections[model](data).save()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async remove({ model, id }: CollectionDriverParams.Record) {
|
|
83
|
+
await this.collections[model].deleteOne({ [this.getIdentifierKey(model)]: id }).exec()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// при update для discriminated collection нельзя всегда использовать только this.collections[model]
|
|
87
|
+
// нужно сначала получить документ по id, узнать его discriminatorKey (type), и если это дискриминатор, выполнять updateOne() через соответствующую discriminator model
|
|
88
|
+
async update({ model, id, patches }: CollectionDriverParams.Update) {
|
|
89
|
+
const identifierKey = this.getIdentifierKey(model)
|
|
90
|
+
const collection = await this.getCollection({ model, id })
|
|
91
|
+
|
|
92
|
+
const responses = await Promise.all(
|
|
93
|
+
patches.map(async (patch) => {
|
|
94
|
+
switch (patch.type) {
|
|
95
|
+
case 'set': {
|
|
96
|
+
return collection.updateOne(
|
|
97
|
+
{ [identifierKey]: id },
|
|
98
|
+
{ $set: { [patch.address]: patch.value } }
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
case 'push': {
|
|
102
|
+
return collection.updateOne(
|
|
103
|
+
{ [identifierKey]: id },
|
|
104
|
+
{ $push: { [patch.address]: { $each: patch.items } } }
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
case 'pull': {
|
|
108
|
+
return collection.updateOne(
|
|
109
|
+
{ [identifierKey]: id },
|
|
110
|
+
{ $pull: { [patch.address]: { $in: patch.items } } }
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
// check transaction
|
|
118
|
+
for (const response of responses) {
|
|
119
|
+
if (!response.acknowledged) {
|
|
120
|
+
console.log('MongoDB Write Warning', { model, id, patches }, responses)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private async getCollection({ model, id }: CollectionDriverParams.Record) {
|
|
126
|
+
if (this.discriminatedModels[model]) {
|
|
127
|
+
const target = await this.get({ model, id })
|
|
128
|
+
const discriminatorValue = target[this.discriminatedModels[model].discriminatorKey] as string
|
|
129
|
+
|
|
130
|
+
return this.discriminatedModels[model].models[discriminatorValue]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return this.collections[model]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async exists({ model, id }: CollectionDriverParams.Record) {
|
|
137
|
+
const result = await this.collections[model].exists({
|
|
138
|
+
[this.getIdentifierKey(model)]: id
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return result !== null
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private getIdentifierKey(model: string) {
|
|
145
|
+
return this.dataScheme.models[model].scheme.identifier.propertyKey
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private useModel<T>(model: string, schema: Schema): Model<T> {
|
|
149
|
+
return this.connection.models[model] ?? this.connection.model(model, schema)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const createMongoDriver = (connection: Connection) => {
|
|
154
|
+
return <T extends CollectionScheme>(dataScheme: DataScheme<T>) => {
|
|
155
|
+
return new MongoCollectionDriver(dataScheme, connection)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CollectionScheme,
|
|
3
|
+
DataScheme,
|
|
4
|
+
IType,
|
|
5
|
+
RelationRecord,
|
|
6
|
+
RelationsTableInfo,
|
|
7
|
+
TableDriver,
|
|
8
|
+
TableRow,
|
|
9
|
+
TableRelationSlice
|
|
10
|
+
} from '@companix/xeo-scheme'
|
|
11
|
+
import { Connection, Model, Schema, SchemaDefinition } from 'mongoose'
|
|
12
|
+
|
|
13
|
+
class MongoTableStore {
|
|
14
|
+
private modelKey: { [model: string]: 'm1' | 'm2' } = {}
|
|
15
|
+
private keyMirror = { m1: 'm2' as 'm2', m2: 'm1' as 'm1' }
|
|
16
|
+
|
|
17
|
+
private model: Model<TableRow>
|
|
18
|
+
|
|
19
|
+
constructor({ rules, tableName }: RelationsTableInfo, private connection: Connection) {
|
|
20
|
+
this.modelKey[rules.m1] = 'm1'
|
|
21
|
+
this.modelKey[rules.m2] = 'm2'
|
|
22
|
+
this.model = this.useModel<TableRow>(tableName, new Schema(this.getSchemeDefinition()))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private getSchemeDefinition(): SchemaDefinition {
|
|
26
|
+
return {
|
|
27
|
+
m1: { type: Schema.Types.Mixed, index: true, required: true },
|
|
28
|
+
m2: { type: Schema.Types.Mixed, index: true, required: true }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private getRow({ modelId, modelSide, oppositeId }: RelationRecord) {
|
|
33
|
+
const key = this.modelKey[modelSide]
|
|
34
|
+
const row = { [key]: modelId, [this.keyMirror[key]]: oppositeId } as unknown as TableRow
|
|
35
|
+
|
|
36
|
+
return row
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async addRow(row: RelationRecord) {
|
|
40
|
+
await this.model.create(this.getRow(row))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async removeRow(row: RelationRecord) {
|
|
44
|
+
await this.model.deleteOne(this.getRow(row)).exec()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async removeModel(model: string, modelId: IType) {
|
|
48
|
+
await this.model.deleteMany({ [this.modelKey[model]]: modelId }).exec()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async getRelations(model: string, modelId: IType) {
|
|
52
|
+
const key = this.modelKey[model]
|
|
53
|
+
const oppositeKey = this.keyMirror[key]
|
|
54
|
+
|
|
55
|
+
const rows = await this.model
|
|
56
|
+
.find({ [key]: modelId })
|
|
57
|
+
.select({ [oppositeKey]: 1, _id: 0 })
|
|
58
|
+
.lean()
|
|
59
|
+
.exec()
|
|
60
|
+
|
|
61
|
+
return rows.map((row) => row[oppositeKey] as IType)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private useModel<T>(model: string, schema: Schema): Model<T> {
|
|
65
|
+
return this.connection.models[model] ?? this.connection.model(model, schema)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getStore() {
|
|
69
|
+
return this.model.find().lean().exec()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class MongoRelationsTable<T extends CollectionScheme> implements TableDriver {
|
|
74
|
+
private readonly tables: { [tableName: string]: MongoTableStore } = {}
|
|
75
|
+
|
|
76
|
+
constructor(dataScheme: DataScheme<T>, connection: Connection) {
|
|
77
|
+
for (const table of dataScheme.tables) {
|
|
78
|
+
this.tables[table.tableName] = new MongoTableStore(table, connection)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async createRecord(row: RelationRecord) {
|
|
83
|
+
await this.tables[row.tableName].addRow(row)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async removeRecord(row: RelationRecord) {
|
|
87
|
+
await this.tables[row.tableName].removeRow(row)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async removeRecordsByModel(row: TableRelationSlice) {
|
|
91
|
+
await this.tables[row.tableName].removeModel(row.modelSide, row.modelId)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async getRecords({ tableName, modelSide, modelId }: TableRelationSlice) {
|
|
95
|
+
return this.tables[tableName].getRelations(modelSide, modelId)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// development
|
|
99
|
+
|
|
100
|
+
async getTables() {
|
|
101
|
+
const state: { [name: string]: TableRow[] } = {}
|
|
102
|
+
|
|
103
|
+
for (const tableName in this.tables) {
|
|
104
|
+
state[tableName] = await this.tables[tableName].getStore()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return state
|
|
108
|
+
}
|
|
109
|
+
}
|