@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.
Files changed (72) hide show
  1. package/.eslintrc +54 -0
  2. package/dist/common/decorators.d.ts +3 -0
  3. package/dist/common/decorators.js +14 -0
  4. package/dist/common/decorators.js.map +1 -0
  5. package/dist/common/index.d.ts +3 -0
  6. package/dist/common/index.js +20 -0
  7. package/dist/common/index.js.map +1 -0
  8. package/dist/common/tokens.d.ts +3 -0
  9. package/dist/common/tokens.js +14 -0
  10. package/dist/common/tokens.js.map +1 -0
  11. package/dist/common/utils.d.ts +2 -0
  12. package/dist/common/utils.js +19 -0
  13. package/dist/common/utils.js.map +1 -0
  14. package/dist/constants.d.ts +4 -0
  15. package/dist/constants.js +8 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/driver.module.d.ts +7 -0
  18. package/dist/driver.module.js +41 -0
  19. package/dist/driver.module.js.map +1 -0
  20. package/dist/drivers/collection.driver.d.ts +21 -0
  21. package/dist/drivers/collection.driver.js +101 -0
  22. package/dist/drivers/collection.driver.js.map +1 -0
  23. package/dist/drivers/table.driver.d.ts +13 -0
  24. package/dist/drivers/table.driver.js +79 -0
  25. package/dist/drivers/table.driver.js.map +1 -0
  26. package/dist/factories/definitions.factory.d.ts +11 -0
  27. package/dist/factories/definitions.factory.js +116 -0
  28. package/dist/factories/definitions.factory.js.map +1 -0
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.js +20 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/mongoose-options.interface.d.ts +12 -0
  33. package/dist/mongoose-options.interface.js +3 -0
  34. package/dist/mongoose-options.interface.js.map +1 -0
  35. package/dist/mongoose.module.d.ts +11 -0
  36. package/dist/mongoose.module.js +108 -0
  37. package/dist/mongoose.module.js.map +1 -0
  38. package/dist/storages/data-source.d.ts +10 -0
  39. package/dist/storages/data-source.js +34 -0
  40. package/dist/storages/data-source.js.map +1 -0
  41. package/jest.cases.config.cjs +14 -0
  42. package/lib/common/decorators.ts +17 -0
  43. package/lib/common/index.ts +3 -0
  44. package/lib/common/tokens.ts +17 -0
  45. package/lib/common/utils.ts +29 -0
  46. package/lib/constants.ts +4 -0
  47. package/lib/driver.module.ts +37 -0
  48. package/lib/drivers/collection.driver.ts +157 -0
  49. package/lib/drivers/table.driver.ts +109 -0
  50. package/lib/factories/definitions.factory.ts +129 -0
  51. package/lib/index.ts +3 -0
  52. package/lib/mongoose-options.interface.ts +19 -0
  53. package/lib/mongoose.module.ts +95 -0
  54. package/lib/storages/data-source.ts +37 -0
  55. package/package.json +42 -0
  56. package/tests/app/bootstrap.ts +16 -0
  57. package/tests/app/db.ts +22 -0
  58. package/tests/app/filters.ts +25 -0
  59. package/tests/app/helpers/is-one-of.ts +58 -0
  60. package/tests/app/main.ts +3 -0
  61. package/tests/app/module/app.controller.ts +67 -0
  62. package/tests/app/module/app.dto.ts +394 -0
  63. package/tests/app/module/app.module.ts +12 -0
  64. package/tests/app/module/app.service.ts +76 -0
  65. package/tests/app/root.module.ts +12 -0
  66. package/tests/globals.d.ts +6 -0
  67. package/tests/integrations/cases.test.ts +154 -0
  68. package/tests/integrations/custom.test.ts +69 -0
  69. package/tests/unit/definitions.test.ts +31 -0
  70. package/tsconfig.build.json +9 -0
  71. package/tsconfig.json +17 -0
  72. 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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=mongoose-options.interface.js.map
@@ -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,3 @@
1
+ export * from './decorators'
2
+ export * from './tokens'
3
+ export * from './utils'
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ export const DEFAULT_DB_CONNECTION = 'XeoDatabaseConnection'
2
+ export const MONGOOSE_MODULE_OPTIONS = 'XeoMongooseModuleOptions'
3
+ export const MONGOOSE_CONNECTION_NAME = 'XeoMongooseConnectionName'
4
+ export const DATA_SOURCE_TOKEN = `XEO_DATA_SOURCE`
@@ -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
+ }