@flusys/nestjs-shared 4.0.2 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +448 -1561
- package/cjs/classes/api-controller.class.js +207 -11
- package/cjs/classes/api-service.class.js +41 -5
- package/cjs/dtos/get-by-ids.dto.js +65 -0
- package/cjs/dtos/index.js +1 -0
- package/cjs/entities/index.js +1 -0
- package/cjs/entities/raw-type.js +4 -0
- package/classes/api-controller.class.d.ts +9 -3
- package/classes/api-service.class.d.ts +6 -5
- package/dtos/get-by-ids.dto.d.ts +4 -0
- package/dtos/index.d.ts +1 -0
- package/entities/index.d.ts +1 -0
- package/entities/raw-type.d.ts +1 -0
- package/fesm/classes/api-controller.class.js +208 -12
- package/fesm/classes/api-service.class.js +41 -5
- package/fesm/dtos/get-by-ids.dto.js +55 -0
- package/fesm/dtos/index.js +1 -0
- package/fesm/entities/index.js +1 -0
- package/fesm/entities/raw-type.js +1 -0
- package/interfaces/api.interface.d.ts +2 -0
- package/package.json +2 -2
|
@@ -25,14 +25,15 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import { CurrentUser, Public, RequirePermission, RequireAnyPermission, RequirePermissionLogic } from '../decorators';
|
|
29
|
-
import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto } from '../dtos';
|
|
30
|
-
import { JwtAuthGuard, PermissionGuard } from '../guards';
|
|
31
|
-
import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '../interceptors';
|
|
32
28
|
import { applyDecorators, Body, HttpCode, HttpStatus, Param, Post, Query, UseGuards, UseInterceptors, Version, VERSION_NEUTRAL } from '@nestjs/common';
|
|
33
|
-
import { ApiBearerAuth, ApiBody, ApiHeader, ApiOperation, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger';
|
|
29
|
+
import { ApiBearerAuth, ApiBody, ApiExcludeEndpoint, ApiHeader, ApiOperation, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger';
|
|
34
30
|
import { plainToInstance } from 'class-transformer';
|
|
31
|
+
import { CurrentUser, Public, RequireAnyPermission, RequirePermission, RequirePermissionLogic } from '../decorators';
|
|
35
32
|
import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
33
|
+
import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto, GetByIdsDto } from '../dtos';
|
|
34
|
+
import { ForbiddenException, NotFoundException } from '../exceptions';
|
|
35
|
+
import { JwtAuthGuard, PermissionGuard } from '../guards';
|
|
36
|
+
import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '../interceptors';
|
|
36
37
|
/**
|
|
37
38
|
* Helper to normalize security config
|
|
38
39
|
*/ function normalizeSecurity(config) {
|
|
@@ -44,6 +45,24 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
44
45
|
};
|
|
45
46
|
return config;
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Helper to check if endpoint is enabled
|
|
50
|
+
*/ function isEndpointEnabled(endpoint, enabledEndpoints) {
|
|
51
|
+
if (!enabledEndpoints || enabledEndpoints === 'all') return true;
|
|
52
|
+
return enabledEndpoints.includes(endpoint);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Helper to conditionally exclude endpoint from Swagger if disabled
|
|
56
|
+
*/ function createEndpointDecorators(security, endpoint, enabledEndpoints) {
|
|
57
|
+
const decorators = [];
|
|
58
|
+
// Add security decorators
|
|
59
|
+
decorators.push(createSecurityDecorators(security));
|
|
60
|
+
// Exclude from Swagger if endpoint is disabled
|
|
61
|
+
if (!isEndpointEnabled(endpoint, enabledEndpoints)) {
|
|
62
|
+
decorators.push(ApiExcludeEndpoint());
|
|
63
|
+
}
|
|
64
|
+
return applyDecorators(...decorators);
|
|
65
|
+
}
|
|
47
66
|
/**
|
|
48
67
|
* Creates security decorators based on configuration
|
|
49
68
|
*/ function createSecurityDecorators(security) {
|
|
@@ -86,7 +105,10 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
86
105
|
'insert',
|
|
87
106
|
'insertMany',
|
|
88
107
|
'getById',
|
|
108
|
+
'getByIds',
|
|
89
109
|
'getAll',
|
|
110
|
+
'bulkUpsert',
|
|
111
|
+
'getByFilter',
|
|
90
112
|
'update',
|
|
91
113
|
'updateMany',
|
|
92
114
|
'delete'
|
|
@@ -105,13 +127,23 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
105
127
|
insert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insert),
|
|
106
128
|
insertMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insertMany),
|
|
107
129
|
getById: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getById),
|
|
130
|
+
getByIds: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByIds),
|
|
108
131
|
getAll: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getAll),
|
|
132
|
+
getByFilter: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByFilter),
|
|
133
|
+
bulkUpsert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.bulkUpsert),
|
|
109
134
|
update: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.update),
|
|
110
135
|
updateMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.updateMany),
|
|
111
136
|
delete: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.delete)
|
|
112
137
|
};
|
|
113
138
|
let ApiController = class ApiController {
|
|
139
|
+
isEnabled(endpoint) {
|
|
140
|
+
if (this.enabledEndpoints === 'all') return true;
|
|
141
|
+
return Array.isArray(this.enabledEndpoints) && this.enabledEndpoints.includes(endpoint);
|
|
142
|
+
}
|
|
114
143
|
async insert(addDto, user) {
|
|
144
|
+
if (!this.isEnabled('insert')) {
|
|
145
|
+
throw new ForbiddenException(`Endpoint 'insert' is disabled`);
|
|
146
|
+
}
|
|
115
147
|
const entity = await this.service.insert(addDto, user);
|
|
116
148
|
const data = plainToInstance(responseDtoClass, entity);
|
|
117
149
|
return {
|
|
@@ -122,6 +154,9 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
122
154
|
};
|
|
123
155
|
}
|
|
124
156
|
async insertMany(addDto, user) {
|
|
157
|
+
if (!this.isEnabled('insertMany')) {
|
|
158
|
+
throw new ForbiddenException(`Endpoint 'insertMany' is disabled`);
|
|
159
|
+
}
|
|
125
160
|
const entities = await this.service.insertMany(addDto, user);
|
|
126
161
|
const data = entities.map((item)=>plainToInstance(responseDtoClass, item));
|
|
127
162
|
return {
|
|
@@ -140,6 +175,9 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
140
175
|
};
|
|
141
176
|
}
|
|
142
177
|
async getById(id, body, user) {
|
|
178
|
+
if (!this.isEnabled('getById')) {
|
|
179
|
+
throw new ForbiddenException(`Endpoint 'getById' is disabled`);
|
|
180
|
+
}
|
|
143
181
|
const entity = await this.service.findById(id, user, body?.select);
|
|
144
182
|
const data = plainToInstance(responseDtoClass, entity);
|
|
145
183
|
return {
|
|
@@ -149,7 +187,30 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
149
187
|
data
|
|
150
188
|
};
|
|
151
189
|
}
|
|
190
|
+
async getByIds(body, user) {
|
|
191
|
+
if (!this.isEnabled('getByIds')) {
|
|
192
|
+
throw new ForbiddenException(`Endpoint 'getByIds' is disabled`);
|
|
193
|
+
}
|
|
194
|
+
const entities = await this.service.findByIds(body.ids, user, body?.select);
|
|
195
|
+
const data = plainToInstance(responseDtoClass, entities);
|
|
196
|
+
return {
|
|
197
|
+
success: true,
|
|
198
|
+
message: `${data.length} ${entityName}${data.length !== 1 ? 's' : ''} retrieved successfully`,
|
|
199
|
+
messageKey: `${entityName}.get.by.ids.success`,
|
|
200
|
+
data,
|
|
201
|
+
meta: {
|
|
202
|
+
total: data.length,
|
|
203
|
+
page: 0,
|
|
204
|
+
pageSize: data.length,
|
|
205
|
+
count: data.length,
|
|
206
|
+
totalPages: 1
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
152
210
|
async update(updateDto, user) {
|
|
211
|
+
if (!this.isEnabled('update')) {
|
|
212
|
+
throw new ForbiddenException(`Endpoint 'update' is disabled`);
|
|
213
|
+
}
|
|
153
214
|
const entity = await this.service.update(updateDto, user);
|
|
154
215
|
const data = plainToInstance(responseDtoClass, entity);
|
|
155
216
|
return {
|
|
@@ -160,6 +221,9 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
160
221
|
};
|
|
161
222
|
}
|
|
162
223
|
async updateMany(updateDtos, user) {
|
|
224
|
+
if (!this.isEnabled('updateMany')) {
|
|
225
|
+
throw new ForbiddenException(`Endpoint 'updateMany' is disabled`);
|
|
226
|
+
}
|
|
163
227
|
const entities = await this.service.updateMany(updateDtos, user);
|
|
164
228
|
const data = plainToInstance(responseDtoClass, entities);
|
|
165
229
|
return {
|
|
@@ -177,7 +241,63 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
177
241
|
}
|
|
178
242
|
};
|
|
179
243
|
}
|
|
244
|
+
async bulkUpsert(dtos, user) {
|
|
245
|
+
if (!this.isEnabled('bulkUpsert')) {
|
|
246
|
+
throw new ForbiddenException(`Endpoint 'bulkUpsert' is disabled`);
|
|
247
|
+
}
|
|
248
|
+
const toInsert = [];
|
|
249
|
+
const toUpdate = [];
|
|
250
|
+
for (const dto of dtos){
|
|
251
|
+
if ('id' in dto && dto.id) {
|
|
252
|
+
toUpdate.push(dto);
|
|
253
|
+
} else {
|
|
254
|
+
toInsert.push(dto);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const allEntities = await this.service.bulkUpsert(toInsert, toUpdate, user);
|
|
258
|
+
const data = plainToInstance(responseDtoClass, allEntities);
|
|
259
|
+
return {
|
|
260
|
+
success: true,
|
|
261
|
+
message: `${data.length} ${entityName}s upserted successfully`,
|
|
262
|
+
messageKey: `${entityName}.upsert.many.success`,
|
|
263
|
+
messageVariables: {
|
|
264
|
+
count: data.length
|
|
265
|
+
},
|
|
266
|
+
data,
|
|
267
|
+
meta: {
|
|
268
|
+
count: data.length,
|
|
269
|
+
total: dtos.length,
|
|
270
|
+
failed: dtos.length - data.length
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
async getByFilter(filter, user) {
|
|
275
|
+
if (!this.isEnabled('getByFilter')) {
|
|
276
|
+
throw new ForbiddenException(`Endpoint 'getByFilter' is disabled`);
|
|
277
|
+
}
|
|
278
|
+
const result = await this.service.getAll('', {
|
|
279
|
+
filter,
|
|
280
|
+
pagination: {
|
|
281
|
+
currentPage: 0,
|
|
282
|
+
pageSize: 1
|
|
283
|
+
}
|
|
284
|
+
}, user);
|
|
285
|
+
if (!result.data || result.data.length === 0) {
|
|
286
|
+
throw new NotFoundException(entityName);
|
|
287
|
+
}
|
|
288
|
+
const entity = result.data[0];
|
|
289
|
+
const data = plainToInstance(responseDtoClass, entity);
|
|
290
|
+
return {
|
|
291
|
+
success: true,
|
|
292
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
|
|
293
|
+
messageKey: `${entityName}.get.by.filter.success`,
|
|
294
|
+
data
|
|
295
|
+
};
|
|
296
|
+
}
|
|
180
297
|
async getAll(filterAndPaginationDto, user, search) {
|
|
298
|
+
if (!this.isEnabled('getAll')) {
|
|
299
|
+
throw new ForbiddenException(`Endpoint 'getAll' is disabled`);
|
|
300
|
+
}
|
|
181
301
|
const result = await this.service.getAll(search ?? '', filterAndPaginationDto, user);
|
|
182
302
|
const data = plainToInstance(responseDtoClass, result.data);
|
|
183
303
|
const page = filterAndPaginationDto.pagination?.currentPage ?? 0;
|
|
@@ -199,6 +319,9 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
199
319
|
};
|
|
200
320
|
}
|
|
201
321
|
async delete(deleteDto, user) {
|
|
322
|
+
if (!this.isEnabled('delete')) {
|
|
323
|
+
throw new ForbiddenException(`Endpoint 'delete' is disabled`);
|
|
324
|
+
}
|
|
202
325
|
await this.service.delete(deleteDto, user);
|
|
203
326
|
const count = Array.isArray(deleteDto.id) ? deleteDto.id.length : 1;
|
|
204
327
|
const action = deleteDto.type === 'restore' ? 'restore' : 'delete';
|
|
@@ -213,11 +336,13 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
213
336
|
}
|
|
214
337
|
constructor(service){
|
|
215
338
|
_define_property(this, "service", void 0);
|
|
339
|
+
_define_property(this, "enabledEndpoints", void 0);
|
|
216
340
|
this.service = service;
|
|
341
|
+
this.enabledEndpoints = options.enabledEndpoints ?? 'all';
|
|
217
342
|
}
|
|
218
343
|
};
|
|
219
344
|
_ts_decorate([
|
|
220
|
-
|
|
345
|
+
createEndpointDecorators(security.insert, 'insert', options.enabledEndpoints),
|
|
221
346
|
Version(VERSION_NEUTRAL),
|
|
222
347
|
Post('insert'),
|
|
223
348
|
HttpCode(HttpStatus.CREATED),
|
|
@@ -245,7 +370,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
245
370
|
_ts_metadata("design:returntype", Promise)
|
|
246
371
|
], ApiController.prototype, "insert", null);
|
|
247
372
|
_ts_decorate([
|
|
248
|
-
|
|
373
|
+
createEndpointDecorators(security.insertMany, 'insertMany', options.enabledEndpoints),
|
|
249
374
|
Version(VERSION_NEUTRAL),
|
|
250
375
|
Post('insert-many'),
|
|
251
376
|
HttpCode(HttpStatus.CREATED),
|
|
@@ -274,7 +399,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
274
399
|
_ts_metadata("design:returntype", Promise)
|
|
275
400
|
], ApiController.prototype, "insertMany", null);
|
|
276
401
|
_ts_decorate([
|
|
277
|
-
|
|
402
|
+
createEndpointDecorators(security.getById, 'getById', options.enabledEndpoints),
|
|
278
403
|
Version(VERSION_NEUTRAL),
|
|
279
404
|
Post('get/:id'),
|
|
280
405
|
HttpCode(HttpStatus.OK),
|
|
@@ -304,7 +429,29 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
304
429
|
_ts_metadata("design:returntype", Promise)
|
|
305
430
|
], ApiController.prototype, "getById", null);
|
|
306
431
|
_ts_decorate([
|
|
307
|
-
|
|
432
|
+
createEndpointDecorators(security.getByIds, 'getByIds', options.enabledEndpoints),
|
|
433
|
+
Version(VERSION_NEUTRAL),
|
|
434
|
+
Post('get-by-ids'),
|
|
435
|
+
HttpCode(HttpStatus.OK),
|
|
436
|
+
ApiOperation({
|
|
437
|
+
summary: 'Get items by IDs',
|
|
438
|
+
description: 'Retrieves multiple items by their IDs. Optionally specify fields to select.'
|
|
439
|
+
}),
|
|
440
|
+
ApiBody({
|
|
441
|
+
type: GetByIdsDto
|
|
442
|
+
}),
|
|
443
|
+
ApiResponseDto(responseDtoClass, true, 'list'),
|
|
444
|
+
_ts_param(0, Body()),
|
|
445
|
+
_ts_param(1, CurrentUser()),
|
|
446
|
+
_ts_metadata("design:type", Function),
|
|
447
|
+
_ts_metadata("design:paramtypes", [
|
|
448
|
+
typeof GetByIdsDto === "undefined" ? Object : GetByIdsDto,
|
|
449
|
+
Object
|
|
450
|
+
]),
|
|
451
|
+
_ts_metadata("design:returntype", Promise)
|
|
452
|
+
], ApiController.prototype, "getByIds", null);
|
|
453
|
+
_ts_decorate([
|
|
454
|
+
createEndpointDecorators(security.update, 'update', options.enabledEndpoints),
|
|
308
455
|
Version(VERSION_NEUTRAL),
|
|
309
456
|
Post('update'),
|
|
310
457
|
HttpCode(HttpStatus.OK),
|
|
@@ -327,7 +474,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
327
474
|
_ts_metadata("design:returntype", Promise)
|
|
328
475
|
], ApiController.prototype, "update", null);
|
|
329
476
|
_ts_decorate([
|
|
330
|
-
|
|
477
|
+
createEndpointDecorators(security.updateMany, 'updateMany', options.enabledEndpoints),
|
|
331
478
|
Version(VERSION_NEUTRAL),
|
|
332
479
|
Post('update-many'),
|
|
333
480
|
HttpCode(HttpStatus.OK),
|
|
@@ -351,7 +498,56 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
351
498
|
_ts_metadata("design:returntype", Promise)
|
|
352
499
|
], ApiController.prototype, "updateMany", null);
|
|
353
500
|
_ts_decorate([
|
|
354
|
-
|
|
501
|
+
createEndpointDecorators(security.bulkUpsert, 'bulkUpsert', options.enabledEndpoints),
|
|
502
|
+
Version(VERSION_NEUTRAL),
|
|
503
|
+
Post('bulk-upsert'),
|
|
504
|
+
HttpCode(HttpStatus.OK),
|
|
505
|
+
ApiOperation({
|
|
506
|
+
summary: 'Create or update multiple items',
|
|
507
|
+
description: 'Bulk upsert: items without ID are created, items with ID are updated.'
|
|
508
|
+
}),
|
|
509
|
+
ApiResponseDto(responseDtoClass, true, 'bulk'),
|
|
510
|
+
ApiBody({
|
|
511
|
+
type: createDtoClass,
|
|
512
|
+
isArray: true
|
|
513
|
+
}),
|
|
514
|
+
UseInterceptors(SetCreatedByOnBody, SetUpdateByOnBody, Slug),
|
|
515
|
+
_ts_param(0, Body()),
|
|
516
|
+
_ts_param(1, CurrentUser()),
|
|
517
|
+
_ts_metadata("design:type", Function),
|
|
518
|
+
_ts_metadata("design:paramtypes", [
|
|
519
|
+
Array,
|
|
520
|
+
Object
|
|
521
|
+
]),
|
|
522
|
+
_ts_metadata("design:returntype", Promise)
|
|
523
|
+
], ApiController.prototype, "bulkUpsert", null);
|
|
524
|
+
_ts_decorate([
|
|
525
|
+
createEndpointDecorators(security.getByFilter, 'getByFilter', options.enabledEndpoints),
|
|
526
|
+
Version(VERSION_NEUTRAL),
|
|
527
|
+
Post('get-by-filter'),
|
|
528
|
+
HttpCode(HttpStatus.OK),
|
|
529
|
+
ApiOperation({
|
|
530
|
+
summary: 'Get single item by filter',
|
|
531
|
+
description: 'Retrieves a single item matching the provided filter criteria. Returns first match if multiple exist.'
|
|
532
|
+
}),
|
|
533
|
+
ApiBody({
|
|
534
|
+
schema: {
|
|
535
|
+
type: 'object',
|
|
536
|
+
description: 'Filter criteria'
|
|
537
|
+
}
|
|
538
|
+
}),
|
|
539
|
+
ApiResponseDto(responseDtoClass),
|
|
540
|
+
_ts_param(0, Body()),
|
|
541
|
+
_ts_param(1, CurrentUser()),
|
|
542
|
+
_ts_metadata("design:type", Function),
|
|
543
|
+
_ts_metadata("design:paramtypes", [
|
|
544
|
+
typeof Record === "undefined" ? Object : Record,
|
|
545
|
+
Object
|
|
546
|
+
]),
|
|
547
|
+
_ts_metadata("design:returntype", Promise)
|
|
548
|
+
], ApiController.prototype, "getByFilter", null);
|
|
549
|
+
_ts_decorate([
|
|
550
|
+
createEndpointDecorators(security.getAll, 'getAll', options.enabledEndpoints),
|
|
355
551
|
Version(VERSION_NEUTRAL),
|
|
356
552
|
Post('get-all'),
|
|
357
553
|
HttpCode(HttpStatus.OK),
|
|
@@ -380,7 +576,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
380
576
|
_ts_metadata("design:returntype", Promise)
|
|
381
577
|
], ApiController.prototype, "getAll", null);
|
|
382
578
|
_ts_decorate([
|
|
383
|
-
|
|
579
|
+
createEndpointDecorators(security.delete, 'delete', options.enabledEndpoints),
|
|
384
580
|
Version(VERSION_NEUTRAL),
|
|
385
581
|
Post('delete'),
|
|
386
582
|
HttpCode(HttpStatus.OK),
|
|
@@ -20,11 +20,11 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
20
20
|
function _ts_metadata(k, v) {
|
|
21
21
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
22
|
}
|
|
23
|
-
import { DeleteDto } from '../dtos';
|
|
24
23
|
import { InternalServerErrorException, NotFoundException } from '@nestjs/common';
|
|
25
|
-
import { SYSTEM_MESSAGES } from '../constants';
|
|
26
24
|
import { In } from 'typeorm';
|
|
25
|
+
import { SYSTEM_MESSAGES } from '../constants';
|
|
27
26
|
import { LogAction } from '../decorators/log-action.decorator';
|
|
27
|
+
import { DeleteDto } from '../dtos';
|
|
28
28
|
/** Generic API service with CRUD operations and caching support */ export class ApiService {
|
|
29
29
|
async insert(dto, user) {
|
|
30
30
|
return this.executeInTransaction('insert', async (qr)=>{
|
|
@@ -74,14 +74,38 @@ import { LogAction } from '../decorators/log-action.decorator';
|
|
|
74
74
|
};
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
-
async
|
|
77
|
+
async bulkUpsert(toInsert, toUpdate, user) {
|
|
78
|
+
return this.executeInTransaction('bulkUpsert', async (qr)=>{
|
|
79
|
+
const allResults = [];
|
|
80
|
+
if (toInsert.length > 0) {
|
|
81
|
+
await this.beforeInsertOperation(toInsert, user, qr);
|
|
82
|
+
const insertedEntities = await this.convertRequestDtoToEntity(toInsert, user);
|
|
83
|
+
const insertedSaved = await qr.manager.save(this.repository.target, insertedEntities);
|
|
84
|
+
await this.afterInsertOperation(this.ensureArray(insertedSaved), user, qr);
|
|
85
|
+
allResults.push(...this.ensureArray(insertedSaved));
|
|
86
|
+
}
|
|
87
|
+
if (toUpdate.length > 0) {
|
|
88
|
+
await this.beforeUpdateOperation(toUpdate, user, qr);
|
|
89
|
+
const updatedEntities = await this.convertRequestDtoToEntity(toUpdate, user);
|
|
90
|
+
const updatedSaved = await qr.manager.save(this.repository.target, updatedEntities);
|
|
91
|
+
await this.afterUpdateOperation(this.ensureArray(updatedSaved), user, qr);
|
|
92
|
+
allResults.push(...this.ensureArray(updatedSaved));
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
saved: allResults,
|
|
96
|
+
returnFirst: false
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async findByIds(ids, user, select) {
|
|
78
101
|
await this.ensureRepositoryInitialized();
|
|
79
102
|
try {
|
|
80
103
|
const query = this.repository.createQueryBuilder(this.entityName);
|
|
81
104
|
query.where({
|
|
82
105
|
id: In(ids)
|
|
83
106
|
});
|
|
84
|
-
const
|
|
107
|
+
const selectArray = typeof select === 'string' ? select.split(',').map((s)=>s.trim()) : select;
|
|
108
|
+
const { query: finalQuery, isRaw } = await this.getSelectQuery(query, user, selectArray);
|
|
85
109
|
let output;
|
|
86
110
|
if (isRaw) {
|
|
87
111
|
output = await finalQuery.getRawMany();
|
|
@@ -417,7 +441,7 @@ import { LogAction } from '../decorators/log-action.decorator';
|
|
|
417
441
|
return Object.assign({}, entity);
|
|
418
442
|
}
|
|
419
443
|
convertEntityListToResponseListDto(entities, _isRaw) {
|
|
420
|
-
return entities.map((entity)=>
|
|
444
|
+
return entities.map((entity)=>this.convertEntityToResponseDto(entity, _isRaw));
|
|
421
445
|
}
|
|
422
446
|
async getEntityClass() {
|
|
423
447
|
return await this.repository.create();
|
|
@@ -483,6 +507,18 @@ _ts_decorate([
|
|
|
483
507
|
]),
|
|
484
508
|
_ts_metadata("design:returntype", Promise)
|
|
485
509
|
], ApiService.prototype, "updateMany", null);
|
|
510
|
+
_ts_decorate([
|
|
511
|
+
LogAction({
|
|
512
|
+
action: 'bulkUpsert'
|
|
513
|
+
}),
|
|
514
|
+
_ts_metadata("design:type", Function),
|
|
515
|
+
_ts_metadata("design:paramtypes", [
|
|
516
|
+
Array,
|
|
517
|
+
Array,
|
|
518
|
+
Object
|
|
519
|
+
]),
|
|
520
|
+
_ts_metadata("design:returntype", Promise)
|
|
521
|
+
], ApiService.prototype, "bulkUpsert", null);
|
|
486
522
|
_ts_decorate([
|
|
487
523
|
LogAction({
|
|
488
524
|
action: 'delete'
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
24
|
+
import { IsArray, IsNotEmpty, IsOptional, IsUUID } from 'class-validator';
|
|
25
|
+
export class GetByIdsDto {
|
|
26
|
+
constructor(){
|
|
27
|
+
_define_property(this, "ids", void 0);
|
|
28
|
+
_define_property(this, "select", void 0);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
_ts_decorate([
|
|
32
|
+
ApiProperty({
|
|
33
|
+
description: 'Array of IDs to retrieve',
|
|
34
|
+
type: [
|
|
35
|
+
String
|
|
36
|
+
],
|
|
37
|
+
example: [
|
|
38
|
+
'uuid-1',
|
|
39
|
+
'uuid-2'
|
|
40
|
+
]
|
|
41
|
+
}),
|
|
42
|
+
IsNotEmpty(),
|
|
43
|
+
IsArray(),
|
|
44
|
+
IsUUID('all', {
|
|
45
|
+
each: true
|
|
46
|
+
}),
|
|
47
|
+
_ts_metadata("design:type", Array)
|
|
48
|
+
], GetByIdsDto.prototype, "ids", void 0);
|
|
49
|
+
_ts_decorate([
|
|
50
|
+
ApiPropertyOptional({
|
|
51
|
+
description: 'Select specific fields to return (comma-separated or array)'
|
|
52
|
+
}),
|
|
53
|
+
IsOptional(),
|
|
54
|
+
_ts_metadata("design:type", Object)
|
|
55
|
+
], GetByIdsDto.prototype, "select", void 0);
|
package/fesm/dtos/index.js
CHANGED
package/fesm/entities/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -4,11 +4,13 @@ export interface IService<CreateDtoT, UpdateDtoT, InterfaceT> {
|
|
|
4
4
|
insert(addDto: CreateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
|
|
5
5
|
insertMany(addDto: Array<CreateDtoT>, user: ILoggedUserInfo | null): Promise<Array<InterfaceT>>;
|
|
6
6
|
findById(id: string, user: ILoggedUserInfo | null, select?: string[]): Promise<InterfaceT>;
|
|
7
|
+
findByIds(ids: string[], user: ILoggedUserInfo | null, select?: string[] | string): Promise<Array<InterfaceT>>;
|
|
7
8
|
getAll(search: string, filterAndPaginationDto: FilterAndPaginationDto, user: ILoggedUserInfo | null): Promise<{
|
|
8
9
|
data: Array<InterfaceT>;
|
|
9
10
|
total: number;
|
|
10
11
|
}>;
|
|
11
12
|
update(updateDto: UpdateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
|
|
12
13
|
updateMany(updateDto: Array<UpdateDtoT>, user: ILoggedUserInfo | null): Promise<Array<InterfaceT>>;
|
|
14
|
+
bulkUpsert(toInsert: Array<CreateDtoT>, toUpdate: Array<UpdateDtoT>, user: ILoggedUserInfo | null): Promise<Array<InterfaceT>>;
|
|
13
15
|
delete(deleteDto: DeleteDto, user: ILoggedUserInfo | null): Promise<null>;
|
|
14
16
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-shared",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.1",
|
|
4
4
|
"description": "Common shared utilities for Flusys NestJS applications",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "fesm/index.js",
|
|
@@ -112,6 +112,6 @@
|
|
|
112
112
|
"rxjs": "^7.8.0"
|
|
113
113
|
},
|
|
114
114
|
"dependencies": {
|
|
115
|
-
"@flusys/nestjs-core": "4.
|
|
115
|
+
"@flusys/nestjs-core": "4.1.1"
|
|
116
116
|
}
|
|
117
117
|
}
|