rez_core 5.0.295 → 5.0.296
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/meta/service/media-data.service.js +108 -38
- package/dist/module/meta/service/media-data.service.js.map +1 -1
- package/dist/module/workflow-automation/service/schedule-handler.service.d.ts +2 -2
- package/dist/module/workflow-automation/service/schedule-handler.service.js +18 -22
- package/dist/module/workflow-automation/service/schedule-handler.service.js.map +1 -1
- package/dist/module/workflow-schedule/interfaces/schedule-job-data.interface.d.ts +7 -8
- package/dist/module/workflow-schedule/processors/schedule.processor.d.ts +2 -0
- package/dist/module/workflow-schedule/processors/schedule.processor.js +115 -19
- package/dist/module/workflow-schedule/processors/schedule.processor.js.map +1 -1
- package/dist/module/workflow-schedule/service/workflow-schedule.service.js +14 -16
- package/dist/module/workflow-schedule/service/workflow-schedule.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/module/meta/service/media-data.service.ts +142 -40
- package/src/module/workflow-automation/service/schedule-handler.service.ts +19 -27
- package/src/module/workflow-schedule/interfaces/schedule-job-data.interface.ts +7 -8
- package/src/module/workflow-schedule/processors/schedule.processor.ts +156 -156
- package/src/module/workflow-schedule/service/workflow-schedule.service.ts +14 -16
- package/src/resources/dev.properties.yaml +0 -31
- package/src/resources/local.properties.yaml +0 -27
- package/src/resources/uat.properties.yaml +0 -31
- package/.idea/250218_ether_core.iml +0 -12
- package/.idea/codeStyles/Project.xml +0 -59
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
package/package.json
CHANGED
|
@@ -46,6 +46,24 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
46
46
|
if (!fileName || !mappedAttributeKey) {
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
// Validate AWS configuration
|
|
51
|
+
if (!this.s3AccessKeyID || !this.s3AccessKeySecret) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
'AWS credentials not configured. Please check AWS_ACCESS_KEY_ID and AWS_SECRET_KEY in configuration.',
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (!this.bucketName) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
'S3 bucket name not configured. Please check BUCKET_NAME in configuration.',
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (!this.s3Region) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
'AWS region not configured. Please check AWS_REGION in configuration.',
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
49
67
|
const ext = fileName.split('.').pop()?.toLowerCase() ?? '';
|
|
50
68
|
|
|
51
69
|
const id = uuidv4();
|
|
@@ -61,32 +79,45 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
61
79
|
|
|
62
80
|
const s3Key = `${s3Path}/${id}.${ext}`;
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
try {
|
|
83
|
+
const uploadUrl = await this.s3.getSignedUrlPromise('putObject', {
|
|
84
|
+
Bucket: this.bucketName,
|
|
85
|
+
Key: s3Key,
|
|
86
|
+
Expires: 60 * 5, // URL valid for 5 mins
|
|
87
|
+
ContentType: this.getContentType(ext),
|
|
88
|
+
});
|
|
70
89
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
const mediaData = new MediaData();
|
|
91
|
+
mediaData.file_name = fileName;
|
|
92
|
+
mediaData.mapped_attribute_key = mappedAttributeKey;
|
|
93
|
+
mediaData.status = STATUS_PENDING;
|
|
94
|
+
if (mappedEntityType) {
|
|
95
|
+
mediaData.mapped_entity_type = mappedEntityType;
|
|
96
|
+
}
|
|
97
|
+
if (mappedEntityId) {
|
|
98
|
+
mediaData.mapped_entity_id = mappedEntityId;
|
|
99
|
+
}
|
|
100
|
+
mediaData.media_url = s3Key;
|
|
82
101
|
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
//INSERT RECORD IN DOC TABLE
|
|
103
|
+
const savedEntity = await super.createEntity(mediaData, loggedInUser);
|
|
85
104
|
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
if (savedEntity) {
|
|
106
|
+
return { id: savedEntity.id, path: s3Key, uploadUrl };
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error('Error generating upload URL:', error);
|
|
111
|
+
console.error('Error details:', {
|
|
112
|
+
message: error.message,
|
|
113
|
+
code: error.code,
|
|
114
|
+
statusCode: error.statusCode,
|
|
115
|
+
bucketName: this.bucketName,
|
|
116
|
+
region: this.s3Region,
|
|
117
|
+
hasCredentials: !!(this.s3AccessKeyID && this.s3AccessKeySecret),
|
|
118
|
+
});
|
|
119
|
+
throw new Error(`Failed to generate upload URL: ${error.message}`);
|
|
88
120
|
}
|
|
89
|
-
return null;
|
|
90
121
|
}
|
|
91
122
|
|
|
92
123
|
async findByAttributeKeyAndMappedEntityIdAndMappedEntityType(
|
|
@@ -147,21 +178,51 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
147
178
|
// New method to get the signed URL for the media (download link)
|
|
148
179
|
async getMediaDownloadUrl(id: number, loggedInUser, expiresIn?: number) {
|
|
149
180
|
try {
|
|
181
|
+
// Validate AWS configuration
|
|
182
|
+
if (!this.s3AccessKeyID || !this.s3AccessKeySecret) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
'AWS credentials not configured. Please check AWS_ACCESS_KEY_ID and AWS_SECRET_KEY in configuration.',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (!this.bucketName) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
'S3 bucket name not configured. Please check BUCKET_NAME in configuration.',
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (!this.s3Region) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
'AWS region not configured. Please check AWS_REGION in configuration.',
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
150
198
|
const entityData = await this.getEntityData(
|
|
151
199
|
ENTITYTYPE_MEDIA,
|
|
152
200
|
id,
|
|
153
201
|
loggedInUser,
|
|
154
202
|
);
|
|
155
203
|
const mediaData = entityData as MediaData;
|
|
204
|
+
|
|
205
|
+
if (!mediaData || !mediaData.media_url) {
|
|
206
|
+
throw new Error(`Media data not found or invalid for id: ${id}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
156
209
|
let fileSize: number | undefined;
|
|
157
210
|
if (this.bucketName) {
|
|
158
|
-
|
|
159
|
-
.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
211
|
+
try {
|
|
212
|
+
const head = await this.s3
|
|
213
|
+
.headObject({
|
|
214
|
+
Bucket: this.bucketName,
|
|
215
|
+
Key: mediaData.media_url,
|
|
216
|
+
})
|
|
217
|
+
.promise();
|
|
218
|
+
fileSize = head.ContentLength;
|
|
219
|
+
} catch (headError) {
|
|
220
|
+
console.error(
|
|
221
|
+
`Error checking file in S3 bucket '${this.bucketName}' with key '${mediaData.media_url}':`,
|
|
222
|
+
headError.message,
|
|
223
|
+
);
|
|
224
|
+
// Continue even if headObject fails - the file might still be accessible
|
|
225
|
+
}
|
|
165
226
|
}
|
|
166
227
|
|
|
167
228
|
const signedUrl = await this.s3.getSignedUrlPromise('getObject', {
|
|
@@ -179,12 +240,37 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
179
240
|
};
|
|
180
241
|
} catch (error) {
|
|
181
242
|
console.error('Error generating signed URL for media:', error);
|
|
182
|
-
|
|
243
|
+
console.error('Error details:', {
|
|
244
|
+
message: error.message,
|
|
245
|
+
code: error.code,
|
|
246
|
+
statusCode: error.statusCode,
|
|
247
|
+
bucketName: this.bucketName,
|
|
248
|
+
region: this.s3Region,
|
|
249
|
+
hasCredentials: !!(this.s3AccessKeyID && this.s3AccessKeySecret),
|
|
250
|
+
});
|
|
251
|
+
throw new Error(`Failed to generate signed URL: ${error.message}`);
|
|
183
252
|
}
|
|
184
253
|
}
|
|
185
254
|
|
|
186
255
|
async getMediaInlineUrl(id: number, loggedInUser, expiresIn?: number) {
|
|
187
256
|
try {
|
|
257
|
+
// Validate AWS configuration
|
|
258
|
+
if (!this.s3AccessKeyID || !this.s3AccessKeySecret) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
'AWS credentials not configured. Please check AWS_ACCESS_KEY_ID and AWS_SECRET_KEY in configuration.',
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
if (!this.bucketName) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
'S3 bucket name not configured. Please check BUCKET_NAME in configuration.',
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
if (!this.s3Region) {
|
|
269
|
+
throw new Error(
|
|
270
|
+
'AWS region not configured. Please check AWS_REGION in configuration.',
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
188
274
|
const entityData = await this.getEntityData(
|
|
189
275
|
ENTITYTYPE_MEDIA,
|
|
190
276
|
id,
|
|
@@ -193,20 +279,28 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
193
279
|
const mediaData = entityData as MediaData;
|
|
194
280
|
let fileSize: number | undefined;
|
|
195
281
|
|
|
196
|
-
if (!mediaData) {
|
|
197
|
-
throw new Error(
|
|
282
|
+
if (!mediaData || !mediaData.media_url) {
|
|
283
|
+
throw new Error(`Media data not found or invalid for id: ${id}`);
|
|
198
284
|
}
|
|
199
285
|
|
|
200
286
|
const ext = mediaData.file_name.split('.').pop()?.toLowerCase() || '';
|
|
201
287
|
|
|
202
288
|
if (this.bucketName) {
|
|
203
|
-
|
|
204
|
-
.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
289
|
+
try {
|
|
290
|
+
const head = await this.s3
|
|
291
|
+
.headObject({
|
|
292
|
+
Bucket: this.bucketName,
|
|
293
|
+
Key: mediaData.media_url,
|
|
294
|
+
})
|
|
295
|
+
.promise();
|
|
296
|
+
fileSize = head.ContentLength;
|
|
297
|
+
} catch (headError) {
|
|
298
|
+
console.error(
|
|
299
|
+
`Error checking file in S3 bucket '${this.bucketName}' with key '${mediaData.media_url}':`,
|
|
300
|
+
headError.message,
|
|
301
|
+
);
|
|
302
|
+
// Continue even if headObject fails - the file might still be accessible
|
|
303
|
+
}
|
|
210
304
|
}
|
|
211
305
|
|
|
212
306
|
const signedUrl = await this.s3.getSignedUrlPromise('getObject', {
|
|
@@ -233,7 +327,15 @@ export class MediaDataService extends EntityServiceImpl {
|
|
|
233
327
|
};
|
|
234
328
|
} catch (error) {
|
|
235
329
|
console.error('Error generating inline URL:', error);
|
|
236
|
-
|
|
330
|
+
console.error('Error details:', {
|
|
331
|
+
message: error.message,
|
|
332
|
+
code: error.code,
|
|
333
|
+
statusCode: error.statusCode,
|
|
334
|
+
bucketName: this.bucketName,
|
|
335
|
+
region: this.s3Region,
|
|
336
|
+
hasCredentials: !!(this.s3AccessKeyID && this.s3AccessKeySecret),
|
|
337
|
+
});
|
|
338
|
+
throw new Error(`Failed to generate inline URL: ${error.message}`);
|
|
237
339
|
}
|
|
238
340
|
}
|
|
239
341
|
|
|
@@ -23,35 +23,35 @@ export class ScheduleHandlerService {
|
|
|
23
23
|
private readonly listMasterRepo: Repository<ListMasterData>,
|
|
24
24
|
) {}
|
|
25
25
|
|
|
26
|
-
async scheduleQueryBuilder(
|
|
26
|
+
async scheduleQueryBuilder(workflowId: number, jobData: any) {
|
|
27
27
|
// 1️⃣ Fetch workflow automation config
|
|
28
28
|
const workflow = await this.workflowAutomation.findOne({
|
|
29
29
|
where: {
|
|
30
|
-
id:
|
|
31
|
-
organization_id: jobData.
|
|
30
|
+
id: workflowId,
|
|
31
|
+
organization_id: jobData.organizationId,
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
if (!workflow) throw new Error(`Workflow with ID ${
|
|
35
|
+
if (!workflow) throw new Error(`Workflow with ID ${workflowId} not found`);
|
|
36
36
|
|
|
37
37
|
const scheduleJson =
|
|
38
38
|
typeof workflow.schedule === 'string'
|
|
39
39
|
? JSON.parse(workflow.schedule)
|
|
40
40
|
: workflow.schedule;
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
const entityType = workflow.applicable_entity_type;
|
|
43
43
|
|
|
44
44
|
// 2️⃣ Get table name from frm_entity_master
|
|
45
45
|
const entity = await this.entityMasterRepository.findOne({
|
|
46
46
|
where: {
|
|
47
47
|
mapped_entity_type: entityType,
|
|
48
|
-
organization_id: jobData.
|
|
48
|
+
organization_id: jobData.organizationId,
|
|
49
49
|
},
|
|
50
|
-
select: ['
|
|
50
|
+
select: ['data_source'],
|
|
51
51
|
});
|
|
52
|
-
if (!entity?.
|
|
52
|
+
if (!entity?.data_source) throw new Error(`Entity ${entityType} not found`);
|
|
53
53
|
|
|
54
|
-
const tableName = entity.
|
|
54
|
+
const tableName = entity.data_source;
|
|
55
55
|
|
|
56
56
|
// 3️⃣ Resolve executionDate (list master item name)
|
|
57
57
|
const executionDateItem = await this.listMasterRepo.findOne({
|
|
@@ -88,19 +88,11 @@ export class ScheduleHandlerService {
|
|
|
88
88
|
|
|
89
89
|
const qb = this.dataSource
|
|
90
90
|
.createQueryBuilder()
|
|
91
|
-
.from(tableName,
|
|
92
|
-
.
|
|
93
|
-
'frm_wf_stage_movement_data',
|
|
94
|
-
'sm',
|
|
95
|
-
`
|
|
96
|
-
sm.mapped_entity_id = e.id
|
|
97
|
-
AND sm.is_current = 'Y'
|
|
98
|
-
`,
|
|
99
|
-
)
|
|
100
|
-
.select('e.*')
|
|
91
|
+
.from(tableName, tableName)
|
|
92
|
+
.select('*')
|
|
101
93
|
.where(comparisonDateCondition)
|
|
102
|
-
.andWhere(
|
|
103
|
-
orgId: jobData.
|
|
94
|
+
.andWhere(`${tableName}.organization_id = :orgId`, {
|
|
95
|
+
orgId: jobData.organizationId,
|
|
104
96
|
});
|
|
105
97
|
|
|
106
98
|
this.logger.debug(`Executing Scheduler Query: ${qb.getSql()}`);
|
|
@@ -115,19 +107,19 @@ export class ScheduleHandlerService {
|
|
|
115
107
|
}
|
|
116
108
|
|
|
117
109
|
// ⚙️ Step 2: Handle scheduled automation (main entry point)
|
|
118
|
-
async handleScheduledWorkflow(
|
|
110
|
+
async handleScheduledWorkflow(workflowId: number, jobData: any) {
|
|
119
111
|
const { results, workflow, tableName } = await this.scheduleQueryBuilder(
|
|
120
|
-
|
|
112
|
+
workflowId,
|
|
121
113
|
jobData,
|
|
122
114
|
);
|
|
123
115
|
|
|
124
116
|
if (!results?.length) {
|
|
125
|
-
this.logger.warn(`No records found for scheduled workflow ${
|
|
117
|
+
this.logger.warn(`No records found for scheduled workflow ${workflowId}`);
|
|
126
118
|
return;
|
|
127
119
|
}
|
|
128
120
|
|
|
129
121
|
this.logger.log(
|
|
130
|
-
`Processing ${results.length} records for workflow ${
|
|
122
|
+
`Processing ${results.length} records for workflow ${workflowId}`,
|
|
131
123
|
);
|
|
132
124
|
|
|
133
125
|
// Parse the workflow event JSON again to get filter/action info
|
|
@@ -157,7 +149,7 @@ export class ScheduleHandlerService {
|
|
|
157
149
|
|
|
158
150
|
// 🧮 Step 3: Execute workflow actions for matched entities
|
|
159
151
|
if (!matchedEntities.length) {
|
|
160
|
-
this.logger.log(`No records passed criteria for workflow ${
|
|
152
|
+
this.logger.log(`No records passed criteria for workflow ${workflowId}`);
|
|
161
153
|
return;
|
|
162
154
|
}
|
|
163
155
|
|
|
@@ -167,7 +159,7 @@ export class ScheduleHandlerService {
|
|
|
167
159
|
|
|
168
160
|
for (const entity of matchedEntities) {
|
|
169
161
|
await this.workflowAutomationEngineService.executeActions(
|
|
170
|
-
|
|
162
|
+
workflowId,
|
|
171
163
|
entity,
|
|
172
164
|
jobData,
|
|
173
165
|
);
|
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
import { UserData } from "src/module/user/entity/user.entity";
|
|
6
6
|
|
|
7
7
|
export interface ScheduleJobData {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
level_type?: string;
|
|
8
|
+
scheduleId: number;
|
|
9
|
+
workflowId: number;
|
|
10
|
+
workflowName: string;
|
|
11
|
+
organizationId: number;
|
|
12
|
+
enterpriseId: number;
|
|
13
|
+
levelId?: string;
|
|
14
|
+
levelType?: string;
|
|
16
15
|
appcode?: string;
|
|
17
16
|
createdBy: number;
|
|
18
17
|
triggeredBy: 'SCHEDULE' | 'MANUAL';
|