@flusys/nestjs-shared 5.1.1 → 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-controller.class.js +16 -10
- package/cjs/classes/api-service.class.js +67 -35
- package/cjs/classes/index.js +0 -1
- package/cjs/dtos/identity-response.dto.js +4 -8
- package/cjs/entities/identity.js +8 -2
- package/cjs/modules/datasource/index.js +0 -1
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +24 -85
- package/classes/api-controller.class.d.ts +1 -0
- 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-controller.class.js +16 -10
- package/fesm/classes/api-service.class.js +70 -38
- package/fesm/classes/index.js +0 -1
- package/fesm/dtos/identity-response.dto.js +4 -8
- package/fesm/entities/identity.js +8 -2
- 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
|
@@ -11,11 +11,11 @@ Object.defineProperty(exports, "createApiController", {
|
|
|
11
11
|
const _common = require("@nestjs/common");
|
|
12
12
|
const _swagger = require("@nestjs/swagger");
|
|
13
13
|
const _classtransformer = require("class-transformer");
|
|
14
|
+
const _messagekeys = require("../constants/message-keys");
|
|
14
15
|
const _decorators = require("../decorators");
|
|
15
16
|
const _apiresponsedecorator = require("../decorators/api-response.decorator");
|
|
16
17
|
const _dtos = require("../dtos");
|
|
17
18
|
const _exceptions = require("../exceptions");
|
|
18
|
-
const _messagekeys = require("../constants/message-keys");
|
|
19
19
|
const _guards = require("../guards");
|
|
20
20
|
const _interceptors = require("../interceptors");
|
|
21
21
|
function _define_property(obj, key, value) {
|
|
@@ -108,6 +108,12 @@ function _ts_param(paramIndex, decorator) {
|
|
|
108
108
|
return (0, _common.applyDecorators)(...decorators);
|
|
109
109
|
}
|
|
110
110
|
function createApiController(createDtoClass, updateDtoClass, responseDtoClass, options = {}) {
|
|
111
|
+
const excludeExtraneousValues = options.excludeExtraneousValues ?? true;
|
|
112
|
+
function toDto(cls, input) {
|
|
113
|
+
return (0, _classtransformer.plainToInstance)(cls, input, {
|
|
114
|
+
excludeExtraneousValues
|
|
115
|
+
});
|
|
116
|
+
}
|
|
111
117
|
// Entity name for message keys (defaults to 'item')
|
|
112
118
|
const entityName = options.entityName ?? 'item';
|
|
113
119
|
// Determine if security is global (applies to all) or per-endpoint
|
|
@@ -163,7 +169,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
163
169
|
});
|
|
164
170
|
}
|
|
165
171
|
const entity = await this.service.insert(addDto, user);
|
|
166
|
-
const data = (
|
|
172
|
+
const data = toDto(responseDtoClass, entity);
|
|
167
173
|
return {
|
|
168
174
|
success: true,
|
|
169
175
|
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} created successfully`,
|
|
@@ -183,7 +189,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
183
189
|
});
|
|
184
190
|
}
|
|
185
191
|
const entities = await this.service.insertMany(addDto, user);
|
|
186
|
-
const data = entities.map((item)=>(
|
|
192
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
187
193
|
return {
|
|
188
194
|
success: true,
|
|
189
195
|
message: `${data.length} ${entityName}s created successfully`,
|
|
@@ -211,7 +217,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
211
217
|
});
|
|
212
218
|
}
|
|
213
219
|
const entity = await this.service.findById(id, user, body?.select);
|
|
214
|
-
const data = (
|
|
220
|
+
const data = toDto(responseDtoClass, entity);
|
|
215
221
|
return {
|
|
216
222
|
success: true,
|
|
217
223
|
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
|
|
@@ -231,7 +237,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
231
237
|
});
|
|
232
238
|
}
|
|
233
239
|
const entities = await this.service.findByIds(body.ids, user, body?.select);
|
|
234
|
-
const data = (
|
|
240
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
235
241
|
return {
|
|
236
242
|
success: true,
|
|
237
243
|
message: `${data.length} ${entityName}${data.length !== 1 ? 's' : ''} retrieved successfully`,
|
|
@@ -258,7 +264,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
258
264
|
});
|
|
259
265
|
}
|
|
260
266
|
const entity = await this.service.update(updateDto, user);
|
|
261
|
-
const data = (
|
|
267
|
+
const data = toDto(responseDtoClass, entity);
|
|
262
268
|
return {
|
|
263
269
|
success: true,
|
|
264
270
|
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} updated successfully`,
|
|
@@ -278,7 +284,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
278
284
|
});
|
|
279
285
|
}
|
|
280
286
|
const entities = await this.service.updateMany(updateDtos, user);
|
|
281
|
-
const data = (
|
|
287
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
282
288
|
return {
|
|
283
289
|
success: true,
|
|
284
290
|
message: `${data.length} ${entityName}s updated successfully`,
|
|
@@ -315,7 +321,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
315
321
|
}
|
|
316
322
|
}
|
|
317
323
|
const allEntities = await this.service.bulkUpsert(toInsert, toUpdate, user);
|
|
318
|
-
const data = (
|
|
324
|
+
const data = allEntities.map((item)=>toDto(responseDtoClass, item));
|
|
319
325
|
return {
|
|
320
326
|
success: true,
|
|
321
327
|
message: `${data.length} ${entityName}s upserted successfully`,
|
|
@@ -353,7 +359,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
353
359
|
throw new _exceptions.NotFoundException(entityName);
|
|
354
360
|
}
|
|
355
361
|
const entity = result.data[0];
|
|
356
|
-
const data = (
|
|
362
|
+
const data = toDto(responseDtoClass, entity);
|
|
357
363
|
return {
|
|
358
364
|
success: true,
|
|
359
365
|
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
|
|
@@ -373,7 +379,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
373
379
|
});
|
|
374
380
|
}
|
|
375
381
|
const result = await this.service.getAll(search ?? '', filterAndPaginationDto, user);
|
|
376
|
-
const data = (
|
|
382
|
+
const data = result.data.map((item)=>toDto(responseDtoClass, item));
|
|
377
383
|
const page = filterAndPaginationDto.pagination?.currentPage ?? 0;
|
|
378
384
|
const pageSize = filterAndPaginationDto.pagination?.pageSize ?? data.length;
|
|
379
385
|
const totalPages = pageSize > 0 ? Math.ceil(result.total / pageSize) : 1;
|
|
@@ -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,43 +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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
query = selectQuery;
|
|
202
|
-
isRaw = isRaw || selectIsRaw;
|
|
203
|
-
}
|
|
204
|
-
// Apply sort
|
|
223
|
+
const { query: selectQuery, isRaw: selectIsRaw } = await this.getSelectQuery(query, user, filterAndPaginationDto.select);
|
|
224
|
+
query = selectQuery;
|
|
225
|
+
isRaw = isRaw || selectIsRaw;
|
|
205
226
|
if (filterAndPaginationDto.sort) {
|
|
206
227
|
const { query: sortQuery, isRaw: sortIsRaw } = await this.getSortQuery(query, filterAndPaginationDto.sort, user);
|
|
207
228
|
query = sortQuery;
|
|
208
229
|
isRaw = isRaw || sortIsRaw;
|
|
209
230
|
}
|
|
210
|
-
// Include soft-deleted if requested
|
|
211
231
|
if (filterAndPaginationDto.withDeleted) query.withDeleted();
|
|
212
|
-
// Extra manipulation
|
|
213
232
|
const { query: extraQuery, isRaw: extraIsRaw } = await this.getExtraManipulateQuery(query, filterAndPaginationDto, user);
|
|
214
233
|
query = extraQuery;
|
|
215
234
|
isRaw = isRaw || extraIsRaw;
|
|
216
|
-
// Pagination
|
|
217
235
|
if (filterAndPaginationDto.pagination) {
|
|
218
236
|
const offset = filterAndPaginationDto.pagination.currentPage * filterAndPaginationDto.pagination.pageSize;
|
|
219
237
|
const limit = filterAndPaginationDto.pagination.pageSize;
|
|
@@ -223,7 +241,6 @@ let ApiService = class ApiService {
|
|
|
223
241
|
query.skip(offset).take(limit);
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
|
-
// Generate cache key
|
|
227
244
|
let cacheKey = '';
|
|
228
245
|
if (this.isCacheable) {
|
|
229
246
|
cacheKey = this.utilsService.getCacheKey(this.entityName, {
|
|
@@ -237,7 +254,6 @@ let ApiService = class ApiService {
|
|
|
237
254
|
}
|
|
238
255
|
}
|
|
239
256
|
if (isRaw) {
|
|
240
|
-
// Handle raw query count manually
|
|
241
257
|
const rawSubQuery = query.clone().offset(undefined).limit(undefined).distinct(true);
|
|
242
258
|
const countSql = `
|
|
243
259
|
SELECT COUNT(*) as count
|
|
@@ -285,7 +301,7 @@ let ApiService = class ApiService {
|
|
|
285
301
|
}
|
|
286
302
|
}
|
|
287
303
|
async delete(option, user) {
|
|
288
|
-
await this.
|
|
304
|
+
await this.ensureDataSourceRepository();
|
|
289
305
|
const queryRunner = this.repository.manager.connection.createQueryRunner();
|
|
290
306
|
await queryRunner.connect();
|
|
291
307
|
await queryRunner.startTransaction();
|
|
@@ -317,7 +333,6 @@ let ApiService = class ApiService {
|
|
|
317
333
|
this.clearCacheForAll(),
|
|
318
334
|
this.clearCacheForId(ids)
|
|
319
335
|
]);
|
|
320
|
-
// return null per IService contract
|
|
321
336
|
return null;
|
|
322
337
|
} catch (error) {
|
|
323
338
|
await queryRunner.rollbackTransaction();
|
|
@@ -326,7 +341,6 @@ let ApiService = class ApiService {
|
|
|
326
341
|
await queryRunner.release();
|
|
327
342
|
}
|
|
328
343
|
}
|
|
329
|
-
// Caching
|
|
330
344
|
async clearCacheForAll() {
|
|
331
345
|
await this.utilsService.clearCache(this.entityName, this.cacheManager);
|
|
332
346
|
}
|
|
@@ -342,13 +356,13 @@ let ApiService = class ApiService {
|
|
|
342
356
|
messageKey: _constants.ERROR_MESSAGES.INTERNAL
|
|
343
357
|
});
|
|
344
358
|
}
|
|
345
|
-
|
|
359
|
+
ensureArray(value) {
|
|
346
360
|
return Array.isArray(value) ? value : [
|
|
347
361
|
value
|
|
348
362
|
];
|
|
349
363
|
}
|
|
350
|
-
|
|
351
|
-
await this.
|
|
364
|
+
async executeInTransaction(operation, fn) {
|
|
365
|
+
await this.ensureDataSourceRepository();
|
|
352
366
|
const qr = this.repository.manager.connection.createQueryRunner();
|
|
353
367
|
await qr.connect();
|
|
354
368
|
await qr.startTransaction();
|
|
@@ -373,8 +387,6 @@ let ApiService = class ApiService {
|
|
|
373
387
|
await qr.release();
|
|
374
388
|
}
|
|
375
389
|
}
|
|
376
|
-
// Hooks (override in child classes)
|
|
377
|
-
async ensureRepositoryInitialized() {}
|
|
378
390
|
async beforeInsertOperation(_dto, _user, _queryRunner) {}
|
|
379
391
|
async afterInsertOperation(_entity, _user, _queryRunner) {}
|
|
380
392
|
async beforeUpdateOperation(_dto, _user, _queryRunner) {}
|
|
@@ -423,7 +435,6 @@ let ApiService = class ApiService {
|
|
|
423
435
|
isRaw: false
|
|
424
436
|
};
|
|
425
437
|
}
|
|
426
|
-
// DTO conversion
|
|
427
438
|
async convertRequestDtoToEntity(dto, user) {
|
|
428
439
|
return Array.isArray(dto) ? await this.convertArrayDtoToEntities(dto, user) : [
|
|
429
440
|
await this.convertSingleDtoToEntity(dto, user)
|
|
@@ -458,21 +469,26 @@ let ApiService = class ApiService {
|
|
|
458
469
|
async getEntityClass() {
|
|
459
470
|
return await this.repository.create();
|
|
460
471
|
}
|
|
461
|
-
constructor(entityName,
|
|
472
|
+
constructor(entityName, cacheManager, utilsService, _loggerName, isCacheable = false, moduleName, entity, dataSourceProvider){
|
|
462
473
|
_define_property(this, "entityName", void 0);
|
|
463
|
-
_define_property(this, "repository", void 0);
|
|
464
474
|
_define_property(this, "cacheManager", void 0);
|
|
465
475
|
_define_property(this, "utilsService", void 0);
|
|
466
476
|
_define_property(this, "_loggerName", void 0);
|
|
467
477
|
_define_property(this, "isCacheable", void 0);
|
|
468
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);
|
|
469
483
|
this.entityName = entityName;
|
|
470
|
-
this.repository = repository;
|
|
471
484
|
this.cacheManager = cacheManager;
|
|
472
485
|
this.utilsService = utilsService;
|
|
473
486
|
this._loggerName = _loggerName;
|
|
474
487
|
this.isCacheable = isCacheable;
|
|
475
488
|
this.moduleName = moduleName;
|
|
489
|
+
this.repositoryInitialized = false;
|
|
490
|
+
this._entity = entity;
|
|
491
|
+
this._dataSourceProvider = dataSourceProvider;
|
|
476
492
|
}
|
|
477
493
|
};
|
|
478
494
|
_ts_decorate([
|
|
@@ -542,3 +558,19 @@ _ts_decorate([
|
|
|
542
558
|
]),
|
|
543
559
|
_ts_metadata("design:returntype", Promise)
|
|
544
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) {
|
|
@@ -8,8 +8,8 @@ Object.defineProperty(exports, "IdentityResponseDto", {
|
|
|
8
8
|
return IdentityResponseDto;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const _classtransformer = require("class-transformer");
|
|
12
11
|
const _swagger = require("@nestjs/swagger");
|
|
12
|
+
const _classtransformer = require("class-transformer");
|
|
13
13
|
function _define_property(obj, key, value) {
|
|
14
14
|
if (key in obj) {
|
|
15
15
|
Object.defineProperty(obj, key, {
|
|
@@ -44,28 +44,28 @@ let IdentityResponseDto = class IdentityResponseDto {
|
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
46
|
_ts_decorate([
|
|
47
|
+
(0, _classtransformer.Expose)(),
|
|
47
48
|
(0, _swagger.ApiProperty)({
|
|
48
49
|
type: String,
|
|
49
50
|
format: 'uuid'
|
|
50
51
|
}),
|
|
51
|
-
(0, _classtransformer.Expose)(),
|
|
52
52
|
_ts_metadata("design:type", String)
|
|
53
53
|
], IdentityResponseDto.prototype, "id", void 0);
|
|
54
54
|
_ts_decorate([
|
|
55
|
+
(0, _classtransformer.Expose)(),
|
|
55
56
|
(0, _swagger.ApiProperty)({
|
|
56
57
|
type: String,
|
|
57
58
|
format: 'date-time'
|
|
58
59
|
}),
|
|
59
|
-
(0, _classtransformer.Expose)(),
|
|
60
60
|
(0, _classtransformer.Type)(()=>Date),
|
|
61
61
|
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
62
62
|
], IdentityResponseDto.prototype, "createdAt", void 0);
|
|
63
63
|
_ts_decorate([
|
|
64
|
+
(0, _classtransformer.Expose)(),
|
|
64
65
|
(0, _swagger.ApiProperty)({
|
|
65
66
|
type: String,
|
|
66
67
|
format: 'date-time'
|
|
67
68
|
}),
|
|
68
|
-
(0, _classtransformer.Expose)(),
|
|
69
69
|
(0, _classtransformer.Type)(()=>Date),
|
|
70
70
|
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
71
71
|
], IdentityResponseDto.prototype, "updatedAt", void 0);
|
|
@@ -76,7 +76,6 @@ _ts_decorate([
|
|
|
76
76
|
nullable: true,
|
|
77
77
|
required: false
|
|
78
78
|
}),
|
|
79
|
-
(0, _classtransformer.Expose)(),
|
|
80
79
|
(0, _classtransformer.Type)(()=>Date),
|
|
81
80
|
_ts_metadata("design:type", Object)
|
|
82
81
|
], IdentityResponseDto.prototype, "deletedAt", void 0);
|
|
@@ -87,7 +86,6 @@ _ts_decorate([
|
|
|
87
86
|
nullable: true,
|
|
88
87
|
required: false
|
|
89
88
|
}),
|
|
90
|
-
(0, _classtransformer.Expose)(),
|
|
91
89
|
_ts_metadata("design:type", Object)
|
|
92
90
|
], IdentityResponseDto.prototype, "createdById", void 0);
|
|
93
91
|
_ts_decorate([
|
|
@@ -97,7 +95,6 @@ _ts_decorate([
|
|
|
97
95
|
nullable: true,
|
|
98
96
|
required: false
|
|
99
97
|
}),
|
|
100
|
-
(0, _classtransformer.Expose)(),
|
|
101
98
|
_ts_metadata("design:type", Object)
|
|
102
99
|
], IdentityResponseDto.prototype, "updatedById", void 0);
|
|
103
100
|
_ts_decorate([
|
|
@@ -107,6 +104,5 @@ _ts_decorate([
|
|
|
107
104
|
nullable: true,
|
|
108
105
|
required: false
|
|
109
106
|
}),
|
|
110
|
-
(0, _classtransformer.Expose)(),
|
|
111
107
|
_ts_metadata("design:type", Object)
|
|
112
108
|
], IdentityResponseDto.prototype, "deletedById", void 0);
|
package/cjs/entities/identity.js
CHANGED
|
@@ -49,14 +49,16 @@ _ts_decorate([
|
|
|
49
49
|
_ts_decorate([
|
|
50
50
|
(0, _typeorm.CreateDateColumn)({
|
|
51
51
|
name: 'created_at',
|
|
52
|
-
type: 'timestamp'
|
|
52
|
+
type: 'timestamp',
|
|
53
|
+
select: false
|
|
53
54
|
}),
|
|
54
55
|
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
55
56
|
], Identity.prototype, "createdAt", void 0);
|
|
56
57
|
_ts_decorate([
|
|
57
58
|
(0, _typeorm.UpdateDateColumn)({
|
|
58
59
|
name: 'updated_at',
|
|
59
|
-
type: 'timestamp'
|
|
60
|
+
type: 'timestamp',
|
|
61
|
+
select: false
|
|
60
62
|
}),
|
|
61
63
|
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
62
64
|
], Identity.prototype, "updatedAt", void 0);
|
|
@@ -64,6 +66,7 @@ _ts_decorate([
|
|
|
64
66
|
(0, _typeorm.DeleteDateColumn)({
|
|
65
67
|
name: 'deleted_at',
|
|
66
68
|
type: 'timestamp',
|
|
69
|
+
select: false,
|
|
67
70
|
nullable: true
|
|
68
71
|
}),
|
|
69
72
|
_ts_metadata("design:type", Object)
|
|
@@ -72,6 +75,7 @@ _ts_decorate([
|
|
|
72
75
|
(0, _typeorm.Column)({
|
|
73
76
|
name: 'created_by_id',
|
|
74
77
|
type: 'uuid',
|
|
78
|
+
select: false,
|
|
75
79
|
nullable: true
|
|
76
80
|
}),
|
|
77
81
|
_ts_metadata("design:type", Object)
|
|
@@ -80,6 +84,7 @@ _ts_decorate([
|
|
|
80
84
|
(0, _typeorm.Column)({
|
|
81
85
|
name: 'updated_by_id',
|
|
82
86
|
type: 'uuid',
|
|
87
|
+
select: false,
|
|
83
88
|
nullable: true
|
|
84
89
|
}),
|
|
85
90
|
_ts_metadata("design:type", Object)
|
|
@@ -88,6 +93,7 @@ _ts_decorate([
|
|
|
88
93
|
(0, _typeorm.Column)({
|
|
89
94
|
name: 'deleted_by_id',
|
|
90
95
|
type: 'uuid',
|
|
96
|
+
select: false,
|
|
91
97
|
nullable: true
|
|
92
98
|
}),
|
|
93
99
|
_ts_metadata("design:type", Object)
|
|
@@ -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() {
|