@flusys/nestjs-shared 5.1.2 → 5.1.3
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/cjs/classes/api-service.class.js +64 -30
- package/cjs/classes/index.js +0 -1
- package/cjs/modules/datasource/index.js +0 -1
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +24 -85
- package/classes/api-service.class.d.ts +13 -6
- package/classes/index.d.ts +0 -1
- package/decorators/current-user.decorator.d.ts +1 -1
- package/fesm/classes/api-service.class.js +67 -33
- package/fesm/classes/index.js +0 -1
- package/fesm/interfaces/datasource.interface.js +1 -20
- package/fesm/modules/datasource/index.js +0 -1
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +24 -85
- package/guards/permission.guard.d.ts +1 -1
- package/interceptors/idempotency.interceptor.d.ts +1 -1
- package/interfaces/notification-adapter.interface.d.ts +13 -14
- package/modules/datasource/index.d.ts +0 -1
- package/modules/datasource/multi-tenant-datasource.service.d.ts +4 -13
- package/package.json +15 -15
- package/cjs/classes/request-scoped-api.service.js +0 -61
- package/cjs/modules/datasource/datasource.module.js +0 -117
- package/classes/request-scoped-api.service.d.ts +0 -14
- package/fesm/classes/request-scoped-api.service.js +0 -51
- package/fesm/modules/datasource/datasource.module.js +0 -107
- package/modules/datasource/datasource.module.d.ts +0 -19
|
@@ -13,6 +13,9 @@ const _typeorm = require("typeorm");
|
|
|
13
13
|
const _constants = require("../constants");
|
|
14
14
|
const _logactiondecorator = require("../decorators/log-action.decorator");
|
|
15
15
|
const _dtos = require("../dtos");
|
|
16
|
+
const _interfaces = require("../interfaces");
|
|
17
|
+
const _utilsservice = require("../modules/utils/utils.service");
|
|
18
|
+
const _hybridcacheclass = require("./hybrid-cache.class");
|
|
16
19
|
function _define_property(obj, key, value) {
|
|
17
20
|
if (key in obj) {
|
|
18
21
|
Object.defineProperty(obj, key, {
|
|
@@ -36,6 +39,31 @@ function _ts_metadata(k, v) {
|
|
|
36
39
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
37
40
|
}
|
|
38
41
|
let ApiService = class ApiService {
|
|
42
|
+
resolveEntity() {
|
|
43
|
+
return this._entity;
|
|
44
|
+
}
|
|
45
|
+
getDataSourceProvider() {
|
|
46
|
+
return this._dataSourceProvider;
|
|
47
|
+
}
|
|
48
|
+
async ensureDataSourceRepository() {
|
|
49
|
+
if (!this.repositoryInitialized) {
|
|
50
|
+
const entity = this.resolveEntity();
|
|
51
|
+
const provider = this.getDataSourceProvider();
|
|
52
|
+
this.repository = await provider.getRepository(entity);
|
|
53
|
+
this.repositoryInitialized = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async resolveRepositories(entities) {
|
|
57
|
+
const provider = this.getDataSourceProvider();
|
|
58
|
+
const repositories = [];
|
|
59
|
+
for (const entity of entities){
|
|
60
|
+
repositories.push(await provider.getRepository(entity));
|
|
61
|
+
}
|
|
62
|
+
return repositories;
|
|
63
|
+
}
|
|
64
|
+
async getDataSourceForService() {
|
|
65
|
+
return this.getDataSourceProvider().getDataSource();
|
|
66
|
+
}
|
|
39
67
|
async insert(dto, user) {
|
|
40
68
|
return this.executeInTransaction('insert', async (qr)=>{
|
|
41
69
|
await this.beforeInsertOperation(dto, user, qr);
|
|
@@ -108,7 +136,7 @@ let ApiService = class ApiService {
|
|
|
108
136
|
});
|
|
109
137
|
}
|
|
110
138
|
async findByIds(ids, user, select) {
|
|
111
|
-
await this.
|
|
139
|
+
await this.ensureDataSourceRepository();
|
|
112
140
|
try {
|
|
113
141
|
const query = this.repository.createQueryBuilder(this.entityName);
|
|
114
142
|
query.where({
|
|
@@ -136,7 +164,7 @@ let ApiService = class ApiService {
|
|
|
136
164
|
}
|
|
137
165
|
}
|
|
138
166
|
async findById(id, user, select) {
|
|
139
|
-
await this.
|
|
167
|
+
await this.ensureDataSourceRepository();
|
|
140
168
|
try {
|
|
141
169
|
const query = this.repository.createQueryBuilder(this.entityName);
|
|
142
170
|
query.where({
|
|
@@ -150,18 +178,17 @@ let ApiService = class ApiService {
|
|
|
150
178
|
result = await this.cacheManager.get(cacheKey);
|
|
151
179
|
}
|
|
152
180
|
if (!result) {
|
|
153
|
-
let output;
|
|
154
181
|
if (isRaw) {
|
|
155
|
-
|
|
156
|
-
const convertResult = this.convertEntityListToResponseListDto(
|
|
182
|
+
const rawOutput = await finalQuery.getRawMany();
|
|
183
|
+
const convertResult = this.convertEntityListToResponseListDto(rawOutput, isRaw);
|
|
157
184
|
if (convertResult.length) result = convertResult[0];
|
|
158
185
|
} else {
|
|
159
|
-
|
|
160
|
-
if (!
|
|
186
|
+
const singleOutput = await finalQuery.getOne();
|
|
187
|
+
if (!singleOutput) throw new _common.NotFoundException({
|
|
161
188
|
message: 'No Data Found',
|
|
162
189
|
messageKey: _constants.ERROR_MESSAGES.NOT_FOUND
|
|
163
190
|
});
|
|
164
|
-
result = this.convertEntityToResponseDto(
|
|
191
|
+
result = this.convertEntityToResponseDto(singleOutput, false);
|
|
165
192
|
}
|
|
166
193
|
if (this.isCacheable) {
|
|
167
194
|
await this.cacheManager.set(cacheKey, result);
|
|
@@ -177,41 +204,34 @@ let ApiService = class ApiService {
|
|
|
177
204
|
}
|
|
178
205
|
}
|
|
179
206
|
async getAll(search, filterAndPaginationDto, user) {
|
|
180
|
-
await this.
|
|
207
|
+
await this.ensureDataSourceRepository();
|
|
181
208
|
try {
|
|
182
209
|
let data;
|
|
183
210
|
let total;
|
|
184
211
|
let isRaw = false;
|
|
185
212
|
let query = this.repository.createQueryBuilder(this.entityName);
|
|
186
|
-
// Apply filters
|
|
187
213
|
if (filterAndPaginationDto.filter) {
|
|
188
214
|
const { query: filterQuery, isRaw: filterIsRaw } = await this.getFilterQuery(query, filterAndPaginationDto.filter, user);
|
|
189
215
|
query = filterQuery;
|
|
190
216
|
isRaw = isRaw || filterIsRaw;
|
|
191
217
|
}
|
|
192
|
-
// Apply search
|
|
193
218
|
if (search) {
|
|
194
219
|
const { query: searchQuery, isRaw: searchIsRaw } = await this.getGlobalSearchQuery(query, search, user);
|
|
195
220
|
query = searchQuery;
|
|
196
221
|
isRaw = isRaw || searchIsRaw;
|
|
197
222
|
}
|
|
198
|
-
// Apply select — always call so subclasses can define a default column set
|
|
199
223
|
const { query: selectQuery, isRaw: selectIsRaw } = await this.getSelectQuery(query, user, filterAndPaginationDto.select);
|
|
200
224
|
query = selectQuery;
|
|
201
225
|
isRaw = isRaw || selectIsRaw;
|
|
202
|
-
// Apply sort
|
|
203
226
|
if (filterAndPaginationDto.sort) {
|
|
204
227
|
const { query: sortQuery, isRaw: sortIsRaw } = await this.getSortQuery(query, filterAndPaginationDto.sort, user);
|
|
205
228
|
query = sortQuery;
|
|
206
229
|
isRaw = isRaw || sortIsRaw;
|
|
207
230
|
}
|
|
208
|
-
// Include soft-deleted if requested
|
|
209
231
|
if (filterAndPaginationDto.withDeleted) query.withDeleted();
|
|
210
|
-
// Extra manipulation
|
|
211
232
|
const { query: extraQuery, isRaw: extraIsRaw } = await this.getExtraManipulateQuery(query, filterAndPaginationDto, user);
|
|
212
233
|
query = extraQuery;
|
|
213
234
|
isRaw = isRaw || extraIsRaw;
|
|
214
|
-
// Pagination
|
|
215
235
|
if (filterAndPaginationDto.pagination) {
|
|
216
236
|
const offset = filterAndPaginationDto.pagination.currentPage * filterAndPaginationDto.pagination.pageSize;
|
|
217
237
|
const limit = filterAndPaginationDto.pagination.pageSize;
|
|
@@ -221,7 +241,6 @@ let ApiService = class ApiService {
|
|
|
221
241
|
query.skip(offset).take(limit);
|
|
222
242
|
}
|
|
223
243
|
}
|
|
224
|
-
// Generate cache key
|
|
225
244
|
let cacheKey = '';
|
|
226
245
|
if (this.isCacheable) {
|
|
227
246
|
cacheKey = this.utilsService.getCacheKey(this.entityName, {
|
|
@@ -235,7 +254,6 @@ let ApiService = class ApiService {
|
|
|
235
254
|
}
|
|
236
255
|
}
|
|
237
256
|
if (isRaw) {
|
|
238
|
-
// Handle raw query count manually
|
|
239
257
|
const rawSubQuery = query.clone().offset(undefined).limit(undefined).distinct(true);
|
|
240
258
|
const countSql = `
|
|
241
259
|
SELECT COUNT(*) as count
|
|
@@ -283,7 +301,7 @@ let ApiService = class ApiService {
|
|
|
283
301
|
}
|
|
284
302
|
}
|
|
285
303
|
async delete(option, user) {
|
|
286
|
-
await this.
|
|
304
|
+
await this.ensureDataSourceRepository();
|
|
287
305
|
const queryRunner = this.repository.manager.connection.createQueryRunner();
|
|
288
306
|
await queryRunner.connect();
|
|
289
307
|
await queryRunner.startTransaction();
|
|
@@ -315,7 +333,6 @@ let ApiService = class ApiService {
|
|
|
315
333
|
this.clearCacheForAll(),
|
|
316
334
|
this.clearCacheForId(ids)
|
|
317
335
|
]);
|
|
318
|
-
// return null per IService contract
|
|
319
336
|
return null;
|
|
320
337
|
} catch (error) {
|
|
321
338
|
await queryRunner.rollbackTransaction();
|
|
@@ -324,7 +341,6 @@ let ApiService = class ApiService {
|
|
|
324
341
|
await queryRunner.release();
|
|
325
342
|
}
|
|
326
343
|
}
|
|
327
|
-
// Caching
|
|
328
344
|
async clearCacheForAll() {
|
|
329
345
|
await this.utilsService.clearCache(this.entityName, this.cacheManager);
|
|
330
346
|
}
|
|
@@ -340,13 +356,13 @@ let ApiService = class ApiService {
|
|
|
340
356
|
messageKey: _constants.ERROR_MESSAGES.INTERNAL
|
|
341
357
|
});
|
|
342
358
|
}
|
|
343
|
-
|
|
359
|
+
ensureArray(value) {
|
|
344
360
|
return Array.isArray(value) ? value : [
|
|
345
361
|
value
|
|
346
362
|
];
|
|
347
363
|
}
|
|
348
|
-
|
|
349
|
-
await this.
|
|
364
|
+
async executeInTransaction(operation, fn) {
|
|
365
|
+
await this.ensureDataSourceRepository();
|
|
350
366
|
const qr = this.repository.manager.connection.createQueryRunner();
|
|
351
367
|
await qr.connect();
|
|
352
368
|
await qr.startTransaction();
|
|
@@ -371,8 +387,6 @@ let ApiService = class ApiService {
|
|
|
371
387
|
await qr.release();
|
|
372
388
|
}
|
|
373
389
|
}
|
|
374
|
-
// Hooks (override in child classes)
|
|
375
|
-
async ensureRepositoryInitialized() {}
|
|
376
390
|
async beforeInsertOperation(_dto, _user, _queryRunner) {}
|
|
377
391
|
async afterInsertOperation(_entity, _user, _queryRunner) {}
|
|
378
392
|
async beforeUpdateOperation(_dto, _user, _queryRunner) {}
|
|
@@ -421,7 +435,6 @@ let ApiService = class ApiService {
|
|
|
421
435
|
isRaw: false
|
|
422
436
|
};
|
|
423
437
|
}
|
|
424
|
-
// DTO conversion
|
|
425
438
|
async convertRequestDtoToEntity(dto, user) {
|
|
426
439
|
return Array.isArray(dto) ? await this.convertArrayDtoToEntities(dto, user) : [
|
|
427
440
|
await this.convertSingleDtoToEntity(dto, user)
|
|
@@ -456,21 +469,26 @@ let ApiService = class ApiService {
|
|
|
456
469
|
async getEntityClass() {
|
|
457
470
|
return await this.repository.create();
|
|
458
471
|
}
|
|
459
|
-
constructor(entityName,
|
|
472
|
+
constructor(entityName, cacheManager, utilsService, _loggerName, isCacheable = false, moduleName, entity, dataSourceProvider){
|
|
460
473
|
_define_property(this, "entityName", void 0);
|
|
461
|
-
_define_property(this, "repository", void 0);
|
|
462
474
|
_define_property(this, "cacheManager", void 0);
|
|
463
475
|
_define_property(this, "utilsService", void 0);
|
|
464
476
|
_define_property(this, "_loggerName", void 0);
|
|
465
477
|
_define_property(this, "isCacheable", void 0);
|
|
466
478
|
_define_property(this, "moduleName", void 0);
|
|
479
|
+
_define_property(this, "repository", void 0);
|
|
480
|
+
_define_property(this, "repositoryInitialized", void 0);
|
|
481
|
+
_define_property(this, "_entity", void 0);
|
|
482
|
+
_define_property(this, "_dataSourceProvider", void 0);
|
|
467
483
|
this.entityName = entityName;
|
|
468
|
-
this.repository = repository;
|
|
469
484
|
this.cacheManager = cacheManager;
|
|
470
485
|
this.utilsService = utilsService;
|
|
471
486
|
this._loggerName = _loggerName;
|
|
472
487
|
this.isCacheable = isCacheable;
|
|
473
488
|
this.moduleName = moduleName;
|
|
489
|
+
this.repositoryInitialized = false;
|
|
490
|
+
this._entity = entity;
|
|
491
|
+
this._dataSourceProvider = dataSourceProvider;
|
|
474
492
|
}
|
|
475
493
|
};
|
|
476
494
|
_ts_decorate([
|
|
@@ -540,3 +558,19 @@ _ts_decorate([
|
|
|
540
558
|
]),
|
|
541
559
|
_ts_metadata("design:returntype", Promise)
|
|
542
560
|
], ApiService.prototype, "delete", null);
|
|
561
|
+
ApiService = _ts_decorate([
|
|
562
|
+
(0, _common.Injectable)({
|
|
563
|
+
scope: _common.Scope.REQUEST
|
|
564
|
+
}),
|
|
565
|
+
_ts_metadata("design:type", Function),
|
|
566
|
+
_ts_metadata("design:paramtypes", [
|
|
567
|
+
String,
|
|
568
|
+
typeof _hybridcacheclass.HybridCache === "undefined" ? Object : _hybridcacheclass.HybridCache,
|
|
569
|
+
typeof _utilsservice.UtilsService === "undefined" ? Object : _utilsservice.UtilsService,
|
|
570
|
+
String,
|
|
571
|
+
Boolean,
|
|
572
|
+
String,
|
|
573
|
+
typeof _typeorm.EntityTarget === "undefined" ? Object : _typeorm.EntityTarget,
|
|
574
|
+
typeof _interfaces.IDataSourceProvider === "undefined" ? Object : _interfaces.IDataSourceProvider
|
|
575
|
+
])
|
|
576
|
+
], ApiService);
|
package/cjs/classes/index.js
CHANGED
|
@@ -6,7 +6,6 @@ _export_star(require("./api-controller.class"), exports);
|
|
|
6
6
|
_export_star(require("./api-service.class"), exports);
|
|
7
7
|
_export_star(require("./handle-custome-db-column-type"), exports);
|
|
8
8
|
_export_star(require("./hybrid-cache.class"), exports);
|
|
9
|
-
_export_star(require("./request-scoped-api.service"), exports);
|
|
10
9
|
_export_star(require("./winston.logger.class"), exports);
|
|
11
10
|
_export_star(require("../constants/permissions"), exports);
|
|
12
11
|
function _export_star(from, to) {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
_export_star(require("./datasource.module"), exports);
|
|
6
5
|
_export_star(require("./multi-tenant-datasource.service"), exports);
|
|
7
6
|
function _export_star(from, to) {
|
|
8
7
|
Object.keys(from).forEach(function(k) {
|
|
@@ -43,21 +43,21 @@ function _ts_param(paramIndex, decorator) {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
46
|
+
get self() {
|
|
47
|
+
return this.constructor;
|
|
48
|
+
}
|
|
46
49
|
initializeFromOptions() {
|
|
47
50
|
if (!this.options) return;
|
|
48
|
-
if (!
|
|
51
|
+
if (!this.self.initialized) {
|
|
49
52
|
this.registerTenants();
|
|
50
|
-
|
|
53
|
+
this.self.initialized = true;
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
registerTenants() {
|
|
54
57
|
this.options?.tenants?.forEach((tenant)=>{
|
|
55
|
-
|
|
58
|
+
this.self.tenantsRegistry.set(tenant.id, tenant);
|
|
56
59
|
});
|
|
57
60
|
}
|
|
58
|
-
setTenantHeader(header) {
|
|
59
|
-
this.tenantHeader = header;
|
|
60
|
-
}
|
|
61
61
|
getDatabaseMode() {
|
|
62
62
|
return this.options?.bootstrapAppConfig?.databaseMode ?? 'single';
|
|
63
63
|
}
|
|
@@ -81,91 +81,33 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
81
81
|
return tenantId ? this.getTenant(tenantId) : null;
|
|
82
82
|
}
|
|
83
83
|
getTenant(tenantId) {
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
getAllTenants() {
|
|
87
|
-
return Array.from(MultiTenantDataSourceService.tenantsRegistry.values());
|
|
88
|
-
}
|
|
89
|
-
getActiveTenants() {
|
|
90
|
-
return this.getAllTenants();
|
|
84
|
+
return this.self.tenantsRegistry.get(tenantId) ?? null;
|
|
91
85
|
}
|
|
92
86
|
async getDataSource() {
|
|
93
87
|
return this.isMultiTenant() ? this.getTenantDataSource() : this.getSingleDataSource();
|
|
94
88
|
}
|
|
95
|
-
async getDataSourceForTenant(tenantId) {
|
|
96
|
-
const tenant = this.getTenant(tenantId);
|
|
97
|
-
if (!tenant) {
|
|
98
|
-
throw new _common.BadRequestException({
|
|
99
|
-
message: `Tenant '${tenantId}' not found`,
|
|
100
|
-
messageKey: _constants.SYSTEM_MESSAGES.TENANT_NOT_FOUND,
|
|
101
|
-
messageVariables: {
|
|
102
|
-
tenantId
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
return this.getOrCreateTenantConnection(tenant);
|
|
107
|
-
}
|
|
108
|
-
setDataSource(dataSource) {
|
|
109
|
-
MultiTenantDataSourceService.singleDataSource = dataSource;
|
|
110
|
-
}
|
|
111
89
|
async getRepository(entity) {
|
|
112
90
|
const dataSource = await this.getDataSource();
|
|
113
91
|
return dataSource.getRepository(entity);
|
|
114
92
|
}
|
|
115
|
-
async withTenant(tenantId, callback) {
|
|
116
|
-
const dataSource = await this.getDataSourceForTenant(tenantId);
|
|
117
|
-
return callback(dataSource);
|
|
118
|
-
}
|
|
119
|
-
async forAllTenants(callback) {
|
|
120
|
-
const results = new Map();
|
|
121
|
-
for (const tenant of this.getActiveTenants()){
|
|
122
|
-
try {
|
|
123
|
-
const dataSource = await this.getOrCreateTenantConnection(tenant);
|
|
124
|
-
results.set(tenant.id, await callback(tenant, dataSource));
|
|
125
|
-
} catch {
|
|
126
|
-
// Silent failure for individual tenant
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return results;
|
|
130
|
-
}
|
|
131
|
-
registerTenant(tenant) {
|
|
132
|
-
MultiTenantDataSourceService.tenantsRegistry.set(tenant.id, tenant);
|
|
133
|
-
}
|
|
134
|
-
async removeTenant(tenantId) {
|
|
135
|
-
await this.closeTenantConnection(tenantId);
|
|
136
|
-
MultiTenantDataSourceService.tenantsRegistry.delete(tenantId);
|
|
137
|
-
}
|
|
138
93
|
async closeTenantConnection(tenantId) {
|
|
139
|
-
const connection =
|
|
94
|
+
const connection = this.self.tenantConnections.get(tenantId);
|
|
140
95
|
if (connection?.isInitialized) {
|
|
141
96
|
await connection.destroy();
|
|
142
|
-
|
|
97
|
+
this.self.tenantConnections.delete(tenantId);
|
|
143
98
|
}
|
|
144
99
|
}
|
|
145
100
|
async onModuleDestroy() {
|
|
146
|
-
for (const [tenantId] of
|
|
101
|
+
for (const [tenantId] of this.self.tenantConnections){
|
|
147
102
|
await this.closeTenantConnection(tenantId);
|
|
148
103
|
}
|
|
149
104
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
MultiTenantDataSourceService.singleConnectionLock = null;
|
|
154
|
-
MultiTenantDataSourceService.tenantConnections.clear();
|
|
155
|
-
MultiTenantDataSourceService.tenantsRegistry.clear();
|
|
156
|
-
MultiTenantDataSourceService.connectionLocks.clear();
|
|
157
|
-
}
|
|
158
|
-
// Protected Methods (for subclasses)
|
|
159
|
-
/**
|
|
160
|
-
* Get single database DataSource with connection locking to prevent race conditions
|
|
161
|
-
*/ async getSingleDataSource() {
|
|
162
|
-
// Return existing initialized connection
|
|
163
|
-
if (MultiTenantDataSourceService.singleDataSource?.isInitialized) {
|
|
164
|
-
return MultiTenantDataSourceService.singleDataSource;
|
|
105
|
+
async getSingleDataSource() {
|
|
106
|
+
if (this.self.singleDataSource?.isInitialized) {
|
|
107
|
+
return this.self.singleDataSource;
|
|
165
108
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return MultiTenantDataSourceService.singleConnectionLock;
|
|
109
|
+
if (this.self.singleConnectionLock) {
|
|
110
|
+
return this.self.singleConnectionLock;
|
|
169
111
|
}
|
|
170
112
|
const config = this.getDefaultDatabaseConfig();
|
|
171
113
|
if (!config) {
|
|
@@ -174,20 +116,17 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
174
116
|
messageKey: _constants.SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
|
|
175
117
|
});
|
|
176
118
|
}
|
|
177
|
-
// Create connection with lock to prevent race conditions
|
|
178
119
|
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
179
|
-
|
|
120
|
+
this.self.singleConnectionLock = connectionPromise;
|
|
180
121
|
try {
|
|
181
122
|
const dataSource = await connectionPromise;
|
|
182
|
-
|
|
123
|
+
this.self.singleDataSource = dataSource;
|
|
183
124
|
return dataSource;
|
|
184
125
|
} finally{
|
|
185
|
-
|
|
126
|
+
this.self.singleConnectionLock = null;
|
|
186
127
|
}
|
|
187
128
|
}
|
|
188
|
-
|
|
189
|
-
* Get tenant-specific DataSource
|
|
190
|
-
*/ async getTenantDataSource() {
|
|
129
|
+
async getTenantDataSource() {
|
|
191
130
|
const tenant = this.getCurrentTenant();
|
|
192
131
|
if (!tenant) {
|
|
193
132
|
throw new _common.BadRequestException({
|
|
@@ -201,19 +140,19 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
201
140
|
return this.getOrCreateTenantConnection(tenant);
|
|
202
141
|
}
|
|
203
142
|
async getOrCreateTenantConnection(tenant) {
|
|
204
|
-
const existing =
|
|
143
|
+
const existing = this.self.tenantConnections.get(tenant.id);
|
|
205
144
|
if (existing?.isInitialized) return existing;
|
|
206
|
-
const pendingConnection =
|
|
145
|
+
const pendingConnection = this.self.connectionLocks.get(tenant.id);
|
|
207
146
|
if (pendingConnection) return pendingConnection;
|
|
208
147
|
const config = this.buildTenantDatabaseConfig(tenant);
|
|
209
148
|
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
210
|
-
|
|
149
|
+
this.self.connectionLocks.set(tenant.id, connectionPromise);
|
|
211
150
|
try {
|
|
212
151
|
const dataSource = await connectionPromise;
|
|
213
|
-
|
|
152
|
+
this.self.tenantConnections.set(tenant.id, dataSource);
|
|
214
153
|
return dataSource;
|
|
215
154
|
} finally{
|
|
216
|
-
|
|
155
|
+
this.self.connectionLocks.delete(tenant.id);
|
|
217
156
|
}
|
|
218
157
|
}
|
|
219
158
|
getDefaultDatabaseConfig() {
|
|
@@ -1,20 +1,28 @@
|
|
|
1
|
-
import { QueryRunner, Repository, SelectQueryBuilder } from 'typeorm';
|
|
1
|
+
import { DataSource, EntityTarget, QueryRunner, Repository, SelectQueryBuilder } from 'typeorm';
|
|
2
2
|
import { DeleteDto, FilterAndPaginationDto } from '../dtos';
|
|
3
3
|
import { Identity, RawEntity } from '../entities';
|
|
4
|
-
import { ILoggedUserInfo, IService } from '../interfaces';
|
|
4
|
+
import { IDataSourceProvider, ILoggedUserInfo, IService } from '../interfaces';
|
|
5
5
|
import { UtilsService } from '../modules/utils/utils.service';
|
|
6
6
|
import { HybridCache } from './hybrid-cache.class';
|
|
7
7
|
export declare abstract class ApiService<CreateDtoT extends object, UpdateDtoT extends {
|
|
8
8
|
id: string;
|
|
9
9
|
}, InterfaceT extends Identity, EntityT extends Identity> implements IService<CreateDtoT, UpdateDtoT, InterfaceT> {
|
|
10
10
|
protected entityName: string;
|
|
11
|
-
protected repository: Repository<EntityT>;
|
|
12
11
|
protected cacheManager: HybridCache;
|
|
13
12
|
protected utilsService: UtilsService;
|
|
14
13
|
protected _loggerName: string;
|
|
15
14
|
protected isCacheable: boolean;
|
|
16
|
-
protected moduleName?: string;
|
|
17
|
-
|
|
15
|
+
protected moduleName?: string | undefined;
|
|
16
|
+
protected repository: Repository<EntityT>;
|
|
17
|
+
private repositoryInitialized;
|
|
18
|
+
private _entity?;
|
|
19
|
+
private _dataSourceProvider?;
|
|
20
|
+
constructor(entityName: string, cacheManager: HybridCache, utilsService: UtilsService, _loggerName: string, isCacheable?: boolean, moduleName?: string | undefined, entity?: EntityTarget<EntityT>, dataSourceProvider?: IDataSourceProvider);
|
|
21
|
+
protected resolveEntity(): EntityTarget<EntityT>;
|
|
22
|
+
protected getDataSourceProvider(): IDataSourceProvider;
|
|
23
|
+
protected ensureDataSourceRepository(): Promise<void>;
|
|
24
|
+
protected resolveRepositories(entities: EntityTarget<any>[]): Promise<Repository<any>[]>;
|
|
25
|
+
protected getDataSourceForService(): Promise<DataSource>;
|
|
18
26
|
insert(dto: CreateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
|
|
19
27
|
insertMany(dtos: Array<CreateDtoT>, user: ILoggedUserInfo | null): Promise<InterfaceT[]>;
|
|
20
28
|
update(dto: UpdateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
|
|
@@ -32,7 +40,6 @@ export declare abstract class ApiService<CreateDtoT extends object, UpdateDtoT e
|
|
|
32
40
|
private handleError;
|
|
33
41
|
private ensureArray;
|
|
34
42
|
private executeInTransaction;
|
|
35
|
-
protected ensureRepositoryInitialized(): Promise<void>;
|
|
36
43
|
protected beforeInsertOperation(_dto: CreateDtoT | Array<CreateDtoT>, _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
|
|
37
44
|
protected afterInsertOperation(_entity: EntityT[], _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
|
|
38
45
|
protected beforeUpdateOperation(_dto: UpdateDtoT | Array<UpdateDtoT>, _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
|
package/classes/index.d.ts
CHANGED
|
@@ -2,6 +2,5 @@ export * from './api-controller.class';
|
|
|
2
2
|
export * from './api-service.class';
|
|
3
3
|
export * from './handle-custome-db-column-type';
|
|
4
4
|
export * from './hybrid-cache.class';
|
|
5
|
-
export * from './request-scoped-api.service';
|
|
6
5
|
export * from './winston.logger.class';
|
|
7
6
|
export * from '../constants/permissions';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CurrentUser: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>>)[]) => ParameterDecorator;
|
|
1
|
+
export declare const CurrentUser: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>> | undefined)[]) => ParameterDecorator;
|