rez_core 5.0.145 → 5.0.150
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/config/bull.config.js +4 -4
- package/dist/config/bull.config.js.map +1 -1
- package/dist/migrations/1732612800000-AddEntityJsonGinIndex.d.ts +6 -0
- package/dist/migrations/1732612800000-AddEntityJsonGinIndex.js +32 -0
- package/dist/migrations/1732612800000-AddEntityJsonGinIndex.js.map +1 -0
- package/dist/module/entity_json/entity_json.module.js +1 -1
- package/dist/module/entity_json/entity_json.module.js.map +1 -1
- package/dist/module/entity_json/service/entity_json.service.js +1 -1
- package/dist/module/entity_json/service/entity_json.service.js.map +1 -1
- package/dist/module/filter/controller/filter.controller.d.ts +12 -0
- package/dist/module/filter/controller/filter.controller.js +1 -1
- package/dist/module/filter/controller/filter.controller.js.map +1 -1
- package/dist/module/filter/filter.module.js +9 -2
- package/dist/module/filter/filter.module.js.map +1 -1
- package/dist/module/filter/repository/saved-filter.repository.d.ts +5 -1
- package/dist/module/filter/repository/saved-filter.repository.js +5 -1
- package/dist/module/filter/repository/saved-filter.repository.js.map +1 -1
- package/dist/module/filter/service/filter.service.d.ts +37 -1
- package/dist/module/filter/service/filter.service.js +30 -2
- package/dist/module/filter/service/filter.service.js.map +1 -1
- package/dist/module/filter/service/flatjson-filter.service.d.ts +30 -0
- package/dist/module/filter/service/flatjson-filter.service.js +615 -0
- package/dist/module/filter/service/flatjson-filter.service.js.map +1 -0
- package/dist/module/integration/service/wrapper.service.d.ts +1 -3
- package/dist/module/integration/service/wrapper.service.js +2 -24
- package/dist/module/integration/service/wrapper.service.js.map +1 -1
- package/dist/module/linked_attributes/controller/linked_attributes.controller.d.ts +19 -0
- package/dist/module/linked_attributes/controller/linked_attributes.controller.js +77 -0
- package/dist/module/linked_attributes/controller/linked_attributes.controller.js.map +1 -1
- package/dist/module/linked_attributes/dto/create-linked-attribute-smart.dto.d.ts +13 -0
- package/dist/module/linked_attributes/dto/create-linked-attribute-smart.dto.js +64 -0
- package/dist/module/linked_attributes/dto/create-linked-attribute-smart.dto.js.map +1 -0
- package/dist/module/linked_attributes/linked_attributes.module.js +8 -1
- package/dist/module/linked_attributes/linked_attributes.module.js.map +1 -1
- package/dist/module/linked_attributes/service/linked_attributes.service.d.ts +41 -1
- package/dist/module/linked_attributes/service/linked_attributes.service.js +266 -2
- package/dist/module/linked_attributes/service/linked_attributes.service.js.map +1 -1
- package/dist/module/meta/dto/entity-table.dto.d.ts +4 -1
- package/dist/module/meta/dto/entity-table.dto.js.map +1 -1
- package/dist/module/meta/service/media-data.service.js +5 -18
- package/dist/module/meta/service/media-data.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config/bull.config.ts +4 -4
- package/src/migrations/1732612800000-AddEntityJsonGinIndex.ts +41 -0
- package/src/module/entity_json/docs/FlatJson_Filterin_System.md +2804 -0
- package/src/module/entity_json/entity_json.module.ts +1 -1
- package/src/module/entity_json/service/entity_json.service.ts +1 -1
- package/src/module/filter/controller/filter.controller.ts +1 -3
- package/src/module/filter/filter.module.ts +9 -2
- package/src/module/filter/repository/saved-filter.repository.ts +5 -8
- package/src/module/filter/service/filter.service.ts +49 -0
- package/src/module/filter/service/flatjson-filter.service.ts +888 -0
- package/src/module/filter/test/flatjson-filter.service.spec.ts +415 -0
- package/src/module/integration/service/wrapper.service.ts +0 -37
- package/src/module/linked_attributes/controller/linked_attributes.controller.ts +86 -0
- package/src/module/linked_attributes/dto/create-linked-attribute-smart.dto.ts +54 -0
- package/src/module/linked_attributes/linked_attributes.module.ts +9 -2
- package/src/module/linked_attributes/service/linked_attributes.service.ts +548 -3
- package/src/module/linked_attributes/test/linked-attributes.service.spec.ts +244 -0
- package/src/module/meta/dto/entity-table.dto.ts +1 -2
- package/src/module/meta/service/media-data.service.ts +9 -27
|
@@ -0,0 +1,244 @@
|
|
|
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
|
+
});
|
|
@@ -6,6 +6,5 @@ export class EntityTableDto extends EntityTable {
|
|
|
6
6
|
operation_list: {};
|
|
7
7
|
default_filter: {} | null;
|
|
8
8
|
saved_filter: { label: string; value: number }[] | null;
|
|
9
|
-
|
|
10
|
-
shared_filter: any[] | null;
|
|
9
|
+
shared_filter: { label: string; value: number }[] | null;
|
|
11
10
|
}
|
|
@@ -129,17 +129,6 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
129
129
|
jpg: 'image/jpeg',
|
|
130
130
|
jpeg: 'image/jpeg',
|
|
131
131
|
png: 'image/png',
|
|
132
|
-
gif: 'image/gif',
|
|
133
|
-
webp: 'image/webp',
|
|
134
|
-
txt: 'text/plain',
|
|
135
|
-
html: 'text/html',
|
|
136
|
-
htm: 'text/html',
|
|
137
|
-
doc: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
138
|
-
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
139
|
-
xls: 'application/vnd.ms-excel',
|
|
140
|
-
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
141
|
-
ppt: 'application/vnd.ms-powerpoint',
|
|
142
|
-
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
143
132
|
};
|
|
144
133
|
return map[ext] || 'application/octet-stream';
|
|
145
134
|
}
|
|
@@ -182,6 +171,7 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
182
171
|
}
|
|
183
172
|
}
|
|
184
173
|
|
|
174
|
+
// media-data.service.ts
|
|
185
175
|
async getMediaInlineUrl(id: number, loggedInUser, expiresIn?: number) {
|
|
186
176
|
try {
|
|
187
177
|
const entityData = await this.getEntityData(
|
|
@@ -191,13 +181,6 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
191
181
|
);
|
|
192
182
|
const mediaData = entityData as MediaData;
|
|
193
183
|
let fileSize: number | undefined;
|
|
194
|
-
|
|
195
|
-
if (!mediaData) {
|
|
196
|
-
throw new Error('Media not found');
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const ext = mediaData.file_name.split('.').pop()?.toLowerCase() || '';
|
|
200
|
-
|
|
201
184
|
if (this.bucketName) {
|
|
202
185
|
const head = await this.s3
|
|
203
186
|
.headObject({
|
|
@@ -208,23 +191,22 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
208
191
|
fileSize = head.ContentLength;
|
|
209
192
|
}
|
|
210
193
|
|
|
194
|
+
if (!mediaData) {
|
|
195
|
+
throw new Error('Media not found');
|
|
196
|
+
}
|
|
197
|
+
|
|
211
198
|
const signedUrl = await this.s3.getSignedUrlPromise('getObject', {
|
|
212
199
|
Bucket: this.bucketName,
|
|
213
200
|
Key: mediaData.media_url,
|
|
214
201
|
Expires: Number(expiresIn) || 60 * 5,
|
|
215
202
|
ResponseContentDisposition: 'inline',
|
|
216
|
-
ResponseContentType: this.getContentType(
|
|
203
|
+
ResponseContentType: this.getContentType(
|
|
204
|
+
mediaData.file_name.split('.').pop() || '',
|
|
205
|
+
),
|
|
217
206
|
});
|
|
218
207
|
|
|
219
|
-
let previewUrl = signedUrl;
|
|
220
|
-
// if (ext === 'doc' || ext === 'docx') {
|
|
221
|
-
// previewUrl =
|
|
222
|
-
// `https://view.officeapps.live.com/op/embed.aspx?src=` +
|
|
223
|
-
// encodeURIComponent(signedUrl);
|
|
224
|
-
// }
|
|
225
|
-
|
|
226
208
|
return {
|
|
227
|
-
signedUrl
|
|
209
|
+
signedUrl,
|
|
228
210
|
fileName: mediaData.file_name,
|
|
229
211
|
id: mediaData.id,
|
|
230
212
|
size: fileSize,
|