rez_core 1.0.98 → 1.1.0
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/dist/module/master/service/master.service.d.ts +7 -2
- package/dist/module/master/service/master.service.js +84 -15
- package/dist/module/master/service/master.service.js.map +1 -1
- package/dist/module/meta/entity/attribute-master.entity.d.ts +1 -0
- package/dist/module/meta/entity/attribute-master.entity.js +4 -0
- package/dist/module/meta/entity/attribute-master.entity.js.map +1 -1
- package/dist/module/meta/entity/base-entity.entity.d.ts +1 -0
- package/dist/module/meta/entity/base-entity.entity.js +5 -0
- package/dist/module/meta/entity/base-entity.entity.js.map +1 -1
- package/dist/module/meta/service/entity-validation.service.d.ts +2 -0
- package/dist/module/meta/service/entity-validation.service.js +27 -4
- package/dist/module/meta/service/entity-validation.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/module/master/service/master.service.ts +99 -22
- package/src/module/meta/entity/attribute-master.entity.ts +5 -2
- package/src/module/meta/entity/base-entity.entity.ts +3 -0
- package/src/module/meta/service/entity-service-impl.service.ts +1 -1
- package/src/module/meta/service/entity-validation.service.ts +69 -23
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
1
|
+
import { BadRequestException, Injectable } from '@nestjs/common';
|
|
2
2
|
import { Brackets, EntityManager } from 'typeorm';
|
|
3
3
|
import { ExcelUtil } from 'src/utils/service/excelUtil.service';
|
|
4
4
|
import { EntityMasterService } from '../../meta/service/entity-master.service';
|
|
@@ -6,6 +6,7 @@ import { AttributeMasterService } from 'src/module/meta/service/attribute-master
|
|
|
6
6
|
import { EntityMaster } from 'src/module/meta/entity/entity-master.entity';
|
|
7
7
|
import { EntityServiceImpl } from 'src/module/meta/service/entity-service-impl.service';
|
|
8
8
|
import { ReflectionHelper } from 'src/utils/service/reflection-helper.service';
|
|
9
|
+
import { EntityValidationService } from 'src/module/meta/service/entity-validation.service';
|
|
9
10
|
|
|
10
11
|
@Injectable()
|
|
11
12
|
export class MasterService {
|
|
@@ -17,6 +18,7 @@ export class MasterService {
|
|
|
17
18
|
private reflectionHelper: ReflectionHelper,
|
|
18
19
|
|
|
19
20
|
private readonly entityServiceImpl: EntityServiceImpl,
|
|
21
|
+
protected readonly entityValidationService: EntityValidationService,
|
|
20
22
|
) {}
|
|
21
23
|
|
|
22
24
|
private readonly metaSheets = [
|
|
@@ -197,6 +199,8 @@ export class MasterService {
|
|
|
197
199
|
loggedInUser,
|
|
198
200
|
duplicateHandling: 'skip_duplicates' | 'overwrite_items',
|
|
199
201
|
) {
|
|
202
|
+
const errors: { row: number; errors: any[] }[] = [];
|
|
203
|
+
|
|
200
204
|
const data = ExcelUtil.readExcel(file.buffer);
|
|
201
205
|
const entityMaster =
|
|
202
206
|
await this.entityMasterService.findByMappedEntityType(entityType);
|
|
@@ -214,6 +218,7 @@ export class MasterService {
|
|
|
214
218
|
await this.attributeMasterService.findAttributesByMappedEntityType(
|
|
215
219
|
entityType,
|
|
216
220
|
);
|
|
221
|
+
|
|
217
222
|
const uniqueFields = attributes
|
|
218
223
|
.filter((attr) => attr.is_unique)
|
|
219
224
|
.map((attr) => attr.attribute_key);
|
|
@@ -222,28 +227,71 @@ export class MasterService {
|
|
|
222
227
|
throw new Error(`No unique fields found for entityType: ${entityType}`);
|
|
223
228
|
}
|
|
224
229
|
|
|
225
|
-
for (const row of sheetData) {
|
|
230
|
+
for (const [i, row] of sheetData.entries()) {
|
|
226
231
|
if (row.parent_type && row.parent_id) {
|
|
227
232
|
await this.resolveParent(row);
|
|
228
233
|
}
|
|
229
234
|
|
|
230
235
|
for (const attr of attributes) {
|
|
231
236
|
if (attr.data_source_type === 'entity' && row[attr.attribute_key]) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const refData = await this.entityManager.query(
|
|
236
|
-
`SELECT * FROM ${refEntity.db_table_name} WHERE code = ? LIMIT 1`,
|
|
237
|
-
[row[attr.attribute_key]],
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
if (!refData.length) {
|
|
241
|
-
throw new Error(
|
|
242
|
-
`Reference entity not found for code: ${row[attr.attribute_key]}`,
|
|
237
|
+
try {
|
|
238
|
+
const refEntity = await this.entityMasterService.getEntityData(
|
|
239
|
+
attr.datasource_list,
|
|
243
240
|
);
|
|
244
|
-
}
|
|
245
241
|
|
|
246
|
-
|
|
242
|
+
const refData = await this.entityManager.query(
|
|
243
|
+
`SELECT * FROM ${refEntity.db_table_name} WHERE code = ? LIMIT 1`,
|
|
244
|
+
[row[attr.attribute_key]],
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (!refData.length) {
|
|
248
|
+
errors.push({
|
|
249
|
+
row: i + 1,
|
|
250
|
+
errors: [
|
|
251
|
+
{
|
|
252
|
+
field: attr.name,
|
|
253
|
+
message: `Reference entity not found for code: ${row[attr.attribute_key]}`,
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const existingError = errors.find((e) => e.row === i + 1);
|
|
261
|
+
|
|
262
|
+
if (refData[0].organization_id !== loggedInUser.organization_id) {
|
|
263
|
+
if (existingError) {
|
|
264
|
+
existingError.errors.push({
|
|
265
|
+
field: attr.name,
|
|
266
|
+
message: `Reference entity not found for code: ${row[attr.attribute_key]} does not belong to the organization`,
|
|
267
|
+
});
|
|
268
|
+
continue;
|
|
269
|
+
} else {
|
|
270
|
+
errors.push({
|
|
271
|
+
row: i + 1,
|
|
272
|
+
errors: [
|
|
273
|
+
{
|
|
274
|
+
field: attr.name,
|
|
275
|
+
message: `Reference entity with code ${row[attr.attribute_key]} does not belong to the organization`,
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
});
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
row[attr.attribute_key] = refData[0].id;
|
|
284
|
+
} catch (err) {
|
|
285
|
+
errors.push({
|
|
286
|
+
row: i + 1,
|
|
287
|
+
errors: [
|
|
288
|
+
{
|
|
289
|
+
field: attr.name,
|
|
290
|
+
message: `Error fetching reference entity for ${attr.attribute_key}`,
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
});
|
|
294
|
+
}
|
|
247
295
|
}
|
|
248
296
|
}
|
|
249
297
|
}
|
|
@@ -255,6 +303,7 @@ export class MasterService {
|
|
|
255
303
|
uniqueFields,
|
|
256
304
|
loggedInUser,
|
|
257
305
|
duplicateHandling,
|
|
306
|
+
errors,
|
|
258
307
|
);
|
|
259
308
|
|
|
260
309
|
return { message: 'Entity data uploaded successfully' };
|
|
@@ -345,6 +394,7 @@ export class MasterService {
|
|
|
345
394
|
uniqueFields: string[],
|
|
346
395
|
loggedInUser: any,
|
|
347
396
|
duplicateHandling: 'skip_duplicates' | 'overwrite_items',
|
|
397
|
+
errors: { row: number; errors: any[] }[],
|
|
348
398
|
): Promise<void> {
|
|
349
399
|
const entityMaster =
|
|
350
400
|
await this.entityMasterService.findByMappedEntityType(entityType);
|
|
@@ -357,6 +407,38 @@ export class MasterService {
|
|
|
357
407
|
if (!entityService)
|
|
358
408
|
throw new Error(`Entity service not found for ${entityType}`);
|
|
359
409
|
|
|
410
|
+
for (const [i, row] of data.entries()) {
|
|
411
|
+
row.entity_type = entityType;
|
|
412
|
+
row.organization_id = loggedInUser.organization_id;
|
|
413
|
+
|
|
414
|
+
const rowErrors =
|
|
415
|
+
await this.entityValidationService.validateExcelEntityData(
|
|
416
|
+
row,
|
|
417
|
+
entityMaster,
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const existingError = errors.find((e) => e.row === i + 1);
|
|
421
|
+
|
|
422
|
+
if (rowErrors?.length > 0) {
|
|
423
|
+
if (existingError) {
|
|
424
|
+
existingError.errors.push(...rowErrors);
|
|
425
|
+
} else {
|
|
426
|
+
errors.push({
|
|
427
|
+
row: i + 1,
|
|
428
|
+
errors: rowErrors,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (errors.length > 0) {
|
|
435
|
+
throw new BadRequestException({
|
|
436
|
+
status: false,
|
|
437
|
+
message: `Validation errors found in the uploaded data.`,
|
|
438
|
+
error: errors,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
360
442
|
for (const row of data) {
|
|
361
443
|
const qb = this.entityManager
|
|
362
444
|
.createQueryBuilder()
|
|
@@ -381,15 +463,10 @@ export class MasterService {
|
|
|
381
463
|
|
|
382
464
|
const existing = await qb.limit(1).getRawOne();
|
|
383
465
|
|
|
384
|
-
row.entity_type = entityType;
|
|
385
|
-
row.organization_id = loggedInUser.organization_id;
|
|
386
|
-
|
|
387
466
|
if (existing) {
|
|
388
|
-
if (duplicateHandling === 'skip_duplicates')
|
|
389
|
-
|
|
390
|
-
} else if (duplicateHandling === 'overwrite_items') {
|
|
467
|
+
if (duplicateHandling === 'skip_duplicates') continue;
|
|
468
|
+
if (duplicateHandling === 'overwrite_items') {
|
|
391
469
|
row.id = existing.id;
|
|
392
|
-
|
|
393
470
|
await entityService.updateEntity(row, loggedInUser);
|
|
394
471
|
}
|
|
395
472
|
} else {
|
|
@@ -6,7 +6,7 @@ export class AttributeMaster extends BaseEntity {
|
|
|
6
6
|
@Column({ name: 'mapped_entity_type', type: 'varchar', nullable: true })
|
|
7
7
|
mapped_entity_type: string;
|
|
8
8
|
|
|
9
|
-
@Column({ name: 'mapped_entity_id'
|
|
9
|
+
@Column({ name: 'mapped_entity_id', nullable: true })
|
|
10
10
|
mapped_entity_id: number;
|
|
11
11
|
|
|
12
12
|
@Column({ name: 'element_type', type: 'varchar', nullable: true, length: 50 })
|
|
@@ -62,9 +62,12 @@ export class AttributeMaster extends BaseEntity {
|
|
|
62
62
|
@Column({ name: 'is_unique', nullable: true })
|
|
63
63
|
is_unique: number;
|
|
64
64
|
|
|
65
|
+
@Column({ name: 'regex', nullable: true })
|
|
66
|
+
regex: string;
|
|
67
|
+
|
|
65
68
|
@Column({ name: 'can_export', nullable: true })
|
|
66
69
|
can_export: number;
|
|
67
70
|
|
|
68
71
|
@Column({ name: 'appcode', nullable: true })
|
|
69
|
-
appcode:string;
|
|
72
|
+
appcode: string;
|
|
70
73
|
}
|
|
@@ -14,7 +14,7 @@ export class EntityValidationService {
|
|
|
14
14
|
constructor(
|
|
15
15
|
private readonly attributeMasterService: AttributeMasterService,
|
|
16
16
|
private readonly reflectionHelper: ReflectionHelper,
|
|
17
|
-
private dataSource: DataSource
|
|
17
|
+
private dataSource: DataSource,
|
|
18
18
|
) {}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -27,8 +27,8 @@ export class EntityValidationService {
|
|
|
27
27
|
const errors: ValidationError[] = [];
|
|
28
28
|
|
|
29
29
|
attributeData
|
|
30
|
-
.filter(attr => attr.required)
|
|
31
|
-
.forEach(attr => {
|
|
30
|
+
.filter((attr) => attr.required)
|
|
31
|
+
.forEach((attr) => {
|
|
32
32
|
const value = entityData[attr.attribute_key];
|
|
33
33
|
if (!this.hasValidValue(value)) {
|
|
34
34
|
errors.push({
|
|
@@ -48,32 +48,34 @@ export class EntityValidationService {
|
|
|
48
48
|
entityData: Record<string, any>,
|
|
49
49
|
attributeData: AttributeMaster[],
|
|
50
50
|
entityType: string,
|
|
51
|
-
db_table_name: string
|
|
51
|
+
db_table_name: string,
|
|
52
52
|
): Promise<ValidationError[]> {
|
|
53
53
|
const errors: ValidationError[] = [];
|
|
54
|
-
|
|
55
|
-
for (const attr of attributeData.filter(a => a.is_unique)) {
|
|
54
|
+
|
|
55
|
+
for (const attr of attributeData.filter((a) => a.is_unique)) {
|
|
56
56
|
const value = entityData[attr.attribute_key];
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
if (this.hasValidValue(value)) {
|
|
59
59
|
let qb = this.dataSource
|
|
60
60
|
.createQueryBuilder()
|
|
61
61
|
.select('*')
|
|
62
62
|
.from(db_table_name, db_table_name)
|
|
63
63
|
.where(`${db_table_name}.${attr.attribute_key} = :value`, { value });
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
// Add AND condition for organization_id if present
|
|
66
66
|
const orgId = entityData.organization_id;
|
|
67
67
|
|
|
68
68
|
if (orgId !== undefined && orgId !== null) {
|
|
69
|
-
qb = qb.andWhere(
|
|
70
|
-
organization_id:
|
|
71
|
-
|
|
69
|
+
qb = qb.andWhere(
|
|
70
|
+
`${db_table_name}.organization_id = :organization_id`,
|
|
71
|
+
{
|
|
72
|
+
organization_id: orgId,
|
|
73
|
+
},
|
|
74
|
+
);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
|
|
75
77
|
const existing = await qb.limit(1).getRawOne();
|
|
76
|
-
|
|
78
|
+
|
|
77
79
|
if (existing) {
|
|
78
80
|
errors.push({
|
|
79
81
|
field: attr.name,
|
|
@@ -82,27 +84,71 @@ export class EntityValidationService {
|
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
|
-
|
|
87
|
+
|
|
88
|
+
return errors;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validates regex patterns for fields based on attribute metadata.
|
|
93
|
+
*/
|
|
94
|
+
async validateRegexFields(
|
|
95
|
+
entityData: Record<string, any>,
|
|
96
|
+
attributeData: AttributeMaster[],
|
|
97
|
+
): Promise<ValidationError[]> {
|
|
98
|
+
const errors: ValidationError[] = [];
|
|
99
|
+
|
|
100
|
+
for (const attr of attributeData.filter((a) => a.regex)) {
|
|
101
|
+
const value = entityData[attr.attribute_key];
|
|
102
|
+
if (this.hasValidValue(value)) {
|
|
103
|
+
const regex = new RegExp(attr.regex);
|
|
104
|
+
if (!regex.test(value)) {
|
|
105
|
+
errors.push({
|
|
106
|
+
field: attr.name,
|
|
107
|
+
message: `Field ${attr.name} does not match the required pattern.`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
86
113
|
return errors;
|
|
87
114
|
}
|
|
88
|
-
|
|
89
|
-
|
|
115
|
+
|
|
90
116
|
/**
|
|
91
117
|
* Validates both required and unique fields for a given entity type.
|
|
92
118
|
*/
|
|
93
119
|
async validateEntityData(
|
|
94
120
|
entityData: Record<string, any>,
|
|
95
|
-
entityMaster
|
|
121
|
+
entityMaster,
|
|
96
122
|
): Promise<ValidationError[]> {
|
|
97
|
-
const attributes =
|
|
98
|
-
|
|
123
|
+
const attributes =
|
|
124
|
+
await this.attributeMasterService.findAttributesByMappedEntityType(
|
|
125
|
+
entityData.entity_type,
|
|
126
|
+
);
|
|
127
|
+
|
|
99
128
|
const requiredErrors = this.validateRequiredFields(entityData, attributes);
|
|
100
|
-
const uniqueErrors = await this.validateUniqueFields(
|
|
101
|
-
|
|
102
|
-
|
|
129
|
+
const uniqueErrors = await this.validateUniqueFields(
|
|
130
|
+
entityData,
|
|
131
|
+
attributes,
|
|
132
|
+
entityData.entity_type,
|
|
133
|
+
entityMaster.db_table_name,
|
|
134
|
+
);
|
|
135
|
+
const regexErros = await this.validateRegexFields(entityData, attributes);
|
|
136
|
+
return [...requiredErrors, ...uniqueErrors, ...regexErros];
|
|
103
137
|
}
|
|
104
|
-
|
|
105
138
|
|
|
139
|
+
async validateExcelEntityData(
|
|
140
|
+
entityData: Record<string, any>,
|
|
141
|
+
entityMaster,
|
|
142
|
+
): Promise<ValidationError[]> {
|
|
143
|
+
const attributes =
|
|
144
|
+
await this.attributeMasterService.findAttributesByMappedEntityType(
|
|
145
|
+
entityData.entity_type,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const requiredErrors = this.validateRequiredFields(entityData, attributes);
|
|
149
|
+
const regexErros = await this.validateRegexFields(entityData, attributes);
|
|
150
|
+
return [...requiredErrors, ...regexErros];
|
|
151
|
+
}
|
|
106
152
|
private hasValidValue(value: any): boolean {
|
|
107
153
|
if (value === null || value === undefined) return false;
|
|
108
154
|
if (typeof value === 'string' && value.trim() === '') return false;
|