@flusys/nestjs-shared 3.0.1 → 4.0.0-lts
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 +159 -79
- package/cjs/classes/api-controller.class.js +26 -8
- package/cjs/classes/api-service.class.js +100 -17
- package/cjs/classes/winston-logger-adapter.class.js +15 -20
- package/cjs/classes/winston.logger.class.js +103 -70
- package/cjs/constants/index.js +1 -0
- package/cjs/constants/message-keys.js +80 -0
- package/cjs/constants/permissions.js +32 -1
- package/cjs/decorators/index.js +1 -0
- package/cjs/decorators/log-action.decorator.js +149 -0
- package/cjs/dtos/response-payload.dto.js +72 -0
- package/cjs/exceptions/base-app.exception.js +145 -0
- package/cjs/exceptions/index.js +1 -0
- package/cjs/exceptions/permission.exception.js +12 -8
- package/cjs/filters/global-exception.filter.js +167 -0
- package/cjs/filters/index.js +18 -0
- package/cjs/guards/jwt-auth.guard.js +4 -1
- package/cjs/guards/permission.guard.js +6 -13
- package/cjs/index.js +1 -0
- package/cjs/interceptors/idempotency.interceptor.js +1 -1
- package/cjs/interceptors/index.js +0 -1
- package/cjs/interfaces/logger.interface.js +1 -4
- package/cjs/middlewares/logger.middleware.js +83 -26
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +33 -11
- package/cjs/modules/utils/utils.service.js +4 -20
- package/cjs/utils/index.js +0 -1
- package/cjs/utils/query-helpers.util.js +8 -1
- package/classes/api-controller.class.d.ts +1 -0
- package/classes/api-service.class.d.ts +5 -10
- package/classes/winston-logger-adapter.class.d.ts +12 -11
- package/classes/winston.logger.class.d.ts +1 -0
- package/constants/index.d.ts +1 -0
- package/constants/message-keys.d.ts +81 -0
- package/constants/permissions.d.ts +36 -0
- package/decorators/index.d.ts +1 -0
- package/decorators/log-action.decorator.d.ts +8 -0
- package/dtos/response-payload.dto.d.ts +8 -0
- package/exceptions/base-app.exception.d.ts +41 -0
- package/exceptions/index.d.ts +1 -0
- package/exceptions/permission.exception.d.ts +1 -1
- package/fesm/classes/api-controller.class.js +26 -8
- package/fesm/classes/api-service.class.js +101 -18
- package/fesm/classes/winston-logger-adapter.class.js +18 -44
- package/fesm/classes/winston.logger.class.js +100 -68
- package/fesm/constants/index.js +2 -0
- package/fesm/constants/message-keys.js +59 -0
- package/fesm/constants/permissions.js +24 -1
- package/fesm/decorators/index.js +1 -0
- package/fesm/decorators/log-action.decorator.js +139 -0
- package/fesm/dtos/response-payload.dto.js +72 -0
- package/fesm/exceptions/base-app.exception.js +109 -0
- package/fesm/exceptions/index.js +1 -0
- package/fesm/exceptions/permission.exception.js +15 -17
- package/fesm/filters/global-exception.filter.js +157 -0
- package/fesm/filters/index.js +1 -0
- package/fesm/guards/jwt-auth.guard.js +5 -2
- package/fesm/guards/permission.guard.js +8 -15
- package/fesm/index.js +1 -0
- package/fesm/interceptors/idempotency.interceptor.js +2 -2
- package/fesm/interceptors/index.js +0 -1
- package/fesm/interfaces/logger.interface.js +1 -4
- package/fesm/middlewares/logger.middleware.js +83 -26
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +34 -12
- package/fesm/modules/utils/utils.service.js +5 -21
- package/fesm/utils/index.js +0 -1
- package/fesm/utils/query-helpers.util.js +8 -1
- package/filters/global-exception.filter.d.ts +10 -0
- package/filters/index.d.ts +1 -0
- package/guards/permission.guard.d.ts +1 -3
- package/index.d.ts +1 -0
- package/interceptors/index.d.ts +0 -1
- package/interfaces/logger.interface.d.ts +5 -5
- package/modules/datasource/multi-tenant-datasource.service.d.ts +1 -2
- package/modules/utils/utils.service.d.ts +0 -1
- package/package.json +5 -3
- package/utils/index.d.ts +0 -1
- package/cjs/interceptors/query-performance.interceptor.js +0 -66
- package/cjs/utils/error-handler.util.js +0 -90
- package/fesm/interceptors/query-performance.interceptor.js +0 -56
- package/fesm/utils/error-handler.util.js +0 -82
- package/interceptors/query-performance.interceptor.d.ts +0 -8
- package/utils/error-handler.util.d.ts +0 -19
package/README.md
CHANGED
|
@@ -134,7 +134,6 @@ nestjs-shared/
|
|
|
134
134
|
│ │ └── utils.service.ts # Cache helpers, string utilities
|
|
135
135
|
│ │
|
|
136
136
|
│ └── utils/ # Utility functions
|
|
137
|
-
│ ├── error-handler.util.ts # ErrorHandler class
|
|
138
137
|
│ ├── html-sanitizer.util.ts # escapeHtml, escapeHtmlVariables
|
|
139
138
|
│ ├── query-helpers.util.ts # applyCompanyFilter, validateCompanyOwnership
|
|
140
139
|
│ ├── request.util.ts # isBrowserRequest, buildCookieOptions, parseDurationToMs
|
|
@@ -582,6 +581,45 @@ export class CreateCommentDto {
|
|
|
582
581
|
}
|
|
583
582
|
```
|
|
584
583
|
|
|
584
|
+
### @LogAction
|
|
585
|
+
|
|
586
|
+
Method-level logging decorator with automatic correlation context and error handling:
|
|
587
|
+
|
|
588
|
+
```typescript
|
|
589
|
+
import { LogAction } from '@flusys/nestjs-shared';
|
|
590
|
+
|
|
591
|
+
@Injectable()
|
|
592
|
+
export class UserService {
|
|
593
|
+
// Module name for routing logs to module-specific files (production)
|
|
594
|
+
protected readonly moduleName = 'auth';
|
|
595
|
+
|
|
596
|
+
// Basic usage - logs start, success (with duration), and errors
|
|
597
|
+
@LogAction({ action: 'user.create' })
|
|
598
|
+
async createUser(dto: CreateUserDto): Promise<User> {
|
|
599
|
+
return this.repository.save(dto);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// With all options
|
|
603
|
+
@LogAction({
|
|
604
|
+
action: 'user.update', // Action name (default: ClassName.methodName)
|
|
605
|
+
module: 'auth', // Override module routing
|
|
606
|
+
includeParams: true, // Log method parameters (sensitive data redacted)
|
|
607
|
+
includeResult: true, // Log result preview
|
|
608
|
+
logLevel: 'info' // 'debug' | 'info' | 'warn' (default: 'debug')
|
|
609
|
+
})
|
|
610
|
+
async updateUser(dto: UpdateUserDto): Promise<User> {
|
|
611
|
+
return this.repository.save(dto);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**Features:**
|
|
617
|
+
- Automatic start/end logging with duration
|
|
618
|
+
- Error logging with stack trace (then re-throws)
|
|
619
|
+
- Sensitive data redaction (password, secret, token, apiKey, authorization)
|
|
620
|
+
- Request context (requestId, userId, tenantId, companyId)
|
|
621
|
+
- Module-specific log routing (production mode)
|
|
622
|
+
|
|
585
623
|
### @ApiResponseDto
|
|
586
624
|
|
|
587
625
|
Generates Swagger schema for response:
|
|
@@ -872,20 +910,6 @@ Removes empty/null `id` fields from request body (single objects and arrays).
|
|
|
872
910
|
// Output: [{ name: 'A' }, { id: '123', name: 'B' }]
|
|
873
911
|
```
|
|
874
912
|
|
|
875
|
-
### QueryPerformanceInterceptor
|
|
876
|
-
|
|
877
|
-
Monitors endpoint execution time and logs warnings for slow requests.
|
|
878
|
-
|
|
879
|
-
```typescript
|
|
880
|
-
import { QueryPerformanceInterceptor } from '@flusys/nestjs-shared';
|
|
881
|
-
|
|
882
|
-
// Default threshold: 1000ms
|
|
883
|
-
app.useGlobalInterceptors(new QueryPerformanceInterceptor());
|
|
884
|
-
|
|
885
|
-
// Custom threshold: 500ms
|
|
886
|
-
app.useGlobalInterceptors(new QueryPerformanceInterceptor(500));
|
|
887
|
-
```
|
|
888
|
-
|
|
889
913
|
### Slug Interceptor
|
|
890
914
|
|
|
891
915
|
Auto-generates `slug` from `name` field if not provided.
|
|
@@ -1202,66 +1226,92 @@ const safeVars = escapeHtmlVariables({
|
|
|
1202
1226
|
|
|
1203
1227
|
## Error Handling
|
|
1204
1228
|
|
|
1205
|
-
###
|
|
1229
|
+
### BaseAppException
|
|
1206
1230
|
|
|
1207
|
-
|
|
1231
|
+
Structured exceptions with i18n support and validation errors:
|
|
1208
1232
|
|
|
1209
1233
|
```typescript
|
|
1210
|
-
import {
|
|
1211
|
-
|
|
1234
|
+
import {
|
|
1235
|
+
BaseAppException,
|
|
1236
|
+
NotFoundException,
|
|
1237
|
+
ValidationException,
|
|
1238
|
+
UnauthorizedException,
|
|
1239
|
+
ForbiddenException,
|
|
1240
|
+
ConflictException,
|
|
1241
|
+
InternalServerException,
|
|
1242
|
+
ServiceUnavailableException,
|
|
1243
|
+
IValidationError,
|
|
1244
|
+
} from '@flusys/nestjs-shared';
|
|
1212
1245
|
|
|
1213
|
-
|
|
1246
|
+
// Custom exception with all options
|
|
1247
|
+
throw new BaseAppException({
|
|
1248
|
+
message: 'User registration failed',
|
|
1249
|
+
code: 'REGISTRATION_FAILED',
|
|
1250
|
+
messageKey: 'error.registration.failed', // For i18n
|
|
1251
|
+
status: HttpStatus.BAD_REQUEST,
|
|
1252
|
+
errors: [
|
|
1253
|
+
{ field: 'email', message: 'Email already exists', code: 'DUPLICATE_EMAIL' }
|
|
1254
|
+
],
|
|
1255
|
+
metadata: { attemptedEmail: email },
|
|
1256
|
+
});
|
|
1214
1257
|
|
|
1215
|
-
//
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
entity?: string;
|
|
1219
|
-
userId?: string;
|
|
1220
|
-
id?: string;
|
|
1221
|
-
companyId?: string;
|
|
1222
|
-
branchId?: string;
|
|
1223
|
-
sectionId?: string;
|
|
1224
|
-
data?: Record<string, unknown>;
|
|
1225
|
-
}
|
|
1258
|
+
// Pre-built exceptions
|
|
1259
|
+
throw new NotFoundException('User', userId);
|
|
1260
|
+
// → { message: "User with id 'abc' not found", code: "NOT_FOUND", messageKey: "error.notFound" }
|
|
1226
1261
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1262
|
+
throw new ValidationException([
|
|
1263
|
+
{ field: 'email', message: 'Invalid format' },
|
|
1264
|
+
{ field: 'password', message: 'Too short' },
|
|
1265
|
+
]);
|
|
1230
1266
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
});
|
|
1267
|
+
throw new UnauthorizedException('Invalid credentials');
|
|
1268
|
+
throw new ForbiddenException('Admin access required');
|
|
1269
|
+
throw new ConflictException('User', 'email');
|
|
1270
|
+
throw new InternalServerException('Database connection failed');
|
|
1271
|
+
throw new ServiceUnavailableException('Email Service');
|
|
1272
|
+
```
|
|
1238
1273
|
|
|
1239
|
-
|
|
1240
|
-
ErrorHandler.rethrowError(error); // throws: never
|
|
1274
|
+
### GlobalExceptionFilter
|
|
1241
1275
|
|
|
1242
|
-
|
|
1243
|
-
ErrorHandler.logAndRethrow(logger, error, 'updateUser', { entity: 'User', id: userId });
|
|
1276
|
+
Unified exception handling with structured responses and logging:
|
|
1244
1277
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1278
|
+
```typescript
|
|
1279
|
+
import { GlobalExceptionFilter } from '@flusys/nestjs-shared';
|
|
1280
|
+
|
|
1281
|
+
// Register in main.ts
|
|
1282
|
+
app.useGlobalFilters(new GlobalExceptionFilter());
|
|
1283
|
+
|
|
1284
|
+
// Or in app.module.ts
|
|
1285
|
+
@Module({
|
|
1286
|
+
providers: [
|
|
1287
|
+
{ provide: APP_FILTER, useClass: GlobalExceptionFilter },
|
|
1288
|
+
],
|
|
1289
|
+
})
|
|
1290
|
+
export class AppModule {}
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
**Response format (all errors):**
|
|
1294
|
+
```json
|
|
1295
|
+
{
|
|
1296
|
+
"success": false,
|
|
1297
|
+
"message": "User with id 'abc' not found",
|
|
1298
|
+
"code": "NOT_FOUND",
|
|
1299
|
+
"messageKey": "error.notFound",
|
|
1300
|
+
"errors": [...],
|
|
1301
|
+
"_meta": {
|
|
1302
|
+
"requestId": "uuid",
|
|
1303
|
+
"timestamp": "2024-01-15T10:30:00.000Z",
|
|
1304
|
+
"responseTime": 45
|
|
1254
1305
|
}
|
|
1255
1306
|
}
|
|
1256
1307
|
```
|
|
1257
1308
|
|
|
1258
|
-
**
|
|
1259
|
-
-
|
|
1260
|
-
-
|
|
1261
|
-
-
|
|
1262
|
-
-
|
|
1263
|
-
-
|
|
1264
|
-
- `authorization`
|
|
1309
|
+
**Features:**
|
|
1310
|
+
- Consistent error response format
|
|
1311
|
+
- Request correlation (requestId, userId, tenantId)
|
|
1312
|
+
- Log level by status (500+ → error, 400+ → warn)
|
|
1313
|
+
- Handles BaseAppException, HttpException, and generic errors
|
|
1314
|
+
- class-validator error transformation
|
|
1265
1315
|
|
|
1266
1316
|
---
|
|
1267
1317
|
|
|
@@ -1376,11 +1426,34 @@ type PermissionCode = 'user.create' | 'user.read' | ... ; // Union of all permis
|
|
|
1376
1426
|
Production-ready logging with tenant-aware routing and daily rotation.
|
|
1377
1427
|
|
|
1378
1428
|
**Configuration via environment:**
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1429
|
+
| Variable | Description | Default |
|
|
1430
|
+
|----------|-------------|---------|
|
|
1431
|
+
| `LOG_DIR` | Directory for log files | `logs/` |
|
|
1432
|
+
| `LOG_LEVEL` | Minimum log level | `info` (prod), `debug` (dev) |
|
|
1433
|
+
| `LOG_MAX_SIZE` | Max file size before rotation | `20m` |
|
|
1434
|
+
| `LOG_MAX_FILES` | Max number of log files to keep | `14d` |
|
|
1435
|
+
| `USE_TENANT_MODE` | Enable tenant-aware log routing | `false` |
|
|
1436
|
+
| `DISABLE_HTTP_LOGGING` | Disable HTTP request/response logs | `false` |
|
|
1437
|
+
|
|
1438
|
+
**Disable HTTP Logging:**
|
|
1439
|
+
```bash
|
|
1440
|
+
# .env - Disable middleware HTTP logs (keep service-level @LogAction logs)
|
|
1441
|
+
DISABLE_HTTP_LOGGING=true
|
|
1442
|
+
```
|
|
1443
|
+
|
|
1444
|
+
When disabled, LoggerMiddleware skips request/response logging but preserves:
|
|
1445
|
+
- `@LogAction` decorator logs
|
|
1446
|
+
- Manual `logger.log()` calls
|
|
1447
|
+
- Error logs from GlobalExceptionFilter
|
|
1448
|
+
|
|
1449
|
+
**Log Format (Production):**
|
|
1450
|
+
```
|
|
1451
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
1452
|
+
2024-01-15 10:30:45.123 | Information | [request-uuid]
|
|
1453
|
+
Context: HTTP | Endpoint: POST /api/users | Status: 200 | Duration: 45ms
|
|
1454
|
+
Request finished "HTTP/1.1" "POST" "/api/users" - 200 534 "application/json" 45.00ms
|
|
1455
|
+
Metadata: {"userId":"user-123","tenantId":"tenant-1"}
|
|
1456
|
+
```
|
|
1384
1457
|
|
|
1385
1458
|
**Logger Modes:**
|
|
1386
1459
|
|
|
@@ -1464,6 +1537,8 @@ import {
|
|
|
1464
1537
|
SanitizeAndTrim,
|
|
1465
1538
|
ApiResponseDto,
|
|
1466
1539
|
ArrayResponseType,
|
|
1540
|
+
LogAction,
|
|
1541
|
+
ILogActionOptions,
|
|
1467
1542
|
|
|
1468
1543
|
// Guards
|
|
1469
1544
|
JwtAuthGuard,
|
|
@@ -1477,7 +1552,6 @@ import {
|
|
|
1477
1552
|
SetDeletedByOnBody,
|
|
1478
1553
|
createSetUserFieldInterceptor, // Factory function
|
|
1479
1554
|
DeleteEmptyIdFromBodyInterceptor,
|
|
1480
|
-
QueryPerformanceInterceptor,
|
|
1481
1555
|
Slug,
|
|
1482
1556
|
|
|
1483
1557
|
// Modules
|
|
@@ -1532,7 +1606,20 @@ import {
|
|
|
1532
1606
|
getUserId,
|
|
1533
1607
|
getCompanyId,
|
|
1534
1608
|
|
|
1609
|
+
// Filters
|
|
1610
|
+
GlobalExceptionFilter,
|
|
1611
|
+
|
|
1535
1612
|
// Exceptions
|
|
1613
|
+
BaseAppException,
|
|
1614
|
+
IBaseAppExceptionOptions,
|
|
1615
|
+
IValidationError,
|
|
1616
|
+
NotFoundException,
|
|
1617
|
+
ValidationException,
|
|
1618
|
+
UnauthorizedException,
|
|
1619
|
+
ForbiddenException,
|
|
1620
|
+
ConflictException,
|
|
1621
|
+
InternalServerException,
|
|
1622
|
+
ServiceUnavailableException,
|
|
1536
1623
|
InsufficientPermissionsException,
|
|
1537
1624
|
NoPermissionsFoundException,
|
|
1538
1625
|
PermissionSystemUnavailableException,
|
|
@@ -1569,8 +1656,6 @@ import {
|
|
|
1569
1656
|
FORM_RESULT_PERMISSIONS,
|
|
1570
1657
|
|
|
1571
1658
|
// Utilities
|
|
1572
|
-
ErrorHandler,
|
|
1573
|
-
IErrorContext,
|
|
1574
1659
|
escapeHtml,
|
|
1575
1660
|
escapeHtmlVariables,
|
|
1576
1661
|
applyCompanyFilter,
|
|
@@ -1758,18 +1843,13 @@ validateCompanyOwnership(entity, user, this.config.isCompanyFeatureEnabled(), 'P
|
|
|
1758
1843
|
### 7. Error Handling Pattern
|
|
1759
1844
|
|
|
1760
1845
|
```typescript
|
|
1761
|
-
import {
|
|
1846
|
+
import { LogAction } from '@flusys/nestjs-shared';
|
|
1762
1847
|
|
|
1763
|
-
// Use
|
|
1848
|
+
// Use @LogAction for consistent logging with sensitive data redaction
|
|
1849
|
+
@LogAction({ action: 'user.create', includeParams: false })
|
|
1764
1850
|
async createUser(dto: CreateUserDto): Promise<User> {
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
} catch (error) {
|
|
1768
|
-
ErrorHandler.logAndRethrow(this.logger, error, 'createUser', {
|
|
1769
|
-
entity: 'User',
|
|
1770
|
-
data: dto, // Sensitive fields auto-redacted
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1851
|
+
return this.repository.save(dto);
|
|
1852
|
+
// Errors automatically logged with stack trace, then re-thrown
|
|
1773
1853
|
}
|
|
1774
1854
|
```
|
|
1775
1855
|
|
|
@@ -88,6 +88,8 @@ function _ts_param(paramIndex, decorator) {
|
|
|
88
88
|
return (0, _common.applyDecorators)(...decorators);
|
|
89
89
|
}
|
|
90
90
|
function createApiController(createDtoClass, updateDtoClass, responseDtoClass, options = {}) {
|
|
91
|
+
// Entity name for message keys (defaults to 'item')
|
|
92
|
+
const entityName = options.entityName ?? 'item';
|
|
91
93
|
// Determine if security is global (applies to all) or per-endpoint
|
|
92
94
|
const securityConfig = options.security;
|
|
93
95
|
const endpointKeys = [
|
|
@@ -124,7 +126,8 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
124
126
|
const data = (0, _classtransformer.plainToInstance)(responseDtoClass, entity);
|
|
125
127
|
return {
|
|
126
128
|
success: true,
|
|
127
|
-
message:
|
|
129
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} created successfully`,
|
|
130
|
+
messageKey: `${entityName}.create.success`,
|
|
128
131
|
data
|
|
129
132
|
};
|
|
130
133
|
}
|
|
@@ -133,7 +136,11 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
133
136
|
const data = entities.map((item)=>(0, _classtransformer.plainToInstance)(responseDtoClass, item));
|
|
134
137
|
return {
|
|
135
138
|
success: true,
|
|
136
|
-
message: `${data.length}
|
|
139
|
+
message: `${data.length} ${entityName}s created successfully`,
|
|
140
|
+
messageKey: `${entityName}.create.many.success`,
|
|
141
|
+
messageVariables: {
|
|
142
|
+
count: data.length
|
|
143
|
+
},
|
|
137
144
|
data,
|
|
138
145
|
meta: {
|
|
139
146
|
count: data.length,
|
|
@@ -147,7 +154,8 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
147
154
|
const data = (0, _classtransformer.plainToInstance)(responseDtoClass, entity);
|
|
148
155
|
return {
|
|
149
156
|
success: true,
|
|
150
|
-
message:
|
|
157
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
|
|
158
|
+
messageKey: `${entityName}.get.success`,
|
|
151
159
|
data
|
|
152
160
|
};
|
|
153
161
|
}
|
|
@@ -156,7 +164,8 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
156
164
|
const data = (0, _classtransformer.plainToInstance)(responseDtoClass, entity);
|
|
157
165
|
return {
|
|
158
166
|
success: true,
|
|
159
|
-
message:
|
|
167
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} updated successfully`,
|
|
168
|
+
messageKey: `${entityName}.update.success`,
|
|
160
169
|
data
|
|
161
170
|
};
|
|
162
171
|
}
|
|
@@ -165,7 +174,11 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
165
174
|
const data = (0, _classtransformer.plainToInstance)(responseDtoClass, entities);
|
|
166
175
|
return {
|
|
167
176
|
success: true,
|
|
168
|
-
message: `${data.length}
|
|
177
|
+
message: `${data.length} ${entityName}s updated successfully`,
|
|
178
|
+
messageKey: `${entityName}.update.many.success`,
|
|
179
|
+
messageVariables: {
|
|
180
|
+
count: data.length
|
|
181
|
+
},
|
|
169
182
|
data,
|
|
170
183
|
meta: {
|
|
171
184
|
count: data.length,
|
|
@@ -182,7 +195,8 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
182
195
|
const totalPages = pageSize > 0 ? Math.ceil(result.total / pageSize) : 1;
|
|
183
196
|
return {
|
|
184
197
|
success: true,
|
|
185
|
-
message:
|
|
198
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)}s retrieved successfully`,
|
|
199
|
+
messageKey: `${entityName}.get.all.success`,
|
|
186
200
|
data,
|
|
187
201
|
meta: {
|
|
188
202
|
total: result.total,
|
|
@@ -197,10 +211,14 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
197
211
|
async delete(deleteDto, user) {
|
|
198
212
|
await this.service.delete(deleteDto, user);
|
|
199
213
|
const count = Array.isArray(deleteDto.id) ? deleteDto.id.length : 1;
|
|
200
|
-
const action = deleteDto.type === 'restore' ? '
|
|
214
|
+
const action = deleteDto.type === 'restore' ? 'restore' : 'delete';
|
|
201
215
|
return {
|
|
202
216
|
success: true,
|
|
203
|
-
message: `${count}
|
|
217
|
+
message: `${count} ${entityName}${count > 1 ? 's' : ''} ${action}d successfully`,
|
|
218
|
+
messageKey: `${entityName}.${action}.success`,
|
|
219
|
+
messageVariables: {
|
|
220
|
+
count
|
|
221
|
+
}
|
|
204
222
|
};
|
|
205
223
|
}
|
|
206
224
|
constructor(service){
|
|
@@ -8,9 +8,11 @@ Object.defineProperty(exports, "ApiService", {
|
|
|
8
8
|
return ApiService;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const
|
|
11
|
+
const _dtos = require("../dtos");
|
|
12
12
|
const _common = require("@nestjs/common");
|
|
13
|
+
const _constants = require("../constants");
|
|
13
14
|
const _typeorm = require("typeorm");
|
|
15
|
+
const _logactiondecorator = require("../decorators/log-action.decorator");
|
|
14
16
|
function _define_property(obj, key, value) {
|
|
15
17
|
if (key in obj) {
|
|
16
18
|
Object.defineProperty(obj, key, {
|
|
@@ -24,6 +26,15 @@ function _define_property(obj, key, value) {
|
|
|
24
26
|
}
|
|
25
27
|
return obj;
|
|
26
28
|
}
|
|
29
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
30
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
31
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
32
|
+
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;
|
|
33
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
34
|
+
}
|
|
35
|
+
function _ts_metadata(k, v) {
|
|
36
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
37
|
+
}
|
|
27
38
|
let ApiService = class ApiService {
|
|
28
39
|
async insert(dto, user) {
|
|
29
40
|
return this.executeInTransaction('insert', async (qr)=>{
|
|
@@ -88,7 +99,10 @@ let ApiService = class ApiService {
|
|
|
88
99
|
output = await finalQuery.getMany();
|
|
89
100
|
}
|
|
90
101
|
const result = this.convertEntityListToResponseListDto(output, isRaw);
|
|
91
|
-
if (!result || !result.length) throw new _common.NotFoundException(
|
|
102
|
+
if (!result || !result.length) throw new _common.NotFoundException({
|
|
103
|
+
message: 'No Data Found',
|
|
104
|
+
messageKey: _constants.SYSTEM_MESSAGES.NOT_FOUND
|
|
105
|
+
});
|
|
92
106
|
return result;
|
|
93
107
|
} catch (error) {
|
|
94
108
|
if (error instanceof _common.NotFoundException) {
|
|
@@ -119,7 +133,10 @@ let ApiService = class ApiService {
|
|
|
119
133
|
if (convertResult.length) result = convertResult[0];
|
|
120
134
|
} else {
|
|
121
135
|
output = await finalQuery.getOne();
|
|
122
|
-
if (!output) throw new _common.NotFoundException(
|
|
136
|
+
if (!output) throw new _common.NotFoundException({
|
|
137
|
+
message: 'No Data Found',
|
|
138
|
+
messageKey: _constants.SYSTEM_MESSAGES.NOT_FOUND
|
|
139
|
+
});
|
|
123
140
|
result = this.convertEntityToResponseDto(output, false);
|
|
124
141
|
}
|
|
125
142
|
if (this.isCacheable) {
|
|
@@ -209,7 +226,15 @@ let ApiService = class ApiService {
|
|
|
209
226
|
const parameters = rawSubQuery.getParameters();
|
|
210
227
|
const orderedValues = [];
|
|
211
228
|
const convertedQuery = countSql.replace(/:(\w+)/g, (_, key)=>{
|
|
212
|
-
if (!(key in parameters))
|
|
229
|
+
if (!(key in parameters)) {
|
|
230
|
+
throw new _common.InternalServerErrorException({
|
|
231
|
+
message: `Missing parameter value for: ${key}`,
|
|
232
|
+
messageKey: _constants.SYSTEM_MESSAGES.MISSING_PARAMETER,
|
|
233
|
+
messageParams: {
|
|
234
|
+
key
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
213
238
|
orderedValues.push(parameters[key]);
|
|
214
239
|
return '?';
|
|
215
240
|
});
|
|
@@ -282,13 +307,14 @@ let ApiService = class ApiService {
|
|
|
282
307
|
async clearCacheForId(entities) {
|
|
283
308
|
await Promise.all(entities.map((item)=>this.utilsService.clearCache(this.entityName, this.cacheManager, item.id)));
|
|
284
309
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
310
|
+
handleError(error, _operation) {
|
|
311
|
+
if (error instanceof Error) {
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
throw new _common.InternalServerErrorException({
|
|
315
|
+
message: typeof error === 'string' ? error : 'Unknown error',
|
|
316
|
+
messageKey: _constants.SYSTEM_MESSAGES.INTERNAL_ERROR
|
|
290
317
|
});
|
|
291
|
-
_errorhandlerutil.ErrorHandler.rethrowError(error);
|
|
292
318
|
}
|
|
293
319
|
/** Ensures value is always an array */ ensureArray(value) {
|
|
294
320
|
return Array.isArray(value) ? value : [
|
|
@@ -385,7 +411,10 @@ let ApiService = class ApiService {
|
|
|
385
411
|
id: d.id
|
|
386
412
|
}
|
|
387
413
|
});
|
|
388
|
-
if (!existing) throw new _common.NotFoundException(
|
|
414
|
+
if (!existing) throw new _common.NotFoundException({
|
|
415
|
+
message: 'No such entity data found for update! Please, Try Again.',
|
|
416
|
+
messageKey: _constants.SYSTEM_MESSAGES.NOT_FOUND
|
|
417
|
+
});
|
|
389
418
|
return Object.assign(existing, d);
|
|
390
419
|
}
|
|
391
420
|
delete d.id;
|
|
@@ -403,21 +432,75 @@ let ApiService = class ApiService {
|
|
|
403
432
|
async getEntityClass() {
|
|
404
433
|
return await this.repository.create();
|
|
405
434
|
}
|
|
406
|
-
constructor(entityName, repository, cacheManager, utilsService,
|
|
435
|
+
constructor(entityName, repository, cacheManager, utilsService, _loggerName, isCacheable = false, moduleName){
|
|
407
436
|
_define_property(this, "entityName", void 0);
|
|
408
437
|
_define_property(this, "repository", void 0);
|
|
409
438
|
_define_property(this, "cacheManager", void 0);
|
|
410
439
|
_define_property(this, "utilsService", void 0);
|
|
411
|
-
_define_property(this, "
|
|
440
|
+
_define_property(this, "_loggerName", void 0);
|
|
412
441
|
_define_property(this, "isCacheable", void 0);
|
|
413
|
-
_define_property(this, "
|
|
442
|
+
_define_property(this, "moduleName", void 0);
|
|
414
443
|
this.entityName = entityName;
|
|
415
444
|
this.repository = repository;
|
|
416
445
|
this.cacheManager = cacheManager;
|
|
417
446
|
this.utilsService = utilsService;
|
|
418
|
-
this.
|
|
447
|
+
this._loggerName = _loggerName;
|
|
419
448
|
this.isCacheable = isCacheable;
|
|
420
|
-
this.
|
|
421
|
-
this.logger = new _common.Logger(loggerName);
|
|
449
|
+
this.moduleName = moduleName;
|
|
422
450
|
}
|
|
423
451
|
};
|
|
452
|
+
_ts_decorate([
|
|
453
|
+
(0, _logactiondecorator.LogAction)({
|
|
454
|
+
action: 'create'
|
|
455
|
+
}),
|
|
456
|
+
_ts_metadata("design:type", Function),
|
|
457
|
+
_ts_metadata("design:paramtypes", [
|
|
458
|
+
typeof CreateDtoT === "undefined" ? Object : CreateDtoT,
|
|
459
|
+
Object
|
|
460
|
+
]),
|
|
461
|
+
_ts_metadata("design:returntype", Promise)
|
|
462
|
+
], ApiService.prototype, "insert", null);
|
|
463
|
+
_ts_decorate([
|
|
464
|
+
(0, _logactiondecorator.LogAction)({
|
|
465
|
+
action: 'createMany'
|
|
466
|
+
}),
|
|
467
|
+
_ts_metadata("design:type", Function),
|
|
468
|
+
_ts_metadata("design:paramtypes", [
|
|
469
|
+
typeof Array === "undefined" ? Object : Array,
|
|
470
|
+
Object
|
|
471
|
+
]),
|
|
472
|
+
_ts_metadata("design:returntype", Promise)
|
|
473
|
+
], ApiService.prototype, "insertMany", null);
|
|
474
|
+
_ts_decorate([
|
|
475
|
+
(0, _logactiondecorator.LogAction)({
|
|
476
|
+
action: 'update'
|
|
477
|
+
}),
|
|
478
|
+
_ts_metadata("design:type", Function),
|
|
479
|
+
_ts_metadata("design:paramtypes", [
|
|
480
|
+
typeof UpdateDtoT === "undefined" ? Object : UpdateDtoT,
|
|
481
|
+
Object
|
|
482
|
+
]),
|
|
483
|
+
_ts_metadata("design:returntype", Promise)
|
|
484
|
+
], ApiService.prototype, "update", null);
|
|
485
|
+
_ts_decorate([
|
|
486
|
+
(0, _logactiondecorator.LogAction)({
|
|
487
|
+
action: 'updateMany'
|
|
488
|
+
}),
|
|
489
|
+
_ts_metadata("design:type", Function),
|
|
490
|
+
_ts_metadata("design:paramtypes", [
|
|
491
|
+
Array,
|
|
492
|
+
Object
|
|
493
|
+
]),
|
|
494
|
+
_ts_metadata("design:returntype", Promise)
|
|
495
|
+
], ApiService.prototype, "updateMany", null);
|
|
496
|
+
_ts_decorate([
|
|
497
|
+
(0, _logactiondecorator.LogAction)({
|
|
498
|
+
action: 'delete'
|
|
499
|
+
}),
|
|
500
|
+
_ts_metadata("design:type", Function),
|
|
501
|
+
_ts_metadata("design:paramtypes", [
|
|
502
|
+
typeof _dtos.DeleteDto === "undefined" ? Object : _dtos.DeleteDto,
|
|
503
|
+
Object
|
|
504
|
+
]),
|
|
505
|
+
_ts_metadata("design:returntype", Promise)
|
|
506
|
+
], ApiService.prototype, "delete", null);
|
|
@@ -31,20 +31,13 @@ function _define_property(obj, key, value) {
|
|
|
31
31
|
}
|
|
32
32
|
return obj;
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const tenantId = (0, _loggermiddleware.getTenantId)();
|
|
42
|
-
const companyId = (0, _loggermiddleware.getCompanyId)();
|
|
43
|
-
if (requestId) meta.requestId = requestId;
|
|
44
|
-
if (userId) meta.userId = userId;
|
|
45
|
-
if (tenantId) meta.tenantId = tenantId;
|
|
46
|
-
if (companyId) meta.companyId = companyId;
|
|
47
|
-
return meta;
|
|
34
|
+
function getCorrelationMeta() {
|
|
35
|
+
return Object.fromEntries(Object.entries({
|
|
36
|
+
requestId: (0, _loggermiddleware.getRequestId)(),
|
|
37
|
+
userId: (0, _loggermiddleware.getUserId)(),
|
|
38
|
+
tenantId: (0, _loggermiddleware.getTenantId)(),
|
|
39
|
+
companyId: (0, _loggermiddleware.getCompanyId)()
|
|
40
|
+
}).filter(([, v])=>v != null));
|
|
48
41
|
}
|
|
49
42
|
let WinstonLoggerAdapter = class WinstonLoggerAdapter {
|
|
50
43
|
buildLogMeta(context, args, extra) {
|
|
@@ -57,25 +50,27 @@ let WinstonLoggerAdapter = class WinstonLoggerAdapter {
|
|
|
57
50
|
};
|
|
58
51
|
}
|
|
59
52
|
log(message, context, ...args) {
|
|
60
|
-
|
|
53
|
+
this.logger.info(message, this.buildLogMeta(context, args));
|
|
61
54
|
}
|
|
62
55
|
error(message, trace, context, ...args) {
|
|
63
|
-
|
|
56
|
+
this.logger.error(message, this.buildLogMeta(context, args, {
|
|
64
57
|
stack: trace
|
|
65
58
|
}));
|
|
66
59
|
}
|
|
67
60
|
warn(message, context, ...args) {
|
|
68
|
-
|
|
61
|
+
this.logger.warn(message, this.buildLogMeta(context, args));
|
|
69
62
|
}
|
|
70
63
|
debug(message, context, ...args) {
|
|
71
|
-
|
|
64
|
+
this.logger.debug(message, this.buildLogMeta(context, args));
|
|
72
65
|
}
|
|
73
66
|
verbose(message, context, ...args) {
|
|
74
|
-
|
|
67
|
+
this.logger.verbose(message, this.buildLogMeta(context, args));
|
|
75
68
|
}
|
|
76
|
-
constructor(context){
|
|
69
|
+
constructor(context, moduleName){
|
|
77
70
|
_define_property(this, "context", void 0);
|
|
71
|
+
_define_property(this, "logger", void 0);
|
|
78
72
|
this.context = context;
|
|
73
|
+
this.logger = moduleName ? (0, _winstonloggerclass.createModuleLogger)(moduleName) : _winstonloggerclass.instance;
|
|
79
74
|
}
|
|
80
75
|
};
|
|
81
76
|
let NestLoggerAdapter = class NestLoggerAdapter {
|