@flusys/nestjs-shared 1.1.0 → 1.2.0-rc.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 +139 -1103
- package/cjs/classes/api-controller.class.js +320 -29
- package/cjs/classes/api-service.class.js +219 -64
- package/cjs/classes/handle-custome-db.js +37 -0
- package/cjs/classes/index.js +1 -2
- package/cjs/classes/winston.logger.class.js +103 -70
- package/cjs/constants/index.js +1 -4
- package/cjs/constants/message-keys.js +68 -0
- package/cjs/constants/permissions.js +114 -11
- package/cjs/decorators/api-response.decorator.js +1 -2
- package/cjs/decorators/index.js +1 -1
- package/cjs/decorators/log-action.decorator.js +149 -0
- package/cjs/decorators/require-permission.decorator.js +5 -5
- package/cjs/dtos/get-by-ids.dto.js +65 -0
- package/cjs/dtos/identity-response.dto.js +4 -8
- package/cjs/dtos/index.js +1 -0
- package/cjs/dtos/response-payload.dto.js +72 -0
- package/cjs/entities/identity.js +8 -2
- package/cjs/entities/index.js +1 -0
- package/cjs/entities/raw-type.js +4 -0
- package/cjs/entities/user-root.js +2 -1
- package/cjs/enums/index.js +20 -0
- package/cjs/enums/notification-type.enum.js +17 -0
- package/cjs/enums/participant-status.enum.js +17 -0
- package/cjs/enums/recurrence-type.enum.js +18 -0
- package/cjs/exceptions/base-app.exception.js +151 -0
- package/cjs/exceptions/index.js +1 -0
- package/cjs/exceptions/permission.exception.js +13 -9
- package/cjs/filters/global-exception.filter.js +279 -0
- package/cjs/filters/index.js +18 -0
- package/cjs/guards/jwt-auth.guard.js +4 -1
- package/cjs/guards/permission.guard.js +71 -77
- package/cjs/index.js +2 -0
- package/cjs/interceptors/idempotency.interceptor.js +1 -1
- package/cjs/interceptors/index.js +0 -1
- package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
- package/cjs/interfaces/index.js +2 -1
- package/cjs/interfaces/notification-adapter.interface.js +11 -0
- package/cjs/interfaces/permission.interface.js +1 -1
- package/cjs/middlewares/logger.middleware.js +83 -26
- package/cjs/modules/datasource/index.js +0 -1
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +46 -85
- package/cjs/modules/utils/utils.service.js +4 -20
- package/cjs/utils/date-time.util.js +94 -0
- package/cjs/utils/index.js +1 -1
- package/cjs/utils/query-helpers.util.js +8 -1
- package/cjs/utils/request.util.js +25 -0
- package/classes/api-controller.class.d.ts +14 -6
- package/classes/api-service.class.d.ts +25 -22
- package/classes/handle-custome-db.d.ts +3 -0
- package/classes/index.d.ts +1 -2
- package/classes/winston.logger.class.d.ts +1 -0
- package/constants/index.d.ts +1 -1
- package/constants/message-keys.d.ts +45 -0
- package/constants/permissions.d.ts +127 -0
- package/decorators/api-response.decorator.d.ts +1 -1
- package/decorators/current-user.decorator.d.ts +1 -1
- package/decorators/index.d.ts +1 -1
- package/decorators/log-action.decorator.d.ts +8 -0
- package/decorators/require-permission.decorator.d.ts +2 -2
- package/dtos/get-by-ids.dto.d.ts +4 -0
- package/dtos/index.d.ts +1 -0
- package/dtos/response-payload.dto.d.ts +8 -0
- package/entities/index.d.ts +1 -0
- package/entities/raw-type.d.ts +1 -0
- package/enums/index.d.ts +3 -0
- package/enums/notification-type.enum.d.ts +6 -0
- package/enums/participant-status.enum.d.ts +6 -0
- package/enums/recurrence-type.enum.d.ts +7 -0
- package/exceptions/base-app.exception.d.ts +41 -0
- package/exceptions/index.d.ts +1 -0
- package/exceptions/permission.exception.d.ts +2 -2
- package/fesm/classes/api-controller.class.js +321 -30
- package/fesm/classes/api-service.class.js +222 -67
- package/fesm/classes/handle-custome-db.js +19 -0
- package/fesm/classes/index.js +1 -2
- package/fesm/classes/winston.logger.class.js +100 -68
- package/fesm/constants/index.js +2 -1
- package/fesm/constants/message-keys.js +49 -0
- package/fesm/constants/permissions.js +86 -14
- package/fesm/decorators/api-response.decorator.js +2 -20
- package/fesm/decorators/index.js +1 -1
- package/fesm/decorators/log-action.decorator.js +139 -0
- package/fesm/decorators/require-permission.decorator.js +17 -65
- package/fesm/dtos/get-by-ids.dto.js +55 -0
- package/fesm/dtos/identity-response.dto.js +4 -8
- package/fesm/dtos/index.js +1 -0
- package/fesm/dtos/response-payload.dto.js +72 -0
- package/fesm/entities/identity.js +8 -2
- package/fesm/entities/index.js +1 -0
- package/fesm/entities/raw-type.js +1 -0
- package/fesm/entities/user-root.js +2 -1
- package/fesm/enums/index.js +3 -0
- package/fesm/enums/notification-type.enum.js +7 -0
- package/fesm/enums/participant-status.enum.js +7 -0
- package/fesm/enums/recurrence-type.enum.js +8 -0
- package/fesm/exceptions/base-app.exception.js +115 -0
- package/fesm/exceptions/index.js +1 -0
- package/fesm/exceptions/permission.exception.js +16 -18
- package/fesm/filters/global-exception.filter.js +269 -0
- package/fesm/filters/index.js +1 -0
- package/fesm/guards/jwt-auth.guard.js +5 -2
- package/fesm/guards/permission.guard.js +73 -79
- package/fesm/index.js +2 -0
- package/fesm/interceptors/idempotency.interceptor.js +2 -2
- package/fesm/interceptors/index.js +0 -1
- package/fesm/interfaces/datasource.interface.js +1 -20
- package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
- package/fesm/interfaces/index.js +2 -1
- package/fesm/interfaces/notification-adapter.interface.js +1 -0
- package/fesm/interfaces/permission.interface.js +1 -3
- package/fesm/middlewares/logger.middleware.js +83 -26
- package/fesm/modules/datasource/index.js +0 -1
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +47 -86
- package/fesm/modules/utils/utils.service.js +5 -21
- package/fesm/utils/date-time.util.js +70 -0
- package/fesm/utils/index.js +1 -1
- package/fesm/utils/query-helpers.util.js +8 -1
- package/fesm/utils/request.util.js +22 -0
- package/filters/global-exception.filter.d.ts +9 -0
- package/filters/index.d.ts +1 -0
- package/guards/permission.guard.d.ts +3 -7
- package/index.d.ts +2 -0
- package/interceptors/idempotency.interceptor.d.ts +1 -1
- package/interceptors/index.d.ts +0 -1
- package/interfaces/api.interface.d.ts +2 -0
- package/interfaces/event-manager-adapter.interface.d.ts +43 -0
- package/interfaces/index.d.ts +2 -1
- package/interfaces/notification-adapter.interface.d.ts +21 -0
- package/interfaces/permission.interface.d.ts +13 -8
- package/modules/datasource/index.d.ts +0 -1
- package/modules/datasource/multi-tenant-datasource.service.d.ts +5 -15
- package/modules/utils/utils.service.d.ts +0 -1
- package/package.json +21 -14
- package/utils/date-time.util.d.ts +8 -0
- package/utils/index.d.ts +1 -1
- package/utils/request.util.d.ts +2 -0
- package/cjs/classes/request-scoped-api.service.js +0 -61
- package/cjs/classes/winston-logger-adapter.class.js +0 -104
- package/cjs/decorators/sanitize-html.decorator.js +0 -36
- package/cjs/interceptors/query-performance.interceptor.js +0 -66
- package/cjs/interfaces/logger.interface.js +0 -7
- package/cjs/modules/datasource/datasource.module.js +0 -117
- package/cjs/utils/error-handler.util.js +0 -90
- package/classes/request-scoped-api.service.d.ts +0 -14
- package/classes/winston-logger-adapter.class.d.ts +0 -22
- package/decorators/sanitize-html.decorator.d.ts +0 -2
- package/fesm/classes/request-scoped-api.service.js +0 -51
- package/fesm/classes/winston-logger-adapter.class.js +0 -107
- package/fesm/decorators/sanitize-html.decorator.js +0 -45
- package/fesm/interceptors/query-performance.interceptor.js +0 -56
- package/fesm/interfaces/logger.interface.js +0 -4
- package/fesm/modules/datasource/datasource.module.js +0 -107
- package/fesm/utils/error-handler.util.js +0 -82
- package/interceptors/query-performance.interceptor.d.ts +0 -8
- package/interfaces/logger.interface.d.ts +0 -7
- package/modules/datasource/datasource.module.d.ts +0 -19
- package/utils/error-handler.util.d.ts +0 -19
|
@@ -25,14 +25,16 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import { CurrentUser, Public, RequirePermission, RequireAnyPermission, RequirePermissionCondition } 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 { ERROR_MESSAGES } from '../constants/message-keys';
|
|
32
|
+
import { CurrentUser, Public, RequireAnyPermission, RequirePermission, RequirePermissionLogic } from '../decorators';
|
|
35
33
|
import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
34
|
+
import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto, GetByIdsDto } from '../dtos';
|
|
35
|
+
import { BaseAppException, NotFoundException } from '../exceptions';
|
|
36
|
+
import { JwtAuthGuard, PermissionGuard } from '../guards';
|
|
37
|
+
import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '../interceptors';
|
|
36
38
|
/**
|
|
37
39
|
* Helper to normalize security config
|
|
38
40
|
*/ function normalizeSecurity(config) {
|
|
@@ -44,6 +46,24 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
44
46
|
};
|
|
45
47
|
return config;
|
|
46
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Helper to check if endpoint is enabled
|
|
51
|
+
*/ function isEndpointEnabled(endpoint, enabledEndpoints) {
|
|
52
|
+
if (!enabledEndpoints || enabledEndpoints === 'all') return true;
|
|
53
|
+
return enabledEndpoints.includes(endpoint);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Helper to conditionally exclude endpoint from Swagger if disabled
|
|
57
|
+
*/ function createEndpointDecorators(security, endpoint, enabledEndpoints) {
|
|
58
|
+
const decorators = [];
|
|
59
|
+
// Add security decorators
|
|
60
|
+
decorators.push(createSecurityDecorators(security));
|
|
61
|
+
// Exclude from Swagger if endpoint is disabled
|
|
62
|
+
if (!isEndpointEnabled(endpoint, enabledEndpoints)) {
|
|
63
|
+
decorators.push(ApiExcludeEndpoint());
|
|
64
|
+
}
|
|
65
|
+
return applyDecorators(...decorators);
|
|
66
|
+
}
|
|
47
67
|
/**
|
|
48
68
|
* Creates security decorators based on configuration
|
|
49
69
|
*/ function createSecurityDecorators(security) {
|
|
@@ -62,11 +82,11 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
62
82
|
// Apply PermissionGuard to check permissions from cache
|
|
63
83
|
decorators.push(UseGuards(JwtAuthGuard, PermissionGuard));
|
|
64
84
|
decorators.push(ApiBearerAuth());
|
|
65
|
-
// Check for complex
|
|
66
|
-
if (security.
|
|
67
|
-
decorators.push(
|
|
85
|
+
// Check for complex logic first
|
|
86
|
+
if (security.logic) {
|
|
87
|
+
decorators.push(RequirePermissionLogic(security.logic));
|
|
68
88
|
} else if (security.permissions && security.permissions.length > 0) {
|
|
69
|
-
if (security.operator === '
|
|
89
|
+
if (security.operator === 'OR') {
|
|
70
90
|
decorators.push(RequireAnyPermission(...security.permissions));
|
|
71
91
|
} else {
|
|
72
92
|
// AND is default and most secure
|
|
@@ -78,13 +98,24 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
78
98
|
return applyDecorators(...decorators);
|
|
79
99
|
}
|
|
80
100
|
/** Creates an API Controller with standardized CRUD endpoints (POST-only RPC pattern) */ export function createApiController(createDtoClass, updateDtoClass, responseDtoClass, options = {}) {
|
|
101
|
+
const excludeExtraneousValues = options.excludeExtraneousValues ?? true;
|
|
102
|
+
function toDto(cls, input) {
|
|
103
|
+
return plainToInstance(cls, input, {
|
|
104
|
+
excludeExtraneousValues
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// Entity name for message keys (defaults to 'item')
|
|
108
|
+
const entityName = options.entityName ?? 'item';
|
|
81
109
|
// Determine if security is global (applies to all) or per-endpoint
|
|
82
110
|
const securityConfig = options.security;
|
|
83
111
|
const endpointKeys = [
|
|
84
112
|
'insert',
|
|
85
113
|
'insertMany',
|
|
86
114
|
'getById',
|
|
115
|
+
'getByIds',
|
|
87
116
|
'getAll',
|
|
117
|
+
'bulkUpsert',
|
|
118
|
+
'getByFilter',
|
|
88
119
|
'update',
|
|
89
120
|
'updateMany',
|
|
90
121
|
'delete'
|
|
@@ -103,27 +134,59 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
103
134
|
insert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insert),
|
|
104
135
|
insertMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insertMany),
|
|
105
136
|
getById: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getById),
|
|
137
|
+
getByIds: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByIds),
|
|
106
138
|
getAll: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getAll),
|
|
139
|
+
getByFilter: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByFilter),
|
|
140
|
+
bulkUpsert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.bulkUpsert),
|
|
107
141
|
update: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.update),
|
|
108
142
|
updateMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.updateMany),
|
|
109
143
|
delete: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.delete)
|
|
110
144
|
};
|
|
111
145
|
let ApiController = class ApiController {
|
|
146
|
+
isEnabled(endpoint) {
|
|
147
|
+
if (this.enabledEndpoints === 'all') return true;
|
|
148
|
+
return Array.isArray(this.enabledEndpoints) && this.enabledEndpoints.includes(endpoint);
|
|
149
|
+
}
|
|
112
150
|
async insert(addDto, user) {
|
|
151
|
+
if (!this.isEnabled('insert')) {
|
|
152
|
+
throw new BaseAppException({
|
|
153
|
+
message: `Endpoint 'insert' is disabled`,
|
|
154
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
155
|
+
messageVariables: {
|
|
156
|
+
endpoint: 'insert'
|
|
157
|
+
},
|
|
158
|
+
status: HttpStatus.FORBIDDEN
|
|
159
|
+
});
|
|
160
|
+
}
|
|
113
161
|
const entity = await this.service.insert(addDto, user);
|
|
114
|
-
const data =
|
|
162
|
+
const data = toDto(responseDtoClass, entity);
|
|
115
163
|
return {
|
|
116
164
|
success: true,
|
|
117
|
-
message:
|
|
165
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} created successfully`,
|
|
166
|
+
messageKey: `${entityName}.create.success`,
|
|
118
167
|
data
|
|
119
168
|
};
|
|
120
169
|
}
|
|
121
170
|
async insertMany(addDto, user) {
|
|
171
|
+
if (!this.isEnabled('insertMany')) {
|
|
172
|
+
throw new BaseAppException({
|
|
173
|
+
message: `Endpoint 'insertMany' is disabled`,
|
|
174
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
175
|
+
messageVariables: {
|
|
176
|
+
endpoint: 'insertMany'
|
|
177
|
+
},
|
|
178
|
+
status: HttpStatus.FORBIDDEN
|
|
179
|
+
});
|
|
180
|
+
}
|
|
122
181
|
const entities = await this.service.insertMany(addDto, user);
|
|
123
|
-
const data = entities.map((item)=>
|
|
182
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
124
183
|
return {
|
|
125
184
|
success: true,
|
|
126
|
-
message: `${data.length}
|
|
185
|
+
message: `${data.length} ${entityName}s created successfully`,
|
|
186
|
+
messageKey: `${entityName}.create.many.success`,
|
|
187
|
+
messageVariables: {
|
|
188
|
+
count: data.length
|
|
189
|
+
},
|
|
127
190
|
data,
|
|
128
191
|
meta: {
|
|
129
192
|
count: data.length,
|
|
@@ -133,29 +196,92 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
133
196
|
};
|
|
134
197
|
}
|
|
135
198
|
async getById(id, body, user) {
|
|
199
|
+
if (!this.isEnabled('getById')) {
|
|
200
|
+
throw new BaseAppException({
|
|
201
|
+
message: `Endpoint 'getById' is disabled`,
|
|
202
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
203
|
+
messageVariables: {
|
|
204
|
+
endpoint: 'getById'
|
|
205
|
+
},
|
|
206
|
+
status: HttpStatus.FORBIDDEN
|
|
207
|
+
});
|
|
208
|
+
}
|
|
136
209
|
const entity = await this.service.findById(id, user, body?.select);
|
|
137
|
-
const data =
|
|
210
|
+
const data = toDto(responseDtoClass, entity);
|
|
138
211
|
return {
|
|
139
212
|
success: true,
|
|
140
|
-
message:
|
|
213
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
|
|
214
|
+
messageKey: `${entityName}.get.success`,
|
|
141
215
|
data
|
|
142
216
|
};
|
|
143
217
|
}
|
|
218
|
+
async getByIds(body, user) {
|
|
219
|
+
if (!this.isEnabled('getByIds')) {
|
|
220
|
+
throw new BaseAppException({
|
|
221
|
+
message: `Endpoint 'getByIds' is disabled`,
|
|
222
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
223
|
+
messageVariables: {
|
|
224
|
+
endpoint: 'getByIds'
|
|
225
|
+
},
|
|
226
|
+
status: HttpStatus.FORBIDDEN
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const entities = await this.service.findByIds(body.ids, user, body?.select);
|
|
230
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
231
|
+
return {
|
|
232
|
+
success: true,
|
|
233
|
+
message: `${data.length} ${entityName}${data.length !== 1 ? 's' : ''} retrieved successfully`,
|
|
234
|
+
messageKey: `${entityName}.get.by.ids.success`,
|
|
235
|
+
data,
|
|
236
|
+
meta: {
|
|
237
|
+
total: data.length,
|
|
238
|
+
page: 0,
|
|
239
|
+
pageSize: data.length,
|
|
240
|
+
count: data.length,
|
|
241
|
+
totalPages: 1
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
144
245
|
async update(updateDto, user) {
|
|
246
|
+
if (!this.isEnabled('update')) {
|
|
247
|
+
throw new BaseAppException({
|
|
248
|
+
message: `Endpoint 'update' is disabled`,
|
|
249
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
250
|
+
messageVariables: {
|
|
251
|
+
endpoint: 'update'
|
|
252
|
+
},
|
|
253
|
+
status: HttpStatus.FORBIDDEN
|
|
254
|
+
});
|
|
255
|
+
}
|
|
145
256
|
const entity = await this.service.update(updateDto, user);
|
|
146
|
-
const data =
|
|
257
|
+
const data = toDto(responseDtoClass, entity);
|
|
147
258
|
return {
|
|
148
259
|
success: true,
|
|
149
|
-
message:
|
|
260
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} updated successfully`,
|
|
261
|
+
messageKey: `${entityName}.update.success`,
|
|
150
262
|
data
|
|
151
263
|
};
|
|
152
264
|
}
|
|
153
265
|
async updateMany(updateDtos, user) {
|
|
266
|
+
if (!this.isEnabled('updateMany')) {
|
|
267
|
+
throw new BaseAppException({
|
|
268
|
+
message: `Endpoint 'updateMany' is disabled`,
|
|
269
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
270
|
+
messageVariables: {
|
|
271
|
+
endpoint: 'updateMany'
|
|
272
|
+
},
|
|
273
|
+
status: HttpStatus.FORBIDDEN
|
|
274
|
+
});
|
|
275
|
+
}
|
|
154
276
|
const entities = await this.service.updateMany(updateDtos, user);
|
|
155
|
-
const data =
|
|
277
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
156
278
|
return {
|
|
157
279
|
success: true,
|
|
158
|
-
message: `${data.length}
|
|
280
|
+
message: `${data.length} ${entityName}s updated successfully`,
|
|
281
|
+
messageKey: `${entityName}.update.many.success`,
|
|
282
|
+
messageVariables: {
|
|
283
|
+
count: data.length
|
|
284
|
+
},
|
|
159
285
|
data,
|
|
160
286
|
meta: {
|
|
161
287
|
count: data.length,
|
|
@@ -164,15 +290,93 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
164
290
|
}
|
|
165
291
|
};
|
|
166
292
|
}
|
|
293
|
+
async bulkUpsert(dtos, user) {
|
|
294
|
+
if (!this.isEnabled('bulkUpsert')) {
|
|
295
|
+
throw new BaseAppException({
|
|
296
|
+
message: `Endpoint 'bulkUpsert' is disabled`,
|
|
297
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
298
|
+
messageVariables: {
|
|
299
|
+
endpoint: 'bulkUpsert'
|
|
300
|
+
},
|
|
301
|
+
status: HttpStatus.FORBIDDEN
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
const toInsert = [];
|
|
305
|
+
const toUpdate = [];
|
|
306
|
+
for (const dto of dtos){
|
|
307
|
+
if ('id' in dto && dto.id) {
|
|
308
|
+
toUpdate.push(dto);
|
|
309
|
+
} else {
|
|
310
|
+
toInsert.push(dto);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const allEntities = await this.service.bulkUpsert(toInsert, toUpdate, user);
|
|
314
|
+
const data = allEntities.map((item)=>toDto(responseDtoClass, item));
|
|
315
|
+
return {
|
|
316
|
+
success: true,
|
|
317
|
+
message: `${data.length} ${entityName}s upserted successfully`,
|
|
318
|
+
messageKey: `${entityName}.upsert.many.success`,
|
|
319
|
+
messageVariables: {
|
|
320
|
+
count: data.length
|
|
321
|
+
},
|
|
322
|
+
data,
|
|
323
|
+
meta: {
|
|
324
|
+
count: data.length,
|
|
325
|
+
total: dtos.length,
|
|
326
|
+
failed: dtos.length - data.length
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
async getByFilter(filter, user) {
|
|
331
|
+
if (!this.isEnabled('getByFilter')) {
|
|
332
|
+
throw new BaseAppException({
|
|
333
|
+
message: `Endpoint 'getByFilter' is disabled`,
|
|
334
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
335
|
+
messageVariables: {
|
|
336
|
+
endpoint: 'getByFilter'
|
|
337
|
+
},
|
|
338
|
+
status: HttpStatus.FORBIDDEN
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const result = await this.service.getAll('', {
|
|
342
|
+
filter,
|
|
343
|
+
pagination: {
|
|
344
|
+
currentPage: 0,
|
|
345
|
+
pageSize: 1
|
|
346
|
+
}
|
|
347
|
+
}, user);
|
|
348
|
+
if (!result.data || result.data.length === 0) {
|
|
349
|
+
throw new NotFoundException(entityName);
|
|
350
|
+
}
|
|
351
|
+
const entity = result.data[0];
|
|
352
|
+
const data = toDto(responseDtoClass, entity);
|
|
353
|
+
return {
|
|
354
|
+
success: true,
|
|
355
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
|
|
356
|
+
messageKey: `${entityName}.get.by.filter.success`,
|
|
357
|
+
data
|
|
358
|
+
};
|
|
359
|
+
}
|
|
167
360
|
async getAll(filterAndPaginationDto, user, search) {
|
|
361
|
+
if (!this.isEnabled('getAll')) {
|
|
362
|
+
throw new BaseAppException({
|
|
363
|
+
message: `Endpoint 'getAll' is disabled`,
|
|
364
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
365
|
+
messageVariables: {
|
|
366
|
+
endpoint: 'getAll'
|
|
367
|
+
},
|
|
368
|
+
status: HttpStatus.FORBIDDEN
|
|
369
|
+
});
|
|
370
|
+
}
|
|
168
371
|
const result = await this.service.getAll(search ?? '', filterAndPaginationDto, user);
|
|
169
|
-
const data =
|
|
372
|
+
const data = result.data.map((item)=>toDto(responseDtoClass, item));
|
|
170
373
|
const page = filterAndPaginationDto.pagination?.currentPage ?? 0;
|
|
171
374
|
const pageSize = filterAndPaginationDto.pagination?.pageSize ?? data.length;
|
|
172
375
|
const totalPages = pageSize > 0 ? Math.ceil(result.total / pageSize) : 1;
|
|
173
376
|
return {
|
|
174
377
|
success: true,
|
|
175
|
-
message:
|
|
378
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)}s retrieved successfully`,
|
|
379
|
+
messageKey: `${entityName}.get.all.success`,
|
|
176
380
|
data,
|
|
177
381
|
meta: {
|
|
178
382
|
total: result.total,
|
|
@@ -185,21 +389,37 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
185
389
|
};
|
|
186
390
|
}
|
|
187
391
|
async delete(deleteDto, user) {
|
|
392
|
+
if (!this.isEnabled('delete')) {
|
|
393
|
+
throw new BaseAppException({
|
|
394
|
+
message: `Endpoint 'delete' is disabled`,
|
|
395
|
+
messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
396
|
+
messageVariables: {
|
|
397
|
+
endpoint: 'delete'
|
|
398
|
+
},
|
|
399
|
+
status: HttpStatus.FORBIDDEN
|
|
400
|
+
});
|
|
401
|
+
}
|
|
188
402
|
await this.service.delete(deleteDto, user);
|
|
189
403
|
const count = Array.isArray(deleteDto.id) ? deleteDto.id.length : 1;
|
|
190
|
-
const action = deleteDto.type === 'restore' ? '
|
|
404
|
+
const action = deleteDto.type === 'restore' ? 'restore' : 'delete';
|
|
191
405
|
return {
|
|
192
406
|
success: true,
|
|
193
|
-
message: `${count}
|
|
407
|
+
message: `${count} ${entityName}${count > 1 ? 's' : ''} ${action}d successfully`,
|
|
408
|
+
messageKey: `${entityName}.${action}.success`,
|
|
409
|
+
messageVariables: {
|
|
410
|
+
count
|
|
411
|
+
}
|
|
194
412
|
};
|
|
195
413
|
}
|
|
196
414
|
constructor(service){
|
|
197
415
|
_define_property(this, "service", void 0);
|
|
416
|
+
_define_property(this, "enabledEndpoints", void 0);
|
|
198
417
|
this.service = service;
|
|
418
|
+
this.enabledEndpoints = options.enabledEndpoints ?? 'all';
|
|
199
419
|
}
|
|
200
420
|
};
|
|
201
421
|
_ts_decorate([
|
|
202
|
-
|
|
422
|
+
createEndpointDecorators(security.insert, 'insert', options.enabledEndpoints),
|
|
203
423
|
Version(VERSION_NEUTRAL),
|
|
204
424
|
Post('insert'),
|
|
205
425
|
HttpCode(HttpStatus.CREATED),
|
|
@@ -227,7 +447,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
227
447
|
_ts_metadata("design:returntype", Promise)
|
|
228
448
|
], ApiController.prototype, "insert", null);
|
|
229
449
|
_ts_decorate([
|
|
230
|
-
|
|
450
|
+
createEndpointDecorators(security.insertMany, 'insertMany', options.enabledEndpoints),
|
|
231
451
|
Version(VERSION_NEUTRAL),
|
|
232
452
|
Post('insert-many'),
|
|
233
453
|
HttpCode(HttpStatus.CREATED),
|
|
@@ -256,7 +476,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
256
476
|
_ts_metadata("design:returntype", Promise)
|
|
257
477
|
], ApiController.prototype, "insertMany", null);
|
|
258
478
|
_ts_decorate([
|
|
259
|
-
|
|
479
|
+
createEndpointDecorators(security.getById, 'getById', options.enabledEndpoints),
|
|
260
480
|
Version(VERSION_NEUTRAL),
|
|
261
481
|
Post('get/:id'),
|
|
262
482
|
HttpCode(HttpStatus.OK),
|
|
@@ -286,7 +506,29 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
286
506
|
_ts_metadata("design:returntype", Promise)
|
|
287
507
|
], ApiController.prototype, "getById", null);
|
|
288
508
|
_ts_decorate([
|
|
289
|
-
|
|
509
|
+
createEndpointDecorators(security.getByIds, 'getByIds', options.enabledEndpoints),
|
|
510
|
+
Version(VERSION_NEUTRAL),
|
|
511
|
+
Post('get-by-ids'),
|
|
512
|
+
HttpCode(HttpStatus.OK),
|
|
513
|
+
ApiOperation({
|
|
514
|
+
summary: 'Get items by IDs',
|
|
515
|
+
description: 'Retrieves multiple items by their IDs. Optionally specify fields to select.'
|
|
516
|
+
}),
|
|
517
|
+
ApiBody({
|
|
518
|
+
type: GetByIdsDto
|
|
519
|
+
}),
|
|
520
|
+
ApiResponseDto(responseDtoClass, true, 'list'),
|
|
521
|
+
_ts_param(0, Body()),
|
|
522
|
+
_ts_param(1, CurrentUser()),
|
|
523
|
+
_ts_metadata("design:type", Function),
|
|
524
|
+
_ts_metadata("design:paramtypes", [
|
|
525
|
+
typeof GetByIdsDto === "undefined" ? Object : GetByIdsDto,
|
|
526
|
+
Object
|
|
527
|
+
]),
|
|
528
|
+
_ts_metadata("design:returntype", Promise)
|
|
529
|
+
], ApiController.prototype, "getByIds", null);
|
|
530
|
+
_ts_decorate([
|
|
531
|
+
createEndpointDecorators(security.update, 'update', options.enabledEndpoints),
|
|
290
532
|
Version(VERSION_NEUTRAL),
|
|
291
533
|
Post('update'),
|
|
292
534
|
HttpCode(HttpStatus.OK),
|
|
@@ -309,7 +551,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
309
551
|
_ts_metadata("design:returntype", Promise)
|
|
310
552
|
], ApiController.prototype, "update", null);
|
|
311
553
|
_ts_decorate([
|
|
312
|
-
|
|
554
|
+
createEndpointDecorators(security.updateMany, 'updateMany', options.enabledEndpoints),
|
|
313
555
|
Version(VERSION_NEUTRAL),
|
|
314
556
|
Post('update-many'),
|
|
315
557
|
HttpCode(HttpStatus.OK),
|
|
@@ -333,7 +575,56 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
333
575
|
_ts_metadata("design:returntype", Promise)
|
|
334
576
|
], ApiController.prototype, "updateMany", null);
|
|
335
577
|
_ts_decorate([
|
|
336
|
-
|
|
578
|
+
createEndpointDecorators(security.bulkUpsert, 'bulkUpsert', options.enabledEndpoints),
|
|
579
|
+
Version(VERSION_NEUTRAL),
|
|
580
|
+
Post('bulk-upsert'),
|
|
581
|
+
HttpCode(HttpStatus.OK),
|
|
582
|
+
ApiOperation({
|
|
583
|
+
summary: 'Create or update multiple items',
|
|
584
|
+
description: 'Bulk upsert: items without ID are created, items with ID are updated.'
|
|
585
|
+
}),
|
|
586
|
+
ApiResponseDto(responseDtoClass, true, 'bulk'),
|
|
587
|
+
ApiBody({
|
|
588
|
+
type: createDtoClass,
|
|
589
|
+
isArray: true
|
|
590
|
+
}),
|
|
591
|
+
UseInterceptors(SetCreatedByOnBody, SetUpdateByOnBody, Slug),
|
|
592
|
+
_ts_param(0, Body()),
|
|
593
|
+
_ts_param(1, CurrentUser()),
|
|
594
|
+
_ts_metadata("design:type", Function),
|
|
595
|
+
_ts_metadata("design:paramtypes", [
|
|
596
|
+
Array,
|
|
597
|
+
Object
|
|
598
|
+
]),
|
|
599
|
+
_ts_metadata("design:returntype", Promise)
|
|
600
|
+
], ApiController.prototype, "bulkUpsert", null);
|
|
601
|
+
_ts_decorate([
|
|
602
|
+
createEndpointDecorators(security.getByFilter, 'getByFilter', options.enabledEndpoints),
|
|
603
|
+
Version(VERSION_NEUTRAL),
|
|
604
|
+
Post('get-by-filter'),
|
|
605
|
+
HttpCode(HttpStatus.OK),
|
|
606
|
+
ApiOperation({
|
|
607
|
+
summary: 'Get single item by filter',
|
|
608
|
+
description: 'Retrieves a single item matching the provided filter criteria. Returns first match if multiple exist.'
|
|
609
|
+
}),
|
|
610
|
+
ApiBody({
|
|
611
|
+
schema: {
|
|
612
|
+
type: 'object',
|
|
613
|
+
description: 'Filter criteria'
|
|
614
|
+
}
|
|
615
|
+
}),
|
|
616
|
+
ApiResponseDto(responseDtoClass),
|
|
617
|
+
_ts_param(0, Body()),
|
|
618
|
+
_ts_param(1, CurrentUser()),
|
|
619
|
+
_ts_metadata("design:type", Function),
|
|
620
|
+
_ts_metadata("design:paramtypes", [
|
|
621
|
+
typeof Record === "undefined" ? Object : Record,
|
|
622
|
+
Object
|
|
623
|
+
]),
|
|
624
|
+
_ts_metadata("design:returntype", Promise)
|
|
625
|
+
], ApiController.prototype, "getByFilter", null);
|
|
626
|
+
_ts_decorate([
|
|
627
|
+
createEndpointDecorators(security.getAll, 'getAll', options.enabledEndpoints),
|
|
337
628
|
Version(VERSION_NEUTRAL),
|
|
338
629
|
Post('get-all'),
|
|
339
630
|
HttpCode(HttpStatus.OK),
|
|
@@ -362,7 +653,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
362
653
|
_ts_metadata("design:returntype", Promise)
|
|
363
654
|
], ApiController.prototype, "getAll", null);
|
|
364
655
|
_ts_decorate([
|
|
365
|
-
|
|
656
|
+
createEndpointDecorators(security.delete, 'delete', options.enabledEndpoints),
|
|
366
657
|
Version(VERSION_NEUTRAL),
|
|
367
658
|
Post('delete'),
|
|
368
659
|
HttpCode(HttpStatus.OK),
|