rez_core 5.0.152 → 5.0.154

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.
Files changed (76) hide show
  1. package/dist/config/bull.config.js +4 -4
  2. package/dist/config/bull.config.js.map +1 -1
  3. package/dist/core.module.js +2 -5
  4. package/dist/core.module.js.map +1 -1
  5. package/dist/module/entity_json/entity_json.module.js +1 -1
  6. package/dist/module/entity_json/entity_json.module.js.map +1 -1
  7. package/dist/module/entity_json/service/entity_json.service.js +1 -1
  8. package/dist/module/entity_json/service/entity_json.service.js.map +1 -1
  9. package/dist/module/filter/controller/filter.controller.d.ts +0 -12
  10. package/dist/module/filter/controller/filter.controller.js +1 -1
  11. package/dist/module/filter/controller/filter.controller.js.map +1 -1
  12. package/dist/module/filter/entity/saved-filter-master.entity.d.ts +2 -1
  13. package/dist/module/filter/entity/saved-filter-master.entity.js +6 -2
  14. package/dist/module/filter/entity/saved-filter-master.entity.js.map +1 -1
  15. package/dist/module/filter/filter.module.js +2 -9
  16. package/dist/module/filter/filter.module.js.map +1 -1
  17. package/dist/module/filter/repository/saved-filter.repository.d.ts +1 -5
  18. package/dist/module/filter/repository/saved-filter.repository.js +1 -5
  19. package/dist/module/filter/repository/saved-filter.repository.js.map +1 -1
  20. package/dist/module/filter/service/filter.service.d.ts +1 -37
  21. package/dist/module/filter/service/filter.service.js +2 -30
  22. package/dist/module/filter/service/filter.service.js.map +1 -1
  23. package/dist/module/integration/service/wrapper.service.d.ts +3 -1
  24. package/dist/module/integration/service/wrapper.service.js +24 -2
  25. package/dist/module/integration/service/wrapper.service.js.map +1 -1
  26. package/dist/module/linked_attributes/controller/linked_attributes.controller.d.ts +0 -19
  27. package/dist/module/linked_attributes/controller/linked_attributes.controller.js +0 -77
  28. package/dist/module/linked_attributes/controller/linked_attributes.controller.js.map +1 -1
  29. package/dist/module/linked_attributes/linked_attributes.module.js +1 -8
  30. package/dist/module/linked_attributes/linked_attributes.module.js.map +1 -1
  31. package/dist/module/linked_attributes/service/linked_attributes.service.d.ts +1 -41
  32. package/dist/module/linked_attributes/service/linked_attributes.service.js +2 -266
  33. package/dist/module/linked_attributes/service/linked_attributes.service.js.map +1 -1
  34. package/dist/module/meta/dto/entity-table.dto.d.ts +1 -4
  35. package/dist/module/meta/dto/entity-table.dto.js.map +1 -1
  36. package/dist/module/meta/service/media-data.service.js +18 -5
  37. package/dist/module/meta/service/media-data.service.js.map +1 -1
  38. package/dist/module/workflow/service/action.service.js +2 -10
  39. package/dist/module/workflow/service/action.service.js.map +1 -1
  40. package/dist/table.config.d.ts +1 -3
  41. package/dist/table.config.js +0 -2
  42. package/dist/table.config.js.map +1 -1
  43. package/dist/tsconfig.build.tsbuildinfo +1 -1
  44. package/package.json +1 -1
  45. package/src/config/bull.config.ts +4 -4
  46. package/src/core.module.ts +2 -5
  47. package/src/module/entity_json/entity_json.module.ts +1 -1
  48. package/src/module/entity_json/service/entity_json.service.ts +1 -1
  49. package/src/module/filter/controller/filter.controller.ts +3 -1
  50. package/src/module/filter/entity/saved-filter-master.entity.ts +5 -2
  51. package/src/module/filter/filter.module.ts +2 -9
  52. package/src/module/filter/repository/saved-filter.repository.ts +9 -5
  53. package/src/module/filter/service/filter.service.ts +0 -49
  54. package/src/module/integration/service/wrapper.service.ts +37 -0
  55. package/src/module/linked_attributes/controller/linked_attributes.controller.ts +0 -86
  56. package/src/module/linked_attributes/linked_attributes.module.ts +2 -9
  57. package/src/module/linked_attributes/service/linked_attributes.service.ts +3 -548
  58. package/src/module/meta/dto/entity-table.dto.ts +3 -1
  59. package/src/module/meta/service/media-data.service.ts +27 -9
  60. package/src/module/workflow/service/action.service.ts +6 -10
  61. package/src/table.config.ts +0 -2
  62. package/dist/migrations/1732612800000-AddEntityJsonGinIndex.d.ts +0 -6
  63. package/dist/migrations/1732612800000-AddEntityJsonGinIndex.js +0 -32
  64. package/dist/migrations/1732612800000-AddEntityJsonGinIndex.js.map +0 -1
  65. package/dist/module/filter/service/flatjson-filter.service.d.ts +0 -30
  66. package/dist/module/filter/service/flatjson-filter.service.js +0 -615
  67. package/dist/module/filter/service/flatjson-filter.service.js.map +0 -1
  68. package/dist/module/linked_attributes/dto/create-linked-attribute-smart.dto.d.ts +0 -13
  69. package/dist/module/linked_attributes/dto/create-linked-attribute-smart.dto.js +0 -64
  70. package/dist/module/linked_attributes/dto/create-linked-attribute-smart.dto.js.map +0 -1
  71. package/src/migrations/1732612800000-AddEntityJsonGinIndex.ts +0 -41
  72. package/src/module/entity_json/docs/FlatJson_Filterin_System.md +0 -2804
  73. package/src/module/filter/service/flatjson-filter.service.ts +0 -888
  74. package/src/module/filter/test/flatjson-filter.service.spec.ts +0 -415
  75. package/src/module/linked_attributes/dto/create-linked-attribute-smart.dto.ts +0 -54
  76. package/src/module/linked_attributes/test/linked-attributes.service.spec.ts +0 -244
@@ -1,415 +0,0 @@
1
- import { Test, TestingModule } from '@nestjs/testing';
2
- import { FlatjsonFilterService } from '../service/flatjson-filter.service';
3
- import { EntityManager } from 'typeorm';
4
- import { EntityMasterService } from 'src/module/meta/service/entity-master.service';
5
- import { AttributeMasterService } from 'src/module/meta/service/attribute-master.service';
6
- import { ResolverService } from 'src/module/meta/service/resolver.service';
7
- import { LoggingService } from 'src/utils/service/loggingUtil.service';
8
- import { ConfigService } from '@nestjs/config';
9
- import { SavedFilterRepositoryService } from '../repository/saved-filter.repository';
10
-
11
- describe('FlatjsonFilterService - Phase 2 Tests', () => {
12
- let service: FlatjsonFilterService;
13
-
14
- const mockEntityManager = {
15
- getRepository: jest.fn(),
16
- };
17
-
18
- const mockEntityMasterService = {};
19
- const mockAttributeMasterService = {
20
- findAttributesByMappedEntityType: jest.fn(),
21
- };
22
- const mockResolverService = {
23
- resolveEntitiesByIds: jest.fn(),
24
- };
25
- const mockLoggingService = {
26
- log: jest.fn(),
27
- };
28
- const mockConfigService = {};
29
- const mockSavedFilterRepositoryService = {
30
- getDetailsByCode: jest.fn(),
31
- };
32
-
33
- beforeEach(async () => {
34
- const module: TestingModule = await Test.createTestingModule({
35
- providers: [
36
- FlatjsonFilterService,
37
- { provide: EntityManager, useValue: mockEntityManager },
38
- { provide: EntityMasterService, useValue: mockEntityMasterService },
39
- {
40
- provide: AttributeMasterService,
41
- useValue: mockAttributeMasterService,
42
- },
43
- { provide: ResolverService, useValue: mockResolverService },
44
- { provide: LoggingService, useValue: mockLoggingService },
45
- { provide: ConfigService, useValue: mockConfigService },
46
- {
47
- provide: SavedFilterRepositoryService,
48
- useValue: mockSavedFilterRepositoryService,
49
- },
50
- ],
51
- }).compile();
52
-
53
- service = module.get<FlatjsonFilterService>(FlatjsonFilterService);
54
- });
55
-
56
- afterEach(() => {
57
- jest.clearAllMocks();
58
- });
59
-
60
- describe('Task 2.3: Text Conditions', () => {
61
- it('should build equal condition for text', () => {
62
- const condition = service['buildTextCondition'](
63
- 'LEAD__name',
64
- 'equal',
65
- 'John',
66
- 'param1',
67
- );
68
-
69
- expect(condition).not.toBeNull();
70
- expect(condition?.query).toContain("json_data->>'LEAD__name'");
71
- expect(condition?.query).toContain('=');
72
- expect(condition?.params.param1).toBe('john'); // lowercase
73
- });
74
-
75
- it('should build contains condition with LIKE', () => {
76
- const condition = service['buildTextCondition'](
77
- 'LEAD__name',
78
- 'contains',
79
- 'John',
80
- 'param1',
81
- );
82
-
83
- expect(condition?.query).toContain('LIKE');
84
- expect(condition?.params.param1).toBe('%john%');
85
- });
86
-
87
- it('should build starts_with condition', () => {
88
- const condition = service['buildTextCondition'](
89
- 'LEAD__name',
90
- 'starts_with',
91
- 'John',
92
- 'param1',
93
- );
94
-
95
- expect(condition?.params.param1).toBe('john%');
96
- });
97
-
98
- it('should build ends_with condition', () => {
99
- const condition = service['buildTextCondition'](
100
- 'LEAD__name',
101
- 'ends_with',
102
- 'Smith',
103
- 'param1',
104
- );
105
-
106
- expect(condition?.params.param1).toBe('%smith');
107
- });
108
-
109
- it('should build empty condition', () => {
110
- const condition = service['buildTextCondition'](
111
- 'LEAD__name',
112
- 'empty',
113
- null,
114
- 'param1',
115
- );
116
-
117
- expect(condition?.query).toContain('IS NULL');
118
- expect(Object.keys(condition?.params || {})).toHaveLength(0);
119
- });
120
- });
121
-
122
- describe('Task 2.4: Number Conditions', () => {
123
- it('should build equal condition for numbers', () => {
124
- const condition = service['buildNumberCondition'](
125
- 'LEAD__age',
126
- 'equal',
127
- 25,
128
- 'param1',
129
- );
130
-
131
- expect(condition?.query).toContain('::numeric');
132
- expect(condition?.query).toContain('=');
133
- expect(condition?.params.param1).toBe(25);
134
- });
135
-
136
- it('should build greater_than condition', () => {
137
- const condition = service['buildNumberCondition'](
138
- 'LEAD__age',
139
- 'greater_than',
140
- 18,
141
- 'param1',
142
- );
143
-
144
- expect(condition?.query).toContain('>');
145
- expect(condition?.params.param1).toBe(18);
146
- });
147
-
148
- it('should build between condition with array', () => {
149
- const condition = service['buildNumberCondition'](
150
- 'LEAD__age',
151
- 'between',
152
- [18, 65],
153
- 'param1',
154
- );
155
-
156
- expect(condition?.query).toContain('BETWEEN');
157
- expect(condition?.params.param1_min).toBe(18);
158
- expect(condition?.params.param1_max).toBe(65);
159
- });
160
-
161
- it('should build between condition with string', () => {
162
- const condition = service['buildNumberCondition'](
163
- 'LEAD__age',
164
- 'between',
165
- '18, 65',
166
- 'param1',
167
- );
168
-
169
- expect(condition?.query).toContain('BETWEEN');
170
- expect(condition?.params.param1_min).toBe(18);
171
- expect(condition?.params.param1_max).toBe(65);
172
- });
173
- });
174
-
175
- describe('Task 2.5: Date Conditions', () => {
176
- it('should build equal condition for dates', () => {
177
- const condition = service['buildDateCondition'](
178
- 'LEAD__created_date',
179
- 'equal',
180
- '2024-01-01',
181
- 'param1',
182
- );
183
-
184
- expect(condition?.query).toContain('::bigint');
185
- expect(condition?.query).toContain('=');
186
- expect(typeof condition?.params.param1).toBe('number');
187
- });
188
-
189
- it('should build before condition', () => {
190
- const condition = service['buildDateCondition'](
191
- 'LEAD__created_date',
192
- 'before',
193
- '2024-12-31',
194
- 'param1',
195
- );
196
-
197
- expect(condition?.query).toContain('<');
198
- });
199
-
200
- it('should build between condition for dates', () => {
201
- const condition = service['buildDateCondition'](
202
- 'LEAD__created_date',
203
- 'between',
204
- ['2024-01-01', '2024-12-31'],
205
- 'param1',
206
- );
207
-
208
- expect(condition?.query).toContain('BETWEEN');
209
- expect(condition?.params.param1_start).toBeDefined();
210
- expect(condition?.params.param1_end).toBeDefined();
211
- });
212
-
213
- it('should build today condition', () => {
214
- const condition = service['buildDateCondition'](
215
- 'LEAD__created_date',
216
- 'today',
217
- null,
218
- 'param1',
219
- );
220
-
221
- expect(condition?.query).toContain('BETWEEN');
222
- expect(condition?.params.param1_start).toBeDefined();
223
- expect(condition?.params.param1_end).toBeDefined();
224
- });
225
-
226
- it('should build in_last_day condition', () => {
227
- const condition = service['buildDateCondition'](
228
- 'LEAD__created_date',
229
- 'in_last_day',
230
- 7,
231
- 'param1',
232
- );
233
-
234
- expect(condition?.query).toContain('BETWEEN');
235
- });
236
- });
237
-
238
- describe('Task 2.6: Select/Multiselect Conditions', () => {
239
- it('should build equal condition for select', () => {
240
- const condition = service['buildSelectCondition'](
241
- 'LEAD__status',
242
- 'equal',
243
- 'NEW',
244
- 'param1',
245
- );
246
-
247
- expect(condition?.query).toContain("json_data->>'LEAD__status'");
248
- expect(condition?.params.param1).toBe('NEW');
249
- });
250
-
251
- it('should build IN condition for array values', () => {
252
- const condition = service['buildSelectCondition'](
253
- 'LEAD__status',
254
- 'equal',
255
- ['NEW', 'ACTIVE'],
256
- 'param1',
257
- );
258
-
259
- expect(condition?.query).toContain('ANY');
260
- expect(Array.isArray(condition?.params.param1)).toBe(true);
261
- });
262
-
263
- it('should build contains condition for multiselect', () => {
264
- const condition = service['buildMultiSelectCondition'](
265
- 'LEAD__languages',
266
- 'contains',
267
- 'hindi',
268
- 'param1',
269
- );
270
-
271
- expect(condition?.query).toContain('?');
272
- expect(condition?.params.param1).toBe('hindi');
273
- });
274
-
275
- it('should build contains_all condition', () => {
276
- const condition = service['buildMultiSelectCondition'](
277
- 'LEAD__languages',
278
- 'contains_all',
279
- ['hindi', 'english'],
280
- 'param1',
281
- );
282
-
283
- expect(condition?.query).toContain('?&');
284
- expect(Array.isArray(condition?.params.param1)).toBe(true);
285
- });
286
-
287
- it('should build contains_any condition', () => {
288
- const condition = service['buildMultiSelectCondition'](
289
- 'LEAD__languages',
290
- 'contains_any',
291
- ['hindi', 'english'],
292
- 'param1',
293
- );
294
-
295
- expect(condition?.query).toContain('?|');
296
- });
297
-
298
- it('should handle comma-separated string input', () => {
299
- const condition = service['buildMultiSelectCondition'](
300
- 'LEAD__languages',
301
- 'contains_any',
302
- 'hindi, english, tamil',
303
- 'param1',
304
- );
305
-
306
- expect(Array.isArray(condition?.params.param1)).toBe(true);
307
- expect(condition?.params.param1).toHaveLength(3);
308
- });
309
- });
310
-
311
- describe('Task 2.9: Sorting', () => {
312
- it('should apply numeric sorting for number fields', () => {
313
- const mockQb = {
314
- orderBy: jest.fn().mockReturnThis(),
315
- addOrderBy: jest.fn().mockReturnThis(),
316
- };
317
-
318
- const attributeMetaMap = {
319
- age: {
320
- data_type: 'number',
321
- flat_json_key: 'LEAD__age',
322
- mapped_entity_type: 'LEAD',
323
- },
324
- };
325
-
326
- const sortby = [{ sortColum: 'age', sortType: 'DESC' }];
327
-
328
- service['applyJsonbSorting'](mockQb as any, sortby, attributeMetaMap);
329
-
330
- expect(mockQb.orderBy).toHaveBeenCalled();
331
- const call = mockQb.orderBy.mock.calls[0];
332
- expect(call[0]).toContain('::numeric');
333
- expect(call[1]).toBe('DESC');
334
- });
335
-
336
- it('should apply bigint sorting for date fields', () => {
337
- const mockQb = {
338
- orderBy: jest.fn().mockReturnThis(),
339
- };
340
-
341
- const attributeMetaMap = {
342
- created_date: {
343
- data_type: 'date',
344
- flat_json_key: 'LEAD__created_date',
345
- mapped_entity_type: 'LEAD',
346
- },
347
- };
348
-
349
- const sortby = [{ sortColum: 'created_date', sortType: 'ASC' }];
350
-
351
- service['applyJsonbSorting'](mockQb as any, sortby, attributeMetaMap);
352
-
353
- const call = mockQb.orderBy.mock.calls[0];
354
- expect(call[0]).toContain('::bigint');
355
- });
356
-
357
- it('should apply text sorting for text fields', () => {
358
- const mockQb = {
359
- orderBy: jest.fn().mockReturnThis(),
360
- };
361
-
362
- const attributeMetaMap = {
363
- name: {
364
- data_type: 'text',
365
- flat_json_key: 'LEAD__name',
366
- mapped_entity_type: 'LEAD',
367
- },
368
- };
369
-
370
- const sortby = [{ sortColum: 'name', sortType: 'ASC' }];
371
-
372
- service['applyJsonbSorting'](mockQb as any, sortby, attributeMetaMap);
373
-
374
- const call = mockQb.orderBy.mock.calls[0];
375
- expect(call[0]).not.toContain('::');
376
- expect(call[0]).toContain("->>"); // Text extraction
377
- });
378
- });
379
-
380
- describe('Edge Cases', () => {
381
- it('should handle NULL values in text conditions', () => {
382
- const condition = service['buildTextCondition'](
383
- 'LEAD__name',
384
- 'equal',
385
- null,
386
- 'param1',
387
- );
388
-
389
- expect(condition).not.toBeNull();
390
- expect(condition?.params.param1).toBe('');
391
- });
392
-
393
- it('should return null for unsupported operators', () => {
394
- const condition = service['buildTextCondition'](
395
- 'LEAD__name',
396
- 'invalid_operator',
397
- 'test',
398
- 'param1',
399
- );
400
-
401
- expect(condition).toBeNull();
402
- });
403
-
404
- it('should handle empty array in multiselect', () => {
405
- const condition = service['buildMultiSelectCondition'](
406
- 'LEAD__languages',
407
- 'contains',
408
- [],
409
- 'param1',
410
- );
411
-
412
- expect(condition?.query).toBe('1=1'); // Always true
413
- });
414
- });
415
- });
@@ -1,54 +0,0 @@
1
- import {
2
- IsString,
3
- IsNotEmpty,
4
- IsArray,
5
- IsOptional,
6
- IsBoolean,
7
- ValidateNested,
8
- } from 'class-validator';
9
- import { Type } from 'class-transformer';
10
-
11
- export class FilterConditionDto {
12
- @IsString()
13
- @IsNotEmpty()
14
- filter_attribute: string;
15
-
16
- @IsString()
17
- @IsNotEmpty()
18
- filter_operator: string;
19
-
20
- @IsNotEmpty()
21
- filter_value: any;
22
- }
23
-
24
- export class CreateLinkedAttributeSmartDto {
25
- @IsString()
26
- @IsNotEmpty()
27
- field_name: string;
28
-
29
- @IsString()
30
- @IsNotEmpty()
31
- mapped_entity_type: string;
32
-
33
- @IsString()
34
- @IsNotEmpty()
35
- applicable_entity_type: string;
36
-
37
- @IsString()
38
- @IsNotEmpty()
39
- applicable_attribute_key: string;
40
-
41
- @IsString()
42
- @IsNotEmpty()
43
- saved_filter_code: string;
44
-
45
- // @IsArray()
46
- // @IsOptional()
47
- // @ValidateNested({ each: true })
48
- // @Type(() => FilterConditionDto)
49
- // filter_conditions?: FilterConditionDto[];
50
-
51
- @IsBoolean()
52
- @IsOptional()
53
- backfill?: boolean;
54
- }
@@ -1,244 +0,0 @@
1
- import { Test, TestingModule } from '@nestjs/testing';
2
- import { LinkedAttributesService } from '../service/linked_attributes.service';
3
- import { DataSource } from 'typeorm';
4
- import { EntityMasterService } from 'src/module/meta/service/entity-master.service';
5
- import { AttributeMasterService } from 'src/module/meta/service/attribute-master.service';
6
- import { SavedFilterService } from 'src/module/filter/service/saved-filter.service';
7
- import { SavedFilterDetailRepository } from 'src/module/filter/repository/saved.filter-detail.repository';
8
- import { EntityJSONService } from 'src/module/entity_json/service/entity_json.service';
9
-
10
- describe('LinkedAttributesService - Phase 1 Tests', () => {
11
- let service: LinkedAttributesService;
12
- let dataSource: DataSource;
13
- let entityMasterService: EntityMasterService;
14
- let attributeMasterService: AttributeMasterService;
15
-
16
- const mockDataSource = {
17
- getRepository: jest.fn(),
18
- transaction: jest.fn(),
19
- };
20
-
21
- const mockEntityMasterService = {
22
- getEntityData: jest.fn(),
23
- };
24
-
25
- const mockAttributeMasterService = {
26
- findAttributesByMappedEntityType: jest.fn(),
27
- };
28
-
29
- const mockSavedFilterService = {
30
- createEntity: jest.fn(),
31
- };
32
-
33
- const mockSavedFilterDetailRepository = {
34
- findByMappedFilterCode: jest.fn(),
35
- };
36
-
37
- const mockEntityJSONService = {
38
- updateEntityJSON: jest.fn(),
39
- };
40
-
41
- const mockLoggedInUser = {
42
- id: 1,
43
- organization_id: 100,
44
- level_type: 'ORG',
45
- level_id: 100,
46
- };
47
-
48
- beforeEach(async () => {
49
- const module: TestingModule = await Test.createTestingModule({
50
- providers: [
51
- LinkedAttributesService,
52
- { provide: DataSource, useValue: mockDataSource },
53
- { provide: EntityMasterService, useValue: mockEntityMasterService },
54
- {
55
- provide: AttributeMasterService,
56
- useValue: mockAttributeMasterService,
57
- },
58
- { provide: 'SavedFilterService', useValue: mockSavedFilterService },
59
- {
60
- provide: SavedFilterDetailRepository,
61
- useValue: mockSavedFilterDetailRepository,
62
- },
63
- { provide: EntityJSONService, useValue: mockEntityJSONService },
64
- ],
65
- }).compile();
66
-
67
- service = module.get<LinkedAttributesService>(LinkedAttributesService);
68
- dataSource = module.get<DataSource>(DataSource);
69
- });
70
-
71
- afterEach(() => {
72
- jest.clearAllMocks();
73
- });
74
-
75
- describe('Task 1.1: generateAttributeKey', () => {
76
- it('should generate correct attribute key format', () => {
77
- const result = service.generateAttributeKey('LFMG', 'name', 1);
78
- expect(result).toBe('LFMG__name__1');
79
- });
80
-
81
- it('should handle different sequences', () => {
82
- expect(service.generateAttributeKey('LFMG', 'name', 1)).toBe(
83
- 'LFMG__name__1',
84
- );
85
- expect(service.generateAttributeKey('LFMG', 'name', 2)).toBe(
86
- 'LFMG__name__2',
87
- );
88
- expect(service.generateAttributeKey('LFMG', 'name', 99)).toBe(
89
- 'LFMG__name__99',
90
- );
91
- });
92
-
93
- it('should throw error for empty entity_type', () => {
94
- expect(() => service.generateAttributeKey('', 'name', 1)).toThrow(
95
- 'applicable_entity_type is required and cannot be empty',
96
- );
97
- });
98
-
99
- it('should throw error for empty attribute_key', () => {
100
- expect(() => service.generateAttributeKey('LFMG', '', 1)).toThrow(
101
- 'applicable_attribute_key is required and cannot be empty',
102
- );
103
- });
104
-
105
- it('should throw error for invalid sequence', () => {
106
- expect(() => service.generateAttributeKey('LFMG', 'name', 0)).toThrow(
107
- 'sequence must be a positive integer greater than 0',
108
- );
109
- expect(() => service.generateAttributeKey('LFMG', 'name', -1)).toThrow(
110
- 'sequence must be a positive integer greater than 0',
111
- );
112
- expect(() => service.generateAttributeKey('LFMG', 'name', 1.5)).toThrow(
113
- 'sequence must be a positive integer greater than 0',
114
- );
115
- });
116
-
117
- it('should trim whitespace from inputs', () => {
118
- expect(() =>
119
- service.generateAttributeKey(' ', 'name', 1),
120
- ).toThrow();
121
- expect(() =>
122
- service.generateAttributeKey('LFMG', ' ', 1),
123
- ).toThrow();
124
- });
125
- });
126
-
127
- describe('Task 1.2: Validation Engine', () => {
128
- it('should validate required fields', async () => {
129
- const payload = {
130
- field_name: '',
131
- mapped_entity_type: 'LEAD',
132
- applicable_entity_type: 'LFMG',
133
- applicable_attribute_key: 'name',
134
- organization_id: 100,
135
- };
136
-
137
- const result = await service.validateLinkedAttribute(
138
- payload,
139
- mockLoggedInUser,
140
- );
141
-
142
- expect(result.valid).toBe(false);
143
- expect(result.errors).toContain(
144
- "Required field 'field_name' is missing or empty",
145
- );
146
- });
147
-
148
- it('should validate entity type exists', async () => {
149
- mockEntityMasterService.getEntityData.mockRejectedValue(
150
- new Error('Not found'),
151
- );
152
-
153
- const payload = {
154
- field_name: 'Father Name',
155
- mapped_entity_type: 'INVALID',
156
- applicable_entity_type: 'LFMG',
157
- applicable_attribute_key: 'name',
158
- organization_id: 100,
159
- };
160
-
161
- const result = await service.validateLinkedAttribute(
162
- payload,
163
- mockLoggedInUser,
164
- );
165
-
166
- expect(result.valid).toBe(false);
167
- expect(
168
- result.errors.some((e) => e.includes('does not exist')),
169
- ).toBe(true);
170
- });
171
-
172
- it('should pass validation for valid payload', async () => {
173
- // Mock successful validations
174
- mockEntityMasterService.getEntityData
175
- .mockResolvedValueOnce({ id: 1 }) // mapped_entity_type
176
- .mockResolvedValueOnce({ id: 2 }); // applicable_entity_type
177
-
178
- mockAttributeMasterService.findAttributesByMappedEntityType.mockResolvedValue(
179
- [{ attribute_key: 'name' }],
180
- );
181
-
182
- const mockRepository = {
183
- createQueryBuilder: jest.fn().mockReturnValue({
184
- where: jest.fn().mockReturnThis(),
185
- andWhere: jest.fn().mockReturnThis(),
186
- getOne: jest.fn().mockResolvedValue(null), // No duplicate
187
- }),
188
- };
189
-
190
- mockDataSource.getRepository.mockReturnValue(mockRepository);
191
-
192
- const payload = {
193
- field_name: 'Father Name',
194
- mapped_entity_type: 'LEAD',
195
- applicable_entity_type: 'LFMG',
196
- applicable_attribute_key: 'name',
197
- organization_id: 100,
198
- };
199
-
200
- const result = await service.validateLinkedAttribute(
201
- payload,
202
- mockLoggedInUser,
203
- );
204
-
205
- expect(result.valid).toBe(true);
206
- expect(result.errors).toHaveLength(0);
207
- });
208
- });
209
-
210
- describe('Task 1.4: Smart Create Method', () => {
211
- it('should throw BadRequestException on validation failure', async () => {
212
- // Mock validation failure
213
- mockEntityMasterService.getEntityData.mockRejectedValue(
214
- new Error('Not found'),
215
- );
216
-
217
- const dto = {
218
- field_name: 'Father Name',
219
- mapped_entity_type: 'INVALID',
220
- applicable_entity_type: 'LFMG',
221
- applicable_attribute_key: 'name',
222
- };
223
-
224
- await expect(
225
- service.createLinkedAttributeSmart(dto, mockLoggedInUser),
226
- ).rejects.toThrow();
227
- });
228
- });
229
-
230
- describe('Integration Tests', () => {
231
- it('should handle end-to-end attribute key generation', () => {
232
- // Test the full flow: sequence 1, 2, 3
233
- expect(service.generateAttributeKey('LFMG', 'name', 1)).toBe(
234
- 'LFMG__name__1',
235
- );
236
- expect(service.generateAttributeKey('LFMG', 'email', 1)).toBe(
237
- 'LFMG__email__1',
238
- );
239
- expect(service.generateAttributeKey('CONTACT', 'phone', 1)).toBe(
240
- 'CONTACT__phone__1',
241
- );
242
- });
243
- });
244
- });