@flusys/nestjs-shared 1.1.0-beta → 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 +146 -1330
- package/cjs/classes/api-controller.class.js +325 -49
- package/cjs/classes/api-service.class.js +271 -149
- package/cjs/classes/handle-custome-db.js +37 -0
- package/cjs/classes/index.js +2 -2
- package/cjs/classes/winston.logger.class.js +103 -70
- package/cjs/constants/index.js +15 -4
- package/cjs/constants/message-keys.js +68 -0
- package/cjs/constants/permissions.js +287 -0
- package/cjs/decorators/api-response.decorator.js +2 -3
- package/cjs/decorators/index.js +1 -0
- package/cjs/decorators/log-action.decorator.js +149 -0
- package/cjs/decorators/require-permission.decorator.js +5 -5
- package/cjs/dtos/delete.dto.js +10 -0
- package/cjs/dtos/filter-and-pagination.dto.js +24 -34
- 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/pagination.dto.js +4 -8
- package/cjs/dtos/response-payload.dto.js +68 -112
- package/cjs/entities/identity.js +12 -6
- package/cjs/entities/index.js +1 -0
- package/cjs/entities/raw-type.js +4 -0
- package/cjs/entities/user-root.js +15 -15
- 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 +88 -148
- package/cjs/index.js +2 -0
- package/cjs/interceptors/idempotency.interceptor.js +1 -1
- package/cjs/interceptors/index.js +1 -4
- package/cjs/interceptors/set-user-field-on-body.interceptor.js +60 -0
- package/cjs/interceptors/slug.interceptor.js +30 -9
- package/cjs/interfaces/datasource.interface.js +4 -0
- package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
- package/cjs/interfaces/index.js +4 -2
- package/cjs/interfaces/module-config.interface.js +4 -0
- package/cjs/interfaces/notification-adapter.interface.js +11 -0
- package/cjs/interfaces/permission.interface.js +1 -1
- package/cjs/middlewares/logger.middleware.js +133 -115
- package/cjs/modules/cache/cache.module.js +3 -3
- package/cjs/modules/datasource/index.js +0 -1
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +60 -183
- package/cjs/modules/utils/utils.service.js +27 -206
- package/cjs/utils/date-time.util.js +94 -0
- package/cjs/utils/html-sanitizer.util.js +64 -0
- package/cjs/utils/index.js +5 -1
- package/cjs/utils/query-helpers.util.js +60 -0
- package/cjs/utils/request.util.js +96 -0
- package/cjs/utils/string.util.js +63 -0
- package/classes/api-controller.class.d.ts +16 -8
- package/classes/api-service.class.d.ts +30 -25
- package/classes/handle-custome-db.d.ts +3 -0
- package/classes/index.d.ts +2 -2
- package/classes/winston.logger.class.d.ts +1 -0
- package/constants/index.d.ts +2 -1
- package/constants/message-keys.d.ts +45 -0
- package/constants/permissions.d.ts +306 -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 -0
- package/decorators/log-action.decorator.d.ts +8 -0
- package/decorators/require-permission.decorator.d.ts +2 -2
- package/dtos/delete.dto.d.ts +1 -0
- package/dtos/filter-and-pagination.dto.d.ts +0 -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 -20
- 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 +326 -50
- package/fesm/classes/api-service.class.js +274 -152
- package/fesm/classes/handle-custome-db.js +19 -0
- package/fesm/classes/index.js +3 -2
- package/fesm/classes/winston.logger.class.js +100 -68
- package/fesm/constants/index.js +4 -1
- package/fesm/constants/message-keys.js +49 -0
- package/fesm/constants/permissions.js +200 -0
- package/fesm/decorators/api-response.decorator.js +3 -21
- package/fesm/decorators/index.js +1 -0
- package/fesm/decorators/log-action.decorator.js +139 -0
- package/fesm/decorators/require-permission.decorator.js +17 -65
- package/fesm/dtos/delete.dto.js +12 -2
- package/fesm/dtos/filter-and-pagination.dto.js +26 -47
- 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/pagination.dto.js +4 -8
- package/fesm/dtos/response-payload.dto.js +68 -103
- package/fesm/entities/identity.js +12 -6
- package/fesm/entities/index.js +1 -0
- package/fesm/entities/raw-type.js +1 -0
- package/fesm/entities/user-root.js +15 -15
- 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 +90 -150
- package/fesm/index.js +2 -0
- package/fesm/interceptors/idempotency.interceptor.js +2 -2
- package/fesm/interceptors/index.js +1 -4
- package/fesm/interceptors/set-user-field-on-body.interceptor.js +39 -0
- package/fesm/interceptors/slug.interceptor.js +31 -10
- package/fesm/interfaces/datasource.interface.js +1 -0
- package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
- package/fesm/interfaces/index.js +4 -2
- package/fesm/interfaces/module-config.interface.js +5 -0
- package/fesm/interfaces/notification-adapter.interface.js +1 -0
- package/fesm/interfaces/permission.interface.js +1 -3
- package/fesm/middlewares/logger.middleware.js +133 -109
- package/fesm/modules/cache/cache.module.js +2 -2
- package/fesm/modules/datasource/index.js +0 -1
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +61 -184
- package/fesm/modules/utils/utils.service.js +27 -206
- package/fesm/utils/date-time.util.js +70 -0
- package/fesm/utils/html-sanitizer.util.js +69 -0
- package/fesm/utils/index.js +5 -1
- package/fesm/utils/query-helpers.util.js +85 -0
- package/fesm/utils/request.util.js +81 -0
- package/fesm/utils/string.util.js +71 -0
- package/filters/global-exception.filter.d.ts +9 -0
- package/filters/index.d.ts +1 -0
- package/guards/permission.guard.d.ts +4 -6
- package/index.d.ts +2 -0
- package/interceptors/idempotency.interceptor.d.ts +1 -1
- package/interceptors/index.d.ts +1 -4
- package/interceptors/set-user-field-on-body.interceptor.d.ts +5 -0
- package/interceptors/slug.interceptor.d.ts +2 -1
- package/interfaces/api.interface.d.ts +4 -2
- package/interfaces/datasource.interface.d.ts +5 -0
- package/interfaces/event-manager-adapter.interface.d.ts +43 -0
- package/interfaces/identity.interface.d.ts +4 -4
- package/interfaces/index.d.ts +4 -2
- package/interfaces/logged-user-info.interface.d.ts +0 -2
- package/interfaces/module-config.interface.d.ts +6 -0
- package/interfaces/notification-adapter.interface.d.ts +21 -0
- package/interfaces/permission.interface.d.ts +13 -9
- package/middlewares/logger.middleware.d.ts +2 -2
- package/modules/datasource/index.d.ts +0 -1
- package/modules/datasource/multi-tenant-datasource.service.d.ts +5 -16
- package/modules/utils/utils.service.d.ts +3 -14
- package/package.json +21 -14
- package/utils/date-time.util.d.ts +8 -0
- package/utils/html-sanitizer.util.d.ts +2 -0
- package/utils/index.d.ts +5 -1
- package/utils/query-helpers.util.d.ts +16 -0
- package/utils/request.util.d.ts +6 -0
- package/utils/string.util.d.ts +2 -0
- package/cjs/classes/request-scoped-api.service.js +0 -61
- package/cjs/classes/winston-logger-adapter.class.js +0 -121
- package/cjs/interceptors/query-performance.interceptor.js +0 -66
- package/cjs/interceptors/set-create-by-on-body.interceptor.js +0 -40
- package/cjs/interceptors/set-delete-by-on-body.interceptor.js +0 -40
- package/cjs/interceptors/set-update-by-on-body.interceptor.js +0 -40
- package/cjs/interfaces/base-query.interface.js +0 -6
- package/cjs/interfaces/logger.interface.js +0 -7
- package/cjs/modules/datasource/datasource.module.js +0 -120
- package/cjs/utils/error-handler.util.js +0 -67
- package/classes/request-scoped-api.service.d.ts +0 -13
- package/classes/winston-logger-adapter.class.d.ts +0 -20
- package/fesm/classes/request-scoped-api.service.js +0 -51
- package/fesm/classes/winston-logger-adapter.class.js +0 -124
- package/fesm/interceptors/query-performance.interceptor.js +0 -56
- package/fesm/interceptors/set-create-by-on-body.interceptor.js +0 -30
- package/fesm/interceptors/set-delete-by-on-body.interceptor.js +0 -30
- package/fesm/interceptors/set-update-by-on-body.interceptor.js +0 -30
- package/fesm/interfaces/base-query.interface.js +0 -3
- package/fesm/interfaces/logger.interface.js +0 -4
- package/fesm/modules/datasource/datasource.module.js +0 -110
- package/fesm/utils/error-handler.util.js +0 -59
- package/interceptors/query-performance.interceptor.d.ts +0 -8
- package/interceptors/set-create-by-on-body.interceptor.d.ts +0 -5
- package/interceptors/set-delete-by-on-body.interceptor.d.ts +0 -5
- package/interceptors/set-update-by-on-body.interceptor.d.ts +0 -5
- package/interfaces/base-query.interface.d.ts +0 -7
- package/interfaces/logger.interface.d.ts +0 -7
- package/modules/datasource/datasource.module.d.ts +0 -18
- package/utils/error-handler.util.d.ts +0 -24
|
@@ -8,14 +8,16 @@ Object.defineProperty(exports, "createApiController", {
|
|
|
8
8
|
return createApiController;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const _decorators = require("@flusys/nestjs-shared/decorators");
|
|
12
|
-
const _dtos = require("@flusys/nestjs-shared/dtos");
|
|
13
|
-
const _guards = require("@flusys/nestjs-shared/guards");
|
|
14
|
-
const _interceptors = require("@flusys/nestjs-shared/interceptors");
|
|
15
11
|
const _common = require("@nestjs/common");
|
|
16
12
|
const _swagger = require("@nestjs/swagger");
|
|
17
13
|
const _classtransformer = require("class-transformer");
|
|
14
|
+
const _messagekeys = require("../constants/message-keys");
|
|
15
|
+
const _decorators = require("../decorators");
|
|
18
16
|
const _apiresponsedecorator = require("../decorators/api-response.decorator");
|
|
17
|
+
const _dtos = require("../dtos");
|
|
18
|
+
const _exceptions = require("../exceptions");
|
|
19
|
+
const _guards = require("../guards");
|
|
20
|
+
const _interceptors = require("../interceptors");
|
|
19
21
|
function _define_property(obj, key, value) {
|
|
20
22
|
if (key in obj) {
|
|
21
23
|
Object.defineProperty(obj, key, {
|
|
@@ -54,6 +56,24 @@ function _ts_param(paramIndex, decorator) {
|
|
|
54
56
|
};
|
|
55
57
|
return config;
|
|
56
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Helper to check if endpoint is enabled
|
|
61
|
+
*/ function isEndpointEnabled(endpoint, enabledEndpoints) {
|
|
62
|
+
if (!enabledEndpoints || enabledEndpoints === 'all') return true;
|
|
63
|
+
return enabledEndpoints.includes(endpoint);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Helper to conditionally exclude endpoint from Swagger if disabled
|
|
67
|
+
*/ function createEndpointDecorators(security, endpoint, enabledEndpoints) {
|
|
68
|
+
const decorators = [];
|
|
69
|
+
// Add security decorators
|
|
70
|
+
decorators.push(createSecurityDecorators(security));
|
|
71
|
+
// Exclude from Swagger if endpoint is disabled
|
|
72
|
+
if (!isEndpointEnabled(endpoint, enabledEndpoints)) {
|
|
73
|
+
decorators.push((0, _swagger.ApiExcludeEndpoint)());
|
|
74
|
+
}
|
|
75
|
+
return (0, _common.applyDecorators)(...decorators);
|
|
76
|
+
}
|
|
57
77
|
/**
|
|
58
78
|
* Creates security decorators based on configuration
|
|
59
79
|
*/ function createSecurityDecorators(security) {
|
|
@@ -72,11 +92,11 @@ function _ts_param(paramIndex, decorator) {
|
|
|
72
92
|
// Apply PermissionGuard to check permissions from cache
|
|
73
93
|
decorators.push((0, _common.UseGuards)(_guards.JwtAuthGuard, _guards.PermissionGuard));
|
|
74
94
|
decorators.push((0, _swagger.ApiBearerAuth)());
|
|
75
|
-
// Check for complex
|
|
76
|
-
if (security.
|
|
77
|
-
decorators.push((0, _decorators.
|
|
95
|
+
// Check for complex logic first
|
|
96
|
+
if (security.logic) {
|
|
97
|
+
decorators.push((0, _decorators.RequirePermissionLogic)(security.logic));
|
|
78
98
|
} else if (security.permissions && security.permissions.length > 0) {
|
|
79
|
-
if (security.operator === '
|
|
99
|
+
if (security.operator === 'OR') {
|
|
80
100
|
decorators.push((0, _decorators.RequireAnyPermission)(...security.permissions));
|
|
81
101
|
} else {
|
|
82
102
|
// AND is default and most secure
|
|
@@ -88,13 +108,24 @@ function _ts_param(paramIndex, decorator) {
|
|
|
88
108
|
return (0, _common.applyDecorators)(...decorators);
|
|
89
109
|
}
|
|
90
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
|
+
}
|
|
117
|
+
// Entity name for message keys (defaults to 'item')
|
|
118
|
+
const entityName = options.entityName ?? 'item';
|
|
91
119
|
// Determine if security is global (applies to all) or per-endpoint
|
|
92
120
|
const securityConfig = options.security;
|
|
93
121
|
const endpointKeys = [
|
|
94
122
|
'insert',
|
|
95
123
|
'insertMany',
|
|
96
124
|
'getById',
|
|
125
|
+
'getByIds',
|
|
97
126
|
'getAll',
|
|
127
|
+
'bulkUpsert',
|
|
128
|
+
'getByFilter',
|
|
98
129
|
'update',
|
|
99
130
|
'updateMany',
|
|
100
131
|
'delete'
|
|
@@ -104,34 +135,68 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
104
135
|
// 2. It's an object with 'level' property but no endpoint keys
|
|
105
136
|
const isGlobalSecurity = typeof securityConfig === 'string' || securityConfig && typeof securityConfig === 'object' && 'level' in securityConfig && !endpointKeys.some((key)=>key in securityConfig);
|
|
106
137
|
// Normalize security config for each endpoint
|
|
138
|
+
// IMPORTANT: When per-endpoint security is specified, default to 'jwt' for unconfigured endpoints
|
|
139
|
+
// to prevent accidentally exposing endpoints without authentication
|
|
107
140
|
const defaultSecurity = isGlobalSecurity ? normalizeSecurity(securityConfig) : {
|
|
108
|
-
level: '
|
|
141
|
+
level: 'jwt'
|
|
109
142
|
};
|
|
110
143
|
const security = {
|
|
111
144
|
insert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insert),
|
|
112
145
|
insertMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insertMany),
|
|
113
146
|
getById: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getById),
|
|
147
|
+
getByIds: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByIds),
|
|
114
148
|
getAll: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getAll),
|
|
149
|
+
getByFilter: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByFilter),
|
|
150
|
+
bulkUpsert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.bulkUpsert),
|
|
115
151
|
update: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.update),
|
|
116
152
|
updateMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.updateMany),
|
|
117
153
|
delete: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.delete)
|
|
118
154
|
};
|
|
119
155
|
let ApiController = class ApiController {
|
|
156
|
+
isEnabled(endpoint) {
|
|
157
|
+
if (this.enabledEndpoints === 'all') return true;
|
|
158
|
+
return Array.isArray(this.enabledEndpoints) && this.enabledEndpoints.includes(endpoint);
|
|
159
|
+
}
|
|
120
160
|
async insert(addDto, user) {
|
|
161
|
+
if (!this.isEnabled('insert')) {
|
|
162
|
+
throw new _exceptions.BaseAppException({
|
|
163
|
+
message: `Endpoint 'insert' is disabled`,
|
|
164
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
165
|
+
messageVariables: {
|
|
166
|
+
endpoint: 'insert'
|
|
167
|
+
},
|
|
168
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
169
|
+
});
|
|
170
|
+
}
|
|
121
171
|
const entity = await this.service.insert(addDto, user);
|
|
122
|
-
const data = (
|
|
172
|
+
const data = toDto(responseDtoClass, entity);
|
|
123
173
|
return {
|
|
124
174
|
success: true,
|
|
125
|
-
message:
|
|
175
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} created successfully`,
|
|
176
|
+
messageKey: `${entityName}.create.success`,
|
|
126
177
|
data
|
|
127
178
|
};
|
|
128
179
|
}
|
|
129
180
|
async insertMany(addDto, user) {
|
|
181
|
+
if (!this.isEnabled('insertMany')) {
|
|
182
|
+
throw new _exceptions.BaseAppException({
|
|
183
|
+
message: `Endpoint 'insertMany' is disabled`,
|
|
184
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
185
|
+
messageVariables: {
|
|
186
|
+
endpoint: 'insertMany'
|
|
187
|
+
},
|
|
188
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
189
|
+
});
|
|
190
|
+
}
|
|
130
191
|
const entities = await this.service.insertMany(addDto, user);
|
|
131
|
-
const data = entities.map((item)=>(
|
|
192
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
132
193
|
return {
|
|
133
194
|
success: true,
|
|
134
|
-
message: `${data.length}
|
|
195
|
+
message: `${data.length} ${entityName}s created successfully`,
|
|
196
|
+
messageKey: `${entityName}.create.many.success`,
|
|
197
|
+
messageVariables: {
|
|
198
|
+
count: data.length
|
|
199
|
+
},
|
|
135
200
|
data,
|
|
136
201
|
meta: {
|
|
137
202
|
count: data.length,
|
|
@@ -141,29 +206,92 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
141
206
|
};
|
|
142
207
|
}
|
|
143
208
|
async getById(id, body, user) {
|
|
209
|
+
if (!this.isEnabled('getById')) {
|
|
210
|
+
throw new _exceptions.BaseAppException({
|
|
211
|
+
message: `Endpoint 'getById' is disabled`,
|
|
212
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
213
|
+
messageVariables: {
|
|
214
|
+
endpoint: 'getById'
|
|
215
|
+
},
|
|
216
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
217
|
+
});
|
|
218
|
+
}
|
|
144
219
|
const entity = await this.service.findById(id, user, body?.select);
|
|
145
|
-
const data = (
|
|
220
|
+
const data = toDto(responseDtoClass, entity);
|
|
146
221
|
return {
|
|
147
222
|
success: true,
|
|
148
|
-
message:
|
|
223
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
|
|
224
|
+
messageKey: `${entityName}.get.success`,
|
|
149
225
|
data
|
|
150
226
|
};
|
|
151
227
|
}
|
|
228
|
+
async getByIds(body, user) {
|
|
229
|
+
if (!this.isEnabled('getByIds')) {
|
|
230
|
+
throw new _exceptions.BaseAppException({
|
|
231
|
+
message: `Endpoint 'getByIds' is disabled`,
|
|
232
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
233
|
+
messageVariables: {
|
|
234
|
+
endpoint: 'getByIds'
|
|
235
|
+
},
|
|
236
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const entities = await this.service.findByIds(body.ids, user, body?.select);
|
|
240
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
241
|
+
return {
|
|
242
|
+
success: true,
|
|
243
|
+
message: `${data.length} ${entityName}${data.length !== 1 ? 's' : ''} retrieved successfully`,
|
|
244
|
+
messageKey: `${entityName}.get.by.ids.success`,
|
|
245
|
+
data,
|
|
246
|
+
meta: {
|
|
247
|
+
total: data.length,
|
|
248
|
+
page: 0,
|
|
249
|
+
pageSize: data.length,
|
|
250
|
+
count: data.length,
|
|
251
|
+
totalPages: 1
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
152
255
|
async update(updateDto, user) {
|
|
256
|
+
if (!this.isEnabled('update')) {
|
|
257
|
+
throw new _exceptions.BaseAppException({
|
|
258
|
+
message: `Endpoint 'update' is disabled`,
|
|
259
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
260
|
+
messageVariables: {
|
|
261
|
+
endpoint: 'update'
|
|
262
|
+
},
|
|
263
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
264
|
+
});
|
|
265
|
+
}
|
|
153
266
|
const entity = await this.service.update(updateDto, user);
|
|
154
|
-
const data = (
|
|
267
|
+
const data = toDto(responseDtoClass, entity);
|
|
155
268
|
return {
|
|
156
269
|
success: true,
|
|
157
|
-
message:
|
|
270
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} updated successfully`,
|
|
271
|
+
messageKey: `${entityName}.update.success`,
|
|
158
272
|
data
|
|
159
273
|
};
|
|
160
274
|
}
|
|
161
275
|
async updateMany(updateDtos, user) {
|
|
276
|
+
if (!this.isEnabled('updateMany')) {
|
|
277
|
+
throw new _exceptions.BaseAppException({
|
|
278
|
+
message: `Endpoint 'updateMany' is disabled`,
|
|
279
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
280
|
+
messageVariables: {
|
|
281
|
+
endpoint: 'updateMany'
|
|
282
|
+
},
|
|
283
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
284
|
+
});
|
|
285
|
+
}
|
|
162
286
|
const entities = await this.service.updateMany(updateDtos, user);
|
|
163
|
-
const data = (
|
|
287
|
+
const data = entities.map((item)=>toDto(responseDtoClass, item));
|
|
164
288
|
return {
|
|
165
289
|
success: true,
|
|
166
|
-
message: `${data.length}
|
|
290
|
+
message: `${data.length} ${entityName}s updated successfully`,
|
|
291
|
+
messageKey: `${entityName}.update.many.success`,
|
|
292
|
+
messageVariables: {
|
|
293
|
+
count: data.length
|
|
294
|
+
},
|
|
167
295
|
data,
|
|
168
296
|
meta: {
|
|
169
297
|
count: data.length,
|
|
@@ -172,15 +300,93 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
172
300
|
}
|
|
173
301
|
};
|
|
174
302
|
}
|
|
303
|
+
async bulkUpsert(dtos, user) {
|
|
304
|
+
if (!this.isEnabled('bulkUpsert')) {
|
|
305
|
+
throw new _exceptions.BaseAppException({
|
|
306
|
+
message: `Endpoint 'bulkUpsert' is disabled`,
|
|
307
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
308
|
+
messageVariables: {
|
|
309
|
+
endpoint: 'bulkUpsert'
|
|
310
|
+
},
|
|
311
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
const toInsert = [];
|
|
315
|
+
const toUpdate = [];
|
|
316
|
+
for (const dto of dtos){
|
|
317
|
+
if ('id' in dto && dto.id) {
|
|
318
|
+
toUpdate.push(dto);
|
|
319
|
+
} else {
|
|
320
|
+
toInsert.push(dto);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const allEntities = await this.service.bulkUpsert(toInsert, toUpdate, user);
|
|
324
|
+
const data = allEntities.map((item)=>toDto(responseDtoClass, item));
|
|
325
|
+
return {
|
|
326
|
+
success: true,
|
|
327
|
+
message: `${data.length} ${entityName}s upserted successfully`,
|
|
328
|
+
messageKey: `${entityName}.upsert.many.success`,
|
|
329
|
+
messageVariables: {
|
|
330
|
+
count: data.length
|
|
331
|
+
},
|
|
332
|
+
data,
|
|
333
|
+
meta: {
|
|
334
|
+
count: data.length,
|
|
335
|
+
total: dtos.length,
|
|
336
|
+
failed: dtos.length - data.length
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
async getByFilter(filter, user) {
|
|
341
|
+
if (!this.isEnabled('getByFilter')) {
|
|
342
|
+
throw new _exceptions.BaseAppException({
|
|
343
|
+
message: `Endpoint 'getByFilter' is disabled`,
|
|
344
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
345
|
+
messageVariables: {
|
|
346
|
+
endpoint: 'getByFilter'
|
|
347
|
+
},
|
|
348
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
const result = await this.service.getAll('', {
|
|
352
|
+
filter,
|
|
353
|
+
pagination: {
|
|
354
|
+
currentPage: 0,
|
|
355
|
+
pageSize: 1
|
|
356
|
+
}
|
|
357
|
+
}, user);
|
|
358
|
+
if (!result.data || result.data.length === 0) {
|
|
359
|
+
throw new _exceptions.NotFoundException(entityName);
|
|
360
|
+
}
|
|
361
|
+
const entity = result.data[0];
|
|
362
|
+
const data = toDto(responseDtoClass, entity);
|
|
363
|
+
return {
|
|
364
|
+
success: true,
|
|
365
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
|
|
366
|
+
messageKey: `${entityName}.get.by.filter.success`,
|
|
367
|
+
data
|
|
368
|
+
};
|
|
369
|
+
}
|
|
175
370
|
async getAll(filterAndPaginationDto, user, search) {
|
|
371
|
+
if (!this.isEnabled('getAll')) {
|
|
372
|
+
throw new _exceptions.BaseAppException({
|
|
373
|
+
message: `Endpoint 'getAll' is disabled`,
|
|
374
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
375
|
+
messageVariables: {
|
|
376
|
+
endpoint: 'getAll'
|
|
377
|
+
},
|
|
378
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
379
|
+
});
|
|
380
|
+
}
|
|
176
381
|
const result = await this.service.getAll(search ?? '', filterAndPaginationDto, user);
|
|
177
|
-
const data = (
|
|
382
|
+
const data = result.data.map((item)=>toDto(responseDtoClass, item));
|
|
178
383
|
const page = filterAndPaginationDto.pagination?.currentPage ?? 0;
|
|
179
384
|
const pageSize = filterAndPaginationDto.pagination?.pageSize ?? data.length;
|
|
180
385
|
const totalPages = pageSize > 0 ? Math.ceil(result.total / pageSize) : 1;
|
|
181
386
|
return {
|
|
182
387
|
success: true,
|
|
183
|
-
message:
|
|
388
|
+
message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)}s retrieved successfully`,
|
|
389
|
+
messageKey: `${entityName}.get.all.success`,
|
|
184
390
|
data,
|
|
185
391
|
meta: {
|
|
186
392
|
total: result.total,
|
|
@@ -193,21 +399,37 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
193
399
|
};
|
|
194
400
|
}
|
|
195
401
|
async delete(deleteDto, user) {
|
|
402
|
+
if (!this.isEnabled('delete')) {
|
|
403
|
+
throw new _exceptions.BaseAppException({
|
|
404
|
+
message: `Endpoint 'delete' is disabled`,
|
|
405
|
+
messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
|
|
406
|
+
messageVariables: {
|
|
407
|
+
endpoint: 'delete'
|
|
408
|
+
},
|
|
409
|
+
status: _common.HttpStatus.FORBIDDEN
|
|
410
|
+
});
|
|
411
|
+
}
|
|
196
412
|
await this.service.delete(deleteDto, user);
|
|
197
413
|
const count = Array.isArray(deleteDto.id) ? deleteDto.id.length : 1;
|
|
198
|
-
const action = deleteDto.type === 'restore' ? '
|
|
414
|
+
const action = deleteDto.type === 'restore' ? 'restore' : 'delete';
|
|
199
415
|
return {
|
|
200
416
|
success: true,
|
|
201
|
-
message: `${count}
|
|
417
|
+
message: `${count} ${entityName}${count > 1 ? 's' : ''} ${action}d successfully`,
|
|
418
|
+
messageKey: `${entityName}.${action}.success`,
|
|
419
|
+
messageVariables: {
|
|
420
|
+
count
|
|
421
|
+
}
|
|
202
422
|
};
|
|
203
423
|
}
|
|
204
424
|
constructor(service){
|
|
205
425
|
_define_property(this, "service", void 0);
|
|
426
|
+
_define_property(this, "enabledEndpoints", void 0);
|
|
206
427
|
this.service = service;
|
|
428
|
+
this.enabledEndpoints = options.enabledEndpoints ?? 'all';
|
|
207
429
|
}
|
|
208
430
|
};
|
|
209
431
|
_ts_decorate([
|
|
210
|
-
|
|
432
|
+
createEndpointDecorators(security.insert, 'insert', options.enabledEndpoints),
|
|
211
433
|
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
212
434
|
(0, _common.Post)('insert'),
|
|
213
435
|
(0, _common.HttpCode)(_common.HttpStatus.CREATED),
|
|
@@ -235,7 +457,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
235
457
|
_ts_metadata("design:returntype", Promise)
|
|
236
458
|
], ApiController.prototype, "insert", null);
|
|
237
459
|
_ts_decorate([
|
|
238
|
-
|
|
460
|
+
createEndpointDecorators(security.insertMany, 'insertMany', options.enabledEndpoints),
|
|
239
461
|
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
240
462
|
(0, _common.Post)('insert-many'),
|
|
241
463
|
(0, _common.HttpCode)(_common.HttpStatus.CREATED),
|
|
@@ -264,7 +486,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
264
486
|
_ts_metadata("design:returntype", Promise)
|
|
265
487
|
], ApiController.prototype, "insertMany", null);
|
|
266
488
|
_ts_decorate([
|
|
267
|
-
|
|
489
|
+
createEndpointDecorators(security.getById, 'getById', options.enabledEndpoints),
|
|
268
490
|
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
269
491
|
(0, _common.Post)('get/:id'),
|
|
270
492
|
(0, _common.HttpCode)(_common.HttpStatus.OK),
|
|
@@ -294,7 +516,29 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
294
516
|
_ts_metadata("design:returntype", Promise)
|
|
295
517
|
], ApiController.prototype, "getById", null);
|
|
296
518
|
_ts_decorate([
|
|
297
|
-
|
|
519
|
+
createEndpointDecorators(security.getByIds, 'getByIds', options.enabledEndpoints),
|
|
520
|
+
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
521
|
+
(0, _common.Post)('get-by-ids'),
|
|
522
|
+
(0, _common.HttpCode)(_common.HttpStatus.OK),
|
|
523
|
+
(0, _swagger.ApiOperation)({
|
|
524
|
+
summary: 'Get items by IDs',
|
|
525
|
+
description: 'Retrieves multiple items by their IDs. Optionally specify fields to select.'
|
|
526
|
+
}),
|
|
527
|
+
(0, _swagger.ApiBody)({
|
|
528
|
+
type: _dtos.GetByIdsDto
|
|
529
|
+
}),
|
|
530
|
+
(0, _apiresponsedecorator.ApiResponseDto)(responseDtoClass, true, 'list'),
|
|
531
|
+
_ts_param(0, (0, _common.Body)()),
|
|
532
|
+
_ts_param(1, (0, _decorators.CurrentUser)()),
|
|
533
|
+
_ts_metadata("design:type", Function),
|
|
534
|
+
_ts_metadata("design:paramtypes", [
|
|
535
|
+
typeof _dtos.GetByIdsDto === "undefined" ? Object : _dtos.GetByIdsDto,
|
|
536
|
+
Object
|
|
537
|
+
]),
|
|
538
|
+
_ts_metadata("design:returntype", Promise)
|
|
539
|
+
], ApiController.prototype, "getByIds", null);
|
|
540
|
+
_ts_decorate([
|
|
541
|
+
createEndpointDecorators(security.update, 'update', options.enabledEndpoints),
|
|
298
542
|
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
299
543
|
(0, _common.Post)('update'),
|
|
300
544
|
(0, _common.HttpCode)(_common.HttpStatus.OK),
|
|
@@ -317,7 +561,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
317
561
|
_ts_metadata("design:returntype", Promise)
|
|
318
562
|
], ApiController.prototype, "update", null);
|
|
319
563
|
_ts_decorate([
|
|
320
|
-
|
|
564
|
+
createEndpointDecorators(security.updateMany, 'updateMany', options.enabledEndpoints),
|
|
321
565
|
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
322
566
|
(0, _common.Post)('update-many'),
|
|
323
567
|
(0, _common.HttpCode)(_common.HttpStatus.OK),
|
|
@@ -341,22 +585,62 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
|
|
|
341
585
|
_ts_metadata("design:returntype", Promise)
|
|
342
586
|
], ApiController.prototype, "updateMany", null);
|
|
343
587
|
_ts_decorate([
|
|
344
|
-
|
|
588
|
+
createEndpointDecorators(security.bulkUpsert, 'bulkUpsert', options.enabledEndpoints),
|
|
589
|
+
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
590
|
+
(0, _common.Post)('bulk-upsert'),
|
|
591
|
+
(0, _common.HttpCode)(_common.HttpStatus.OK),
|
|
592
|
+
(0, _swagger.ApiOperation)({
|
|
593
|
+
summary: 'Create or update multiple items',
|
|
594
|
+
description: 'Bulk upsert: items without ID are created, items with ID are updated.'
|
|
595
|
+
}),
|
|
596
|
+
(0, _apiresponsedecorator.ApiResponseDto)(responseDtoClass, true, 'bulk'),
|
|
597
|
+
(0, _swagger.ApiBody)({
|
|
598
|
+
type: createDtoClass,
|
|
599
|
+
isArray: true
|
|
600
|
+
}),
|
|
601
|
+
(0, _common.UseInterceptors)(_interceptors.SetCreatedByOnBody, _interceptors.SetUpdateByOnBody, _interceptors.Slug),
|
|
602
|
+
_ts_param(0, (0, _common.Body)()),
|
|
603
|
+
_ts_param(1, (0, _decorators.CurrentUser)()),
|
|
604
|
+
_ts_metadata("design:type", Function),
|
|
605
|
+
_ts_metadata("design:paramtypes", [
|
|
606
|
+
Array,
|
|
607
|
+
Object
|
|
608
|
+
]),
|
|
609
|
+
_ts_metadata("design:returntype", Promise)
|
|
610
|
+
], ApiController.prototype, "bulkUpsert", null);
|
|
611
|
+
_ts_decorate([
|
|
612
|
+
createEndpointDecorators(security.getByFilter, 'getByFilter', options.enabledEndpoints),
|
|
613
|
+
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
614
|
+
(0, _common.Post)('get-by-filter'),
|
|
615
|
+
(0, _common.HttpCode)(_common.HttpStatus.OK),
|
|
616
|
+
(0, _swagger.ApiOperation)({
|
|
617
|
+
summary: 'Get single item by filter',
|
|
618
|
+
description: 'Retrieves a single item matching the provided filter criteria. Returns first match if multiple exist.'
|
|
619
|
+
}),
|
|
620
|
+
(0, _swagger.ApiBody)({
|
|
621
|
+
schema: {
|
|
622
|
+
type: 'object',
|
|
623
|
+
description: 'Filter criteria'
|
|
624
|
+
}
|
|
625
|
+
}),
|
|
626
|
+
(0, _apiresponsedecorator.ApiResponseDto)(responseDtoClass),
|
|
627
|
+
_ts_param(0, (0, _common.Body)()),
|
|
628
|
+
_ts_param(1, (0, _decorators.CurrentUser)()),
|
|
629
|
+
_ts_metadata("design:type", Function),
|
|
630
|
+
_ts_metadata("design:paramtypes", [
|
|
631
|
+
typeof Record === "undefined" ? Object : Record,
|
|
632
|
+
Object
|
|
633
|
+
]),
|
|
634
|
+
_ts_metadata("design:returntype", Promise)
|
|
635
|
+
], ApiController.prototype, "getByFilter", null);
|
|
636
|
+
_ts_decorate([
|
|
637
|
+
createEndpointDecorators(security.getAll, 'getAll', options.enabledEndpoints),
|
|
345
638
|
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
346
639
|
(0, _common.Post)('get-all'),
|
|
347
640
|
(0, _common.HttpCode)(_common.HttpStatus.OK),
|
|
348
641
|
(0, _swagger.ApiOperation)({
|
|
349
642
|
summary: 'Get all items with filters and pagination',
|
|
350
|
-
description:
|
|
351
|
-
Retrieves items with support for:
|
|
352
|
-
- **filter**: Apply field-based filters (e.g., \`{ "isActive": true }\`)
|
|
353
|
-
- **pagination**: Control page and page size
|
|
354
|
-
- **sort**: Order by any field (e.g., \`{ "createdAt": "DESC" }\`)
|
|
355
|
-
- **select**: Choose specific fields to return
|
|
356
|
-
- **withDeleted**: Include soft-deleted items
|
|
357
|
-
- **extraKey**: Include additional relations
|
|
358
|
-
- **q** (query param): Global text search
|
|
359
|
-
`
|
|
643
|
+
description: 'Supports filter, pagination, sort, select, withDeleted, and q (search) params'
|
|
360
644
|
}),
|
|
361
645
|
(0, _swagger.ApiQuery)({
|
|
362
646
|
name: 'q',
|
|
@@ -379,21 +663,13 @@ Retrieves items with support for:
|
|
|
379
663
|
_ts_metadata("design:returntype", Promise)
|
|
380
664
|
], ApiController.prototype, "getAll", null);
|
|
381
665
|
_ts_decorate([
|
|
382
|
-
|
|
666
|
+
createEndpointDecorators(security.delete, 'delete', options.enabledEndpoints),
|
|
383
667
|
(0, _common.Version)(_common.VERSION_NEUTRAL),
|
|
384
668
|
(0, _common.Post)('delete'),
|
|
385
669
|
(0, _common.HttpCode)(_common.HttpStatus.OK),
|
|
386
670
|
(0, _swagger.ApiOperation)({
|
|
387
671
|
summary: 'Delete, restore, or permanently remove items',
|
|
388
|
-
description:
|
|
389
|
-
Performs one of three actions:
|
|
390
|
-
|
|
391
|
-
- **"delete"** (soft delete): Marks items as deleted but keeps in database
|
|
392
|
-
- **"restore"**: Reverts soft-deleted items to active
|
|
393
|
-
- **"permanent"**: Completely removes items from database
|
|
394
|
-
|
|
395
|
-
Supports single ID or array of IDs for batch operations.
|
|
396
|
-
`
|
|
672
|
+
description: 'Types: delete (soft), restore, permanent. Supports batch IDs.'
|
|
397
673
|
}),
|
|
398
674
|
(0, _swagger.ApiResponse)({
|
|
399
675
|
status: 200,
|