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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "1.0.98",
3
+ "version": "1.1.0",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -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
- const refEntity = await this.entityMasterService.getEntityData(
233
- attr.datasource_list,
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
- row[attr.attribute_key] = refData[0].id;
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
- continue; // Skip this row
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' ,nullable: true })
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
  }
@@ -49,4 +49,7 @@ export class BaseEntity {
49
49
  @Column({ name: 'organization_id', type: 'int', nullable: true })
50
50
  @Expose()
51
51
  organization_id: number;
52
+ @Column({ name: 'appcode',type: 'varchar', length: 100, nullable: true })
53
+ @Expose()
54
+ appcode: string;
52
55
  }
@@ -141,7 +141,7 @@ protected readonly entityValidationService: EntityValidationService;
141
141
 
142
142
  return result;
143
143
  }
144
-
144
+
145
145
  async updateEntity(
146
146
  entityData: BaseEntity,
147
147
  loggedInUser: UserData | null,
@@ -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(`${db_table_name}.organization_id = :organization_id`, {
70
- organization_id: orgId,
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 = await this.attributeMasterService.findAttributesByMappedEntityType(entityData.entity_type);
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(entityData, attributes, entityData.entity_type,entityMaster.db_table_name);
101
-
102
- return [...requiredErrors, ...uniqueErrors];
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;