rez_core 4.0.184 → 4.0.187
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/app.module.js +11 -0
- package/dist/app.module.js.map +1 -1
- package/dist/config/bull.config.d.ts +10 -0
- package/dist/config/bull.config.js +66 -0
- package/dist/config/bull.config.js.map +1 -0
- package/dist/config/database.config.js +1 -1
- package/dist/config/database.config.js.map +1 -1
- package/dist/module/entity_json/controller/entity_json.controller.d.ts +8 -0
- package/dist/module/entity_json/controller/entity_json.controller.js +43 -0
- package/dist/module/entity_json/controller/entity_json.controller.js.map +1 -0
- package/dist/module/entity_json/entity_json.module.d.ts +2 -0
- package/dist/module/entity_json/entity_json.module.js +26 -0
- package/dist/module/entity_json/entity_json.module.js.map +1 -0
- package/dist/module/entity_json/service/entity_json.service.d.ts +7 -0
- package/dist/module/entity_json/service/entity_json.service.js +116 -0
- package/dist/module/entity_json/service/entity_json.service.js.map +1 -0
- package/dist/module/filter/service/filter.service.js.map +1 -1
- package/dist/module/integration/service/wrapper.service.d.ts +3 -1
- package/dist/module/integration/service/wrapper.service.js +39 -36
- package/dist/module/integration/service/wrapper.service.js.map +1 -1
- package/dist/module/linked_attributes/controller/linked_attributes.controller.d.ts +5 -0
- package/dist/module/linked_attributes/controller/linked_attributes.controller.js +29 -0
- package/dist/module/linked_attributes/controller/linked_attributes.controller.js.map +1 -0
- package/dist/module/linked_attributes/entity/linked_attribute.entity.d.ts +9 -0
- package/dist/module/linked_attributes/entity/linked_attribute.entity.js +70 -0
- package/dist/module/linked_attributes/entity/linked_attribute.entity.js.map +1 -0
- package/dist/module/linked_attributes/linked_attributes.module.d.ts +2 -0
- package/dist/module/linked_attributes/linked_attributes.module.js +29 -0
- package/dist/module/linked_attributes/linked_attributes.module.js.map +1 -0
- package/dist/module/linked_attributes/repository/linked_attribute.repository.d.ts +6 -0
- package/dist/module/linked_attributes/repository/linked_attribute.repository.js +31 -0
- package/dist/module/linked_attributes/repository/linked_attribute.repository.js.map +1 -0
- package/dist/module/linked_attributes/service/linked_attributes.service.d.ts +5 -0
- package/dist/module/linked_attributes/service/linked_attributes.service.js +34 -0
- package/dist/module/linked_attributes/service/linked_attributes.service.js.map +1 -0
- package/dist/module/mapper/service/field-mapper.service.js +2 -2
- package/dist/module/mapper/service/field-mapper.service.js.map +1 -1
- package/dist/module/meta/controller/attribute-master.controller.d.ts +10 -1
- package/dist/module/meta/controller/attribute-master.controller.js +27 -2
- package/dist/module/meta/controller/attribute-master.controller.js.map +1 -1
- package/dist/module/meta/controller/entity-master.controller.d.ts +6 -0
- package/dist/module/meta/controller/entity-master.controller.js +13 -0
- package/dist/module/meta/controller/entity-master.controller.js.map +1 -1
- package/dist/module/meta/entity.module.js +7 -0
- package/dist/module/meta/entity.module.js.map +1 -1
- package/dist/module/meta/repository/attribute-master.repository.d.ts +4 -0
- package/dist/module/meta/repository/attribute-master.repository.js +19 -0
- package/dist/module/meta/repository/attribute-master.repository.js.map +1 -1
- package/dist/module/meta/repository/entity-attribute-update.repository.d.ts +6 -0
- package/dist/module/meta/repository/entity-attribute-update.repository.js +44 -0
- package/dist/module/meta/repository/entity-attribute-update.repository.js.map +1 -0
- package/dist/module/meta/repository/entity-master.repository.d.ts +7 -1
- package/dist/module/meta/repository/entity-master.repository.js +24 -2
- package/dist/module/meta/repository/entity-master.repository.js.map +1 -1
- package/dist/module/meta/service/attribute-master.service.d.ts +4 -0
- package/dist/module/meta/service/attribute-master.service.js +3 -0
- package/dist/module/meta/service/attribute-master.service.js.map +1 -1
- package/dist/module/meta/service/entity-attribute-update.service.d.ts +7 -0
- package/dist/module/meta/service/entity-attribute-update.service.js +35 -0
- package/dist/module/meta/service/entity-attribute-update.service.js.map +1 -0
- package/dist/module/meta/service/entity-master.service.d.ts +4 -0
- package/dist/module/meta/service/entity-master.service.js +3 -0
- package/dist/module/meta/service/entity-master.service.js.map +1 -1
- package/dist/module/meta/service/resolver.service.js +2 -2
- package/dist/module/meta/service/resolver.service.js.map +1 -1
- package/dist/module/module/service/module-access.service.js +1 -7
- package/dist/module/module/service/module-access.service.js.map +1 -1
- package/dist/module/workflow/entity/action.entity.d.ts +1 -0
- package/dist/module/workflow/entity/action.entity.js +4 -0
- package/dist/module/workflow/entity/action.entity.js.map +1 -1
- package/dist/module/workflow/repository/action-data.repository.js +1 -0
- package/dist/module/workflow/repository/action-data.repository.js.map +1 -1
- package/dist/module/workflow/repository/task.repository.d.ts +3 -1
- package/dist/module/workflow/repository/task.repository.js +6 -1
- package/dist/module/workflow/repository/task.repository.js.map +1 -1
- package/dist/module/workflow/service/task.service.js +41 -27
- package/dist/module/workflow/service/task.service.js.map +1 -1
- package/dist/module/workflow/service/workflow-meta.service.js +23 -21
- package/dist/module/workflow/service/workflow-meta.service.js.map +1 -1
- package/dist/module/workflow-automation/controller/workflow-automation.controller.d.ts +6 -0
- package/dist/module/workflow-automation/controller/workflow-automation.controller.js +16 -0
- package/dist/module/workflow-automation/controller/workflow-automation.controller.js.map +1 -1
- package/dist/module/workflow-automation/entity/workflow-automation.entity.d.ts +2 -0
- package/dist/module/workflow-automation/entity/workflow-automation.entity.js +8 -0
- package/dist/module/workflow-automation/entity/workflow-automation.entity.js.map +1 -1
- package/dist/module/workflow-automation/service/schedule-handler.service.d.ts +16 -0
- package/dist/module/workflow-automation/service/schedule-handler.service.js +109 -0
- package/dist/module/workflow-automation/service/schedule-handler.service.js.map +1 -0
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.d.ts +1 -1
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.js.map +1 -1
- package/dist/module/workflow-automation/service/workflow-automation.service.d.ts +13 -1
- package/dist/module/workflow-automation/service/workflow-automation.service.js +111 -2
- package/dist/module/workflow-automation/service/workflow-automation.service.js.map +1 -1
- package/dist/module/workflow-automation/workflow-automation.module.js +14 -1
- package/dist/module/workflow-automation/workflow-automation.module.js.map +1 -1
- package/dist/module/workflow-schedule/constants/schedule.constants.d.ts +27 -0
- package/dist/module/workflow-schedule/constants/schedule.constants.js +31 -0
- package/dist/module/workflow-schedule/constants/schedule.constants.js.map +1 -0
- package/dist/module/workflow-schedule/controller/workflow-schedule.controller.d.ts +83 -0
- package/dist/module/workflow-schedule/controller/workflow-schedule.controller.js +220 -0
- package/dist/module/workflow-schedule/controller/workflow-schedule.controller.js.map +1 -0
- package/dist/module/workflow-schedule/dto/create-schedule.dto.d.ts +32 -0
- package/dist/module/workflow-schedule/dto/create-schedule.dto.js +163 -0
- package/dist/module/workflow-schedule/dto/create-schedule.dto.js.map +1 -0
- package/dist/module/workflow-schedule/dto/get-execution-logs.dto.d.ts +35 -0
- package/dist/module/workflow-schedule/dto/get-execution-logs.dto.js +124 -0
- package/dist/module/workflow-schedule/dto/get-execution-logs.dto.js.map +1 -0
- package/dist/module/workflow-schedule/dto/update-schedule.dto.d.ts +19 -0
- package/dist/module/workflow-schedule/dto/update-schedule.dto.js +106 -0
- package/dist/module/workflow-schedule/dto/update-schedule.dto.js.map +1 -0
- package/dist/module/workflow-schedule/entities/scheduled-workflow.entity.d.ts +30 -0
- package/dist/module/workflow-schedule/entities/scheduled-workflow.entity.js +113 -0
- package/dist/module/workflow-schedule/entities/scheduled-workflow.entity.js.map +1 -0
- package/dist/module/workflow-schedule/entities/workflow-execution-log.entity.d.ts +34 -0
- package/dist/module/workflow-schedule/entities/workflow-execution-log.entity.js +118 -0
- package/dist/module/workflow-schedule/entities/workflow-execution-log.entity.js.map +1 -0
- package/dist/module/workflow-schedule/interfaces/schedule-job-data.interface.d.ts +44 -0
- package/dist/module/workflow-schedule/interfaces/schedule-job-data.interface.js +3 -0
- package/dist/module/workflow-schedule/interfaces/schedule-job-data.interface.js.map +1 -0
- package/dist/module/workflow-schedule/interfaces/workflow-schedule-options.interface.d.ts +3 -0
- package/dist/module/workflow-schedule/interfaces/workflow-schedule-options.interface.js +3 -0
- package/dist/module/workflow-schedule/interfaces/workflow-schedule-options.interface.js.map +1 -0
- package/dist/module/workflow-schedule/processors/schedule.processor.d.ts +31 -0
- package/dist/module/workflow-schedule/processors/schedule.processor.js +409 -0
- package/dist/module/workflow-schedule/processors/schedule.processor.js.map +1 -0
- package/dist/module/workflow-schedule/service/workflow-schedule.service.d.ts +44 -0
- package/dist/module/workflow-schedule/service/workflow-schedule.service.js +434 -0
- package/dist/module/workflow-schedule/service/workflow-schedule.service.js.map +1 -0
- package/dist/module/workflow-schedule/workflow-schedule.module.d.ts +5 -0
- package/dist/module/workflow-schedule/workflow-schedule.module.js +52 -0
- package/dist/module/workflow-schedule/workflow-schedule.module.js.map +1 -0
- package/dist/table.config.d.ts +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/service/loggingUtil.service.d.ts +9 -2
- package/dist/utils/service/loggingUtil.service.js +65 -14
- package/dist/utils/service/loggingUtil.service.js.map +1 -1
- package/dist/utils/utils.module.js +2 -0
- package/dist/utils/utils.module.js.map +1 -1
- package/package.json +9 -4
- package/src/app.module.ts +16 -2
- package/src/config/bull.config.ts +69 -0
- package/src/config/database.config.ts +1 -1
- package/src/module/entity_json/controller/entity_json.controller.ts +47 -0
- package/src/module/entity_json/entity_json.module.ts +13 -0
- package/src/module/entity_json/service/entity_json.service.ts +162 -0
- package/src/module/filter/service/filter.service.ts +0 -2
- package/src/module/integration/service/wrapper.service.ts +248 -41
- package/src/module/linked_attributes/controller/linked_attributes.controller.ts +10 -0
- package/src/module/linked_attributes/entity/linked_attribute.entity.ts +48 -0
- package/src/module/linked_attributes/linked_attributes.module.ts +16 -0
- package/src/module/linked_attributes/repository/linked_attribute.repository.ts +12 -0
- package/src/module/linked_attributes/service/linked_attributes.service.ts +22 -0
- package/src/module/mapper/service/field-mapper.service.ts +12 -7
- package/src/module/meta/controller/attribute-master.controller.ts +20 -0
- package/src/module/meta/controller/entity-master.controller.ts +13 -0
- package/src/module/meta/entity.module.ts +8 -0
- package/src/module/meta/repository/attribute-master.repository.ts +20 -0
- package/src/module/meta/repository/entity-attribute-update.repository.ts +44 -0
- package/src/module/meta/repository/entity-master.repository.ts +30 -0
- package/src/module/meta/service/attribute-master.service.ts +4 -0
- package/src/module/meta/service/entity-attribute-update.service.ts +29 -0
- package/src/module/meta/service/entity-master.service.ts +6 -0
- package/src/module/meta/service/resolver.service.ts +2 -2
- package/src/module/module/service/module-access.service.ts +3 -16
- package/src/module/workflow/entity/action.entity.ts +3 -0
- package/src/module/workflow/repository/action-data.repository.ts +1 -0
- package/src/module/workflow/repository/task.repository.ts +8 -0
- package/src/module/workflow/service/task.service.ts +47 -38
- package/src/module/workflow/service/workflow-meta.service.ts +47 -33
- package/src/module/workflow-automation/SCHEDULING_GUIDE.md +145 -0
- package/src/module/workflow-automation/controller/workflow-automation.controller.ts +22 -0
- package/src/module/workflow-automation/entity/workflow-automation.entity.ts +5 -0
- package/src/module/workflow-automation/service/schedule-handler.service.ts +149 -0
- package/src/module/workflow-automation/service/workflow-automation-engine.service.ts +1 -1
- package/src/module/workflow-automation/service/workflow-automation.service.ts +163 -2
- package/src/module/workflow-automation/workflow-automation.module.ts +14 -1
- package/src/module/workflow-schedule/INSTALLATION.md +244 -0
- package/src/module/workflow-schedule/MULTI_PROJECT_GUIDE.md +196 -0
- package/src/module/workflow-schedule/README.md +422 -0
- package/src/module/workflow-schedule/constants/schedule.constants.ts +48 -0
- package/src/module/workflow-schedule/controller/workflow-schedule.controller.ts +255 -0
- package/src/module/workflow-schedule/docs/CLAUDE_CODE_GUIDE.md +510 -0
- package/src/module/workflow-schedule/docs/CLAUDE_CODE_PROMPT.md +362 -0
- package/src/module/workflow-schedule/docs/RUN_CLAUDE_CODE.sh +68 -0
- package/src/module/workflow-schedule/dto/create-schedule.dto.ts +147 -0
- package/src/module/workflow-schedule/dto/get-execution-logs.dto.ts +119 -0
- package/src/module/workflow-schedule/dto/update-schedule.dto.ts +96 -0
- package/src/module/workflow-schedule/entities/scheduled-workflow.entity.ts +148 -0
- package/src/module/workflow-schedule/entities/workflow-execution-log.entity.ts +154 -0
- package/src/module/workflow-schedule/interfaces/schedule-job-data.interface.ts +53 -0
- package/src/module/workflow-schedule/interfaces/workflow-schedule-options.interface.ts +12 -0
- package/src/module/workflow-schedule/processors/schedule.processor.ts +584 -0
- package/src/module/workflow-schedule/service/workflow-schedule.service.ts +600 -0
- package/src/module/workflow-schedule/workflow-schedule.module.ts +67 -0
- package/src/resources/dev.properties.yaml +1 -1
- package/src/utils/service/loggingUtil.service.ts +70 -16
- package/src/utils/utils.module.ts +2 -0
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
|
|
2
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
3
|
+
import { Repository, DataSource, Between, In } from 'typeorm';
|
|
4
|
+
import { InjectQueue } from '@nestjs/bull';
|
|
5
|
+
import { Queue } from 'bull';
|
|
6
|
+
import * as cronParser from 'cron-parser';
|
|
7
|
+
|
|
8
|
+
import { ScheduledWorkflow } from '../entities/scheduled-workflow.entity';
|
|
9
|
+
import { WorkflowExecutionLog } from '../entities/workflow-execution-log.entity';
|
|
10
|
+
import { CreateScheduleDto } from '../dto/create-schedule.dto';
|
|
11
|
+
import { UpdateScheduleDto } from '../dto/update-schedule.dto';
|
|
12
|
+
import { GetExecutionLogsDto } from '../dto/get-execution-logs.dto';
|
|
13
|
+
import { UserData } from '../../user/entity/user.entity';
|
|
14
|
+
import {
|
|
15
|
+
WORKFLOW_SCHEDULE_QUEUE,
|
|
16
|
+
EXECUTE_SCHEDULED_WORKFLOW_JOB,
|
|
17
|
+
SCHEDULE_STATUS_ACTIVE,
|
|
18
|
+
SCHEDULE_STATUS_PAUSED,
|
|
19
|
+
SCHEDULE_STATUS_INACTIVE,
|
|
20
|
+
DEFAULT_MAX_RETRIES,
|
|
21
|
+
DEFAULT_RETRY_DELAY,
|
|
22
|
+
DEFAULT_BACKOFF_MULTIPLIER,
|
|
23
|
+
DEFAULT_TIMEZONE,
|
|
24
|
+
} from '../constants/schedule.constants';
|
|
25
|
+
import { ScheduleJobData } from '../interfaces/schedule-job-data.interface';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Workflow Schedule Service
|
|
29
|
+
* Manages scheduled workflows and their execution
|
|
30
|
+
*/
|
|
31
|
+
@Injectable()
|
|
32
|
+
export class WorkflowScheduleService {
|
|
33
|
+
private readonly logger = new Logger(WorkflowScheduleService.name);
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
@InjectRepository(ScheduledWorkflow)
|
|
37
|
+
private readonly scheduledWorkflowRepository: Repository<ScheduledWorkflow>,
|
|
38
|
+
@InjectRepository(WorkflowExecutionLog)
|
|
39
|
+
private readonly executionLogRepository: Repository<WorkflowExecutionLog>,
|
|
40
|
+
@InjectQueue(WORKFLOW_SCHEDULE_QUEUE)
|
|
41
|
+
private readonly scheduleQueue: Queue<ScheduleJobData>,
|
|
42
|
+
private readonly dataSource: DataSource,
|
|
43
|
+
) {}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 1. Create a new scheduled workflow
|
|
47
|
+
*/
|
|
48
|
+
async createSchedule(
|
|
49
|
+
createScheduleDto: CreateScheduleDto,
|
|
50
|
+
loggedInUser: UserData,
|
|
51
|
+
): Promise<ScheduledWorkflow> {
|
|
52
|
+
this.logger.log(
|
|
53
|
+
`Creating schedule for workflow ${createScheduleDto.workflow_id} by user ${loggedInUser.id}`,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// Validate cron expression
|
|
58
|
+
this.validateCronExpression(createScheduleDto.cron_expression, createScheduleDto.timezone);
|
|
59
|
+
|
|
60
|
+
// Calculate next execution time
|
|
61
|
+
const nextExecutionAt = this.calculateNextExecution(
|
|
62
|
+
createScheduleDto.cron_expression,
|
|
63
|
+
createScheduleDto.timezone || DEFAULT_TIMEZONE,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Create scheduled workflow entity
|
|
67
|
+
const schedule = this.scheduledWorkflowRepository.create({
|
|
68
|
+
workflow_id: createScheduleDto.workflow_id,
|
|
69
|
+
workflow_name: createScheduleDto.workflow_name,
|
|
70
|
+
name: createScheduleDto.name,
|
|
71
|
+
description: createScheduleDto.description,
|
|
72
|
+
cron_expression: createScheduleDto.cron_expression,
|
|
73
|
+
timezone: createScheduleDto.timezone || DEFAULT_TIMEZONE,
|
|
74
|
+
start_date: createScheduleDto.start_date ? new Date(createScheduleDto.start_date) : null,
|
|
75
|
+
end_date: createScheduleDto.end_date ? new Date(createScheduleDto.end_date) : null,
|
|
76
|
+
max_executions: createScheduleDto.max_executions,
|
|
77
|
+
execution_count: 0,
|
|
78
|
+
next_execution_at: nextExecutionAt,
|
|
79
|
+
schedule_status: SCHEDULE_STATUS_ACTIVE,
|
|
80
|
+
retry_config: createScheduleDto.retry_config || {
|
|
81
|
+
maxRetries: DEFAULT_MAX_RETRIES,
|
|
82
|
+
retryDelay: DEFAULT_RETRY_DELAY,
|
|
83
|
+
backoffMultiplier: DEFAULT_BACKOFF_MULTIPLIER,
|
|
84
|
+
},
|
|
85
|
+
metadata: createScheduleDto.metadata || {},
|
|
86
|
+
is_enabled: createScheduleDto.is_enabled !== false,
|
|
87
|
+
organization_id: createScheduleDto.organization_id || loggedInUser.organization_id,
|
|
88
|
+
enterprise_id: createScheduleDto.enterprise_id || loggedInUser.enterprise_id,
|
|
89
|
+
level_id: createScheduleDto.level_id || loggedInUser.level_id,
|
|
90
|
+
level_type: createScheduleDto.level_type || loggedInUser.level_type,
|
|
91
|
+
appcode: createScheduleDto.appcode || loggedInUser.appcode,
|
|
92
|
+
created_by: loggedInUser.id,
|
|
93
|
+
created_date: new Date(),
|
|
94
|
+
status: createScheduleDto.status || 'ACTIVE',
|
|
95
|
+
entity_type: 'WFSC',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const savedSchedule = await this.scheduledWorkflowRepository.save(schedule);
|
|
99
|
+
|
|
100
|
+
// Add job to Bull queue with cron schedule
|
|
101
|
+
if (savedSchedule.is_enabled && savedSchedule.schedule_status === SCHEDULE_STATUS_ACTIVE) {
|
|
102
|
+
await this.scheduleJob(savedSchedule, loggedInUser);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.logger.log(`Schedule created successfully with ID: ${savedSchedule.id}`);
|
|
106
|
+
return savedSchedule;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
this.logger.error(`Failed to create schedule: ${error.message}`, error.stack);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 2. Update an existing scheduled workflow
|
|
115
|
+
*/
|
|
116
|
+
async updateSchedule(
|
|
117
|
+
updateScheduleDto: UpdateScheduleDto,
|
|
118
|
+
loggedInUser: UserData,
|
|
119
|
+
): Promise<ScheduledWorkflow> {
|
|
120
|
+
this.logger.log(`Updating schedule ${updateScheduleDto.id} by user ${loggedInUser.id}`);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const schedule = await this.scheduledWorkflowRepository.findOne({
|
|
124
|
+
where: { id: updateScheduleDto.id },
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (!schedule) {
|
|
128
|
+
throw new NotFoundException(`Scheduled workflow not found with ID: ${updateScheduleDto.id}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Validate cron expression if updated
|
|
132
|
+
if (updateScheduleDto.cron_expression) {
|
|
133
|
+
this.validateCronExpression(
|
|
134
|
+
updateScheduleDto.cron_expression,
|
|
135
|
+
updateScheduleDto.timezone || schedule.timezone,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Update fields
|
|
140
|
+
if (updateScheduleDto.workflow_id !== undefined) {
|
|
141
|
+
schedule.workflow_id = updateScheduleDto.workflow_id;
|
|
142
|
+
}
|
|
143
|
+
if (updateScheduleDto.workflow_name !== undefined) {
|
|
144
|
+
schedule.workflow_name = updateScheduleDto.workflow_name;
|
|
145
|
+
}
|
|
146
|
+
if (updateScheduleDto.name !== undefined) {
|
|
147
|
+
schedule.name = updateScheduleDto.name;
|
|
148
|
+
}
|
|
149
|
+
if (updateScheduleDto.description !== undefined) {
|
|
150
|
+
schedule.description = updateScheduleDto.description;
|
|
151
|
+
}
|
|
152
|
+
if (updateScheduleDto.cron_expression !== undefined) {
|
|
153
|
+
schedule.cron_expression = updateScheduleDto.cron_expression;
|
|
154
|
+
schedule.next_execution_at = this.calculateNextExecution(
|
|
155
|
+
updateScheduleDto.cron_expression,
|
|
156
|
+
updateScheduleDto.timezone || schedule.timezone,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (updateScheduleDto.timezone !== undefined) {
|
|
160
|
+
schedule.timezone = updateScheduleDto.timezone;
|
|
161
|
+
}
|
|
162
|
+
if (updateScheduleDto.start_date !== undefined) {
|
|
163
|
+
schedule.start_date = updateScheduleDto.start_date
|
|
164
|
+
? new Date(updateScheduleDto.start_date)
|
|
165
|
+
: null;
|
|
166
|
+
}
|
|
167
|
+
if (updateScheduleDto.end_date !== undefined) {
|
|
168
|
+
schedule.end_date = updateScheduleDto.end_date ? new Date(updateScheduleDto.end_date) : null;
|
|
169
|
+
}
|
|
170
|
+
if (updateScheduleDto.max_executions !== undefined) {
|
|
171
|
+
schedule.max_executions = updateScheduleDto.max_executions;
|
|
172
|
+
}
|
|
173
|
+
if (updateScheduleDto.schedule_status !== undefined) {
|
|
174
|
+
schedule.schedule_status = updateScheduleDto.schedule_status;
|
|
175
|
+
}
|
|
176
|
+
if (updateScheduleDto.retry_config !== undefined) {
|
|
177
|
+
schedule.retry_config = updateScheduleDto.retry_config;
|
|
178
|
+
}
|
|
179
|
+
if (updateScheduleDto.actions !== undefined) {
|
|
180
|
+
schedule.actions = updateScheduleDto.actions;
|
|
181
|
+
}
|
|
182
|
+
if (updateScheduleDto.metadata !== undefined) {
|
|
183
|
+
schedule.metadata = updateScheduleDto.metadata;
|
|
184
|
+
}
|
|
185
|
+
if (updateScheduleDto.is_enabled !== undefined) {
|
|
186
|
+
schedule.is_enabled = updateScheduleDto.is_enabled;
|
|
187
|
+
}
|
|
188
|
+
if (updateScheduleDto.status !== undefined) {
|
|
189
|
+
schedule.status = updateScheduleDto.status;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
schedule.modified_by = loggedInUser.id;
|
|
193
|
+
schedule.modified_date = new Date();
|
|
194
|
+
|
|
195
|
+
const updatedSchedule = await this.scheduledWorkflowRepository.save(schedule);
|
|
196
|
+
|
|
197
|
+
// Remove old job and create new one if schedule changed
|
|
198
|
+
if (schedule.job_id) {
|
|
199
|
+
await this.removeJob(schedule.job_id);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (updatedSchedule.is_enabled && updatedSchedule.schedule_status === SCHEDULE_STATUS_ACTIVE) {
|
|
203
|
+
await this.scheduleJob(updatedSchedule, loggedInUser);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.logger.log(`Schedule updated successfully: ${updatedSchedule.id}`);
|
|
207
|
+
return updatedSchedule;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
this.logger.error(`Failed to update schedule: ${error.message}`, error.stack);
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* 3. Get a scheduled workflow by ID
|
|
216
|
+
*/
|
|
217
|
+
async getScheduleById(scheduleId: number, loggedInUser: UserData): Promise<ScheduledWorkflow> {
|
|
218
|
+
const schedule = await this.scheduledWorkflowRepository.findOne({
|
|
219
|
+
where: {
|
|
220
|
+
id: scheduleId,
|
|
221
|
+
organization_id: loggedInUser.organization_id,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (!schedule) {
|
|
226
|
+
throw new NotFoundException(`Scheduled workflow not found with ID: ${scheduleId}`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return schedule;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 4. Get all scheduled workflows with pagination and filters
|
|
234
|
+
*/
|
|
235
|
+
async getAllSchedules(
|
|
236
|
+
page: number = 1,
|
|
237
|
+
size: number = 10,
|
|
238
|
+
filters: any = {},
|
|
239
|
+
loggedInUser: UserData,
|
|
240
|
+
): Promise<{ data: ScheduledWorkflow[]; total: number; page: number; size: number }> {
|
|
241
|
+
const skip = (page - 1) * size;
|
|
242
|
+
|
|
243
|
+
const whereConditions: any = {
|
|
244
|
+
organization_id: loggedInUser.organization_id,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
if (filters.workflow_id) {
|
|
248
|
+
whereConditions.workflow_id = filters.workflow_id;
|
|
249
|
+
}
|
|
250
|
+
if (filters.schedule_status) {
|
|
251
|
+
whereConditions.schedule_status = filters.schedule_status;
|
|
252
|
+
}
|
|
253
|
+
if (filters.is_enabled !== undefined) {
|
|
254
|
+
whereConditions.is_enabled = filters.is_enabled;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const [data, total] = await this.scheduledWorkflowRepository.findAndCount({
|
|
258
|
+
where: whereConditions,
|
|
259
|
+
skip,
|
|
260
|
+
take: size,
|
|
261
|
+
order: { created_date: 'DESC' },
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
data,
|
|
266
|
+
total,
|
|
267
|
+
page,
|
|
268
|
+
size,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 5. Pause a scheduled workflow
|
|
274
|
+
*/
|
|
275
|
+
async pauseSchedule(scheduleId: number, loggedInUser: UserData): Promise<ScheduledWorkflow> {
|
|
276
|
+
this.logger.log(`Pausing schedule ${scheduleId} by user ${loggedInUser.id}`);
|
|
277
|
+
|
|
278
|
+
const schedule = await this.getScheduleById(scheduleId, loggedInUser);
|
|
279
|
+
|
|
280
|
+
if (schedule.schedule_status === SCHEDULE_STATUS_PAUSED) {
|
|
281
|
+
throw new BadRequestException('Schedule is already paused');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
schedule.schedule_status = SCHEDULE_STATUS_PAUSED;
|
|
285
|
+
schedule.modified_by = loggedInUser.id;
|
|
286
|
+
schedule.modified_date = new Date();
|
|
287
|
+
|
|
288
|
+
const updatedSchedule = await this.scheduledWorkflowRepository.save(schedule);
|
|
289
|
+
|
|
290
|
+
// Remove job from queue
|
|
291
|
+
if (schedule.job_id) {
|
|
292
|
+
await this.removeJob(schedule.job_id);
|
|
293
|
+
updatedSchedule.job_id = null;
|
|
294
|
+
await this.scheduledWorkflowRepository.save(updatedSchedule);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
this.logger.log(`Schedule paused successfully: ${scheduleId}`);
|
|
298
|
+
return updatedSchedule;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 6. Resume a paused scheduled workflow
|
|
303
|
+
*/
|
|
304
|
+
async resumeSchedule(scheduleId: number, loggedInUser: UserData): Promise<ScheduledWorkflow> {
|
|
305
|
+
this.logger.log(`Resuming schedule ${scheduleId} by user ${loggedInUser.id}`);
|
|
306
|
+
|
|
307
|
+
const schedule = await this.getScheduleById(scheduleId, loggedInUser);
|
|
308
|
+
|
|
309
|
+
if (schedule.schedule_status !== SCHEDULE_STATUS_PAUSED) {
|
|
310
|
+
throw new BadRequestException('Schedule is not paused');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
schedule.schedule_status = SCHEDULE_STATUS_ACTIVE;
|
|
314
|
+
schedule.modified_by = loggedInUser.id;
|
|
315
|
+
schedule.modified_date = new Date();
|
|
316
|
+
|
|
317
|
+
// Recalculate next execution time
|
|
318
|
+
schedule.next_execution_at = this.calculateNextExecution(
|
|
319
|
+
schedule.cron_expression,
|
|
320
|
+
schedule.timezone,
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const updatedSchedule = await this.scheduledWorkflowRepository.save(schedule);
|
|
324
|
+
|
|
325
|
+
// Re-add job to queue
|
|
326
|
+
if (updatedSchedule.is_enabled) {
|
|
327
|
+
await this.scheduleJob(updatedSchedule, loggedInUser);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
this.logger.log(`Schedule resumed successfully: ${scheduleId}`);
|
|
331
|
+
return updatedSchedule;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* 7. Delete a scheduled workflow
|
|
336
|
+
*/
|
|
337
|
+
async deleteSchedule(scheduleId: number, loggedInUser: UserData): Promise<void> {
|
|
338
|
+
this.logger.log(`Deleting schedule ${scheduleId} by user ${loggedInUser.id}`);
|
|
339
|
+
|
|
340
|
+
const schedule = await this.getScheduleById(scheduleId, loggedInUser);
|
|
341
|
+
|
|
342
|
+
// Remove job from queue
|
|
343
|
+
if (schedule.job_id) {
|
|
344
|
+
await this.removeJob(schedule.job_id);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Soft delete by setting status to inactive
|
|
348
|
+
schedule.schedule_status = SCHEDULE_STATUS_INACTIVE;
|
|
349
|
+
schedule.status = 'INACTIVE';
|
|
350
|
+
schedule.is_enabled = false;
|
|
351
|
+
schedule.modified_by = loggedInUser.id;
|
|
352
|
+
schedule.modified_date = new Date();
|
|
353
|
+
|
|
354
|
+
await this.scheduledWorkflowRepository.save(schedule);
|
|
355
|
+
|
|
356
|
+
this.logger.log(`Schedule deleted successfully: ${scheduleId}`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* 8. Manually trigger a scheduled workflow execution
|
|
361
|
+
*/
|
|
362
|
+
async triggerManualExecution(
|
|
363
|
+
scheduleId: number,
|
|
364
|
+
loggedInUser: UserData,
|
|
365
|
+
metadata?: Record<string, any>,
|
|
366
|
+
): Promise<{ executionLogId: number; jobId: string }> {
|
|
367
|
+
this.logger.log(`Manual trigger for schedule ${scheduleId} by user ${loggedInUser.id}`);
|
|
368
|
+
|
|
369
|
+
const schedule = await this.getScheduleById(scheduleId, loggedInUser);
|
|
370
|
+
|
|
371
|
+
const jobData: ScheduleJobData = {
|
|
372
|
+
scheduleId: schedule.id,
|
|
373
|
+
workflowId: schedule.workflow_id,
|
|
374
|
+
workflowName: schedule.workflow_name,
|
|
375
|
+
organizationId: schedule.organization_id,
|
|
376
|
+
enterpriseId: schedule.enterprise_id,
|
|
377
|
+
levelId: schedule.level_id,
|
|
378
|
+
levelType: schedule.level_type,
|
|
379
|
+
appcode: schedule.appcode,
|
|
380
|
+
createdBy: loggedInUser.id,
|
|
381
|
+
triggeredBy: 'MANUAL',
|
|
382
|
+
triggeredAt: new Date(),
|
|
383
|
+
metadata: metadata || {},
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// Add job to queue immediately (not scheduled)
|
|
387
|
+
const job = await this.scheduleQueue.add(EXECUTE_SCHEDULED_WORKFLOW_JOB, jobData, {
|
|
388
|
+
attempts: schedule.retry_config?.maxRetries || DEFAULT_MAX_RETRIES,
|
|
389
|
+
backoff: {
|
|
390
|
+
type: 'exponential',
|
|
391
|
+
delay: schedule.retry_config?.retryDelay || DEFAULT_RETRY_DELAY,
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Create execution log entry
|
|
396
|
+
const executionLog = this.executionLogRepository.create({
|
|
397
|
+
schedule_id: schedule.id,
|
|
398
|
+
workflow_id: schedule.workflow_id,
|
|
399
|
+
job_id: job.id.toString(),
|
|
400
|
+
execution_status: 'PENDING',
|
|
401
|
+
triggered_by: 'MANUAL',
|
|
402
|
+
triggered_by_user_id: loggedInUser.id,
|
|
403
|
+
organization_id: schedule.organization_id,
|
|
404
|
+
enterprise_id: schedule.enterprise_id,
|
|
405
|
+
created_by: loggedInUser.id,
|
|
406
|
+
created_date: new Date(),
|
|
407
|
+
entity_type: 'WFEL',
|
|
408
|
+
status: 'ACTIVE',
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const savedLog = await this.executionLogRepository.save(executionLog);
|
|
412
|
+
|
|
413
|
+
this.logger.log(`Manual execution triggered for schedule ${scheduleId}, job ID: ${job.id}`);
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
executionLogId: savedLog.id,
|
|
417
|
+
jobId: job.id.toString(),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* 9. Get execution logs with filters and pagination
|
|
423
|
+
*/
|
|
424
|
+
async getExecutionLogs(
|
|
425
|
+
getExecutionLogsDto: GetExecutionLogsDto,
|
|
426
|
+
loggedInUser: UserData,
|
|
427
|
+
): Promise<{ data: WorkflowExecutionLog[]; total: number; page: number; size: number }> {
|
|
428
|
+
const page = getExecutionLogsDto.page || 1;
|
|
429
|
+
const size = getExecutionLogsDto.size || 10;
|
|
430
|
+
const skip = (page - 1) * size;
|
|
431
|
+
|
|
432
|
+
const whereConditions: any = {
|
|
433
|
+
organization_id: getExecutionLogsDto.organization_id || loggedInUser.organization_id,
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
if (getExecutionLogsDto.schedule_id) {
|
|
437
|
+
whereConditions.schedule_id = getExecutionLogsDto.schedule_id;
|
|
438
|
+
}
|
|
439
|
+
if (getExecutionLogsDto.workflow_id) {
|
|
440
|
+
whereConditions.workflow_id = getExecutionLogsDto.workflow_id;
|
|
441
|
+
}
|
|
442
|
+
if (getExecutionLogsDto.execution_status) {
|
|
443
|
+
whereConditions.execution_status = getExecutionLogsDto.execution_status;
|
|
444
|
+
}
|
|
445
|
+
if (getExecutionLogsDto.execution_statuses && getExecutionLogsDto.execution_statuses.length > 0) {
|
|
446
|
+
whereConditions.execution_status = In(getExecutionLogsDto.execution_statuses);
|
|
447
|
+
}
|
|
448
|
+
if (getExecutionLogsDto.triggered_by) {
|
|
449
|
+
whereConditions.triggered_by = getExecutionLogsDto.triggered_by;
|
|
450
|
+
}
|
|
451
|
+
if (getExecutionLogsDto.triggered_by_user_id) {
|
|
452
|
+
whereConditions.triggered_by_user_id = getExecutionLogsDto.triggered_by_user_id;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Date range filter
|
|
456
|
+
if (getExecutionLogsDto.start_date && getExecutionLogsDto.end_date) {
|
|
457
|
+
whereConditions.created_date = Between(
|
|
458
|
+
new Date(getExecutionLogsDto.start_date),
|
|
459
|
+
new Date(getExecutionLogsDto.end_date),
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const [data, total] = await this.executionLogRepository.findAndCount({
|
|
464
|
+
where: whereConditions,
|
|
465
|
+
skip,
|
|
466
|
+
take: size,
|
|
467
|
+
order: {
|
|
468
|
+
[getExecutionLogsDto.sortBy || 'created_date']:
|
|
469
|
+
getExecutionLogsDto.sortOrder || 'DESC',
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
data,
|
|
475
|
+
total,
|
|
476
|
+
page,
|
|
477
|
+
size,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Get execution statistics for a schedule
|
|
483
|
+
*/
|
|
484
|
+
async getExecutionStats(scheduleId: number, loggedInUser: UserData): Promise<any> {
|
|
485
|
+
const schedule = await this.getScheduleById(scheduleId, loggedInUser);
|
|
486
|
+
|
|
487
|
+
const stats = await this.dataSource.query(
|
|
488
|
+
`SELECT
|
|
489
|
+
COUNT(*) as total_executions,
|
|
490
|
+
SUM(CASE WHEN execution_status = 'COMPLETED' THEN 1 ELSE 0 END) as successful_executions,
|
|
491
|
+
SUM(CASE WHEN execution_status = 'FAILED' THEN 1 ELSE 0 END) as failed_executions,
|
|
492
|
+
SUM(CASE WHEN execution_status = 'PENDING' THEN 1 ELSE 0 END) as pending_executions,
|
|
493
|
+
SUM(CASE WHEN execution_status = 'RUNNING' THEN 1 ELSE 0 END) as running_executions,
|
|
494
|
+
AVG(duration_ms) as average_duration_ms,
|
|
495
|
+
SUM(total_records) as total_records_processed
|
|
496
|
+
FROM cr_wf_execution_log
|
|
497
|
+
WHERE schedule_id = ?`,
|
|
498
|
+
[scheduleId],
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
const result = stats[0];
|
|
502
|
+
const successRate =
|
|
503
|
+
result.total_executions > 0
|
|
504
|
+
? (result.successful_executions / result.total_executions) * 100
|
|
505
|
+
: 0;
|
|
506
|
+
|
|
507
|
+
return {
|
|
508
|
+
schedule_id: scheduleId,
|
|
509
|
+
schedule_name: schedule.name,
|
|
510
|
+
total_executions: parseInt(result.total_executions),
|
|
511
|
+
successful_executions: parseInt(result.successful_executions),
|
|
512
|
+
failed_executions: parseInt(result.failed_executions),
|
|
513
|
+
pending_executions: parseInt(result.pending_executions),
|
|
514
|
+
running_executions: parseInt(result.running_executions),
|
|
515
|
+
average_duration_ms: parseFloat(result.average_duration_ms) || 0,
|
|
516
|
+
total_records_processed: parseInt(result.total_records_processed) || 0,
|
|
517
|
+
success_rate_percentage: parseFloat(successRate.toFixed(2)),
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Private helper: Schedule a job in Bull queue
|
|
523
|
+
*/
|
|
524
|
+
private async scheduleJob(schedule: ScheduledWorkflow, loggedInUser: UserData): Promise<void> {
|
|
525
|
+
const jobData: ScheduleJobData = {
|
|
526
|
+
scheduleId: schedule.id,
|
|
527
|
+
workflowId: schedule.workflow_id,
|
|
528
|
+
workflowName: schedule.workflow_name,
|
|
529
|
+
organizationId: schedule.organization_id,
|
|
530
|
+
enterpriseId: schedule.enterprise_id,
|
|
531
|
+
levelId: schedule.level_id,
|
|
532
|
+
levelType: schedule.level_type,
|
|
533
|
+
appcode: schedule.appcode,
|
|
534
|
+
createdBy: loggedInUser.id,
|
|
535
|
+
triggeredBy: 'SCHEDULE',
|
|
536
|
+
triggeredAt: new Date(),
|
|
537
|
+
metadata: schedule.metadata || {},
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
const job = await this.scheduleQueue.add(EXECUTE_SCHEDULED_WORKFLOW_JOB, jobData, {
|
|
541
|
+
repeat: {
|
|
542
|
+
cron: '*/1 * * * *',
|
|
543
|
+
tz: schedule.timezone,
|
|
544
|
+
startDate: schedule.start_date || undefined,
|
|
545
|
+
endDate: schedule.end_date || undefined,
|
|
546
|
+
},
|
|
547
|
+
attempts: schedule.retry_config?.maxRetries || DEFAULT_MAX_RETRIES,
|
|
548
|
+
backoff: {
|
|
549
|
+
type: 'exponential',
|
|
550
|
+
delay: schedule.retry_config?.retryDelay || DEFAULT_RETRY_DELAY,
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
schedule.job_id = job.id.toString();
|
|
555
|
+
await this.scheduledWorkflowRepository.save(schedule);
|
|
556
|
+
|
|
557
|
+
this.logger.log(`Job scheduled with ID: ${job.id} for schedule ${schedule.id}`);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Private helper: Remove a job from Bull queue
|
|
562
|
+
*/
|
|
563
|
+
private async removeJob(jobId: string): Promise<void> {
|
|
564
|
+
try {
|
|
565
|
+
const job = await this.scheduleQueue.getJob(jobId);
|
|
566
|
+
if (job) {
|
|
567
|
+
await job.remove();
|
|
568
|
+
this.logger.log(`Job removed: ${jobId}`);
|
|
569
|
+
}
|
|
570
|
+
} catch (error) {
|
|
571
|
+
this.logger.warn(`Failed to remove job ${jobId}: ${error.message}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Private helper: Validate cron expression
|
|
577
|
+
*/
|
|
578
|
+
private validateCronExpression(cronExpression: string, timezone: string): void {
|
|
579
|
+
try {
|
|
580
|
+
cronParser.CronExpressionParser.parse(cronExpression, { tz: timezone });
|
|
581
|
+
} catch (error: any) {
|
|
582
|
+
throw new BadRequestException(
|
|
583
|
+
`Invalid cron expression: ${cronExpression}. Error: ${error.message}`,
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Private helper: Calculate next execution time
|
|
590
|
+
*/
|
|
591
|
+
private calculateNextExecution(cronExpression: string, timezone: string): Date | null {
|
|
592
|
+
try {
|
|
593
|
+
const interval = cronParser.CronExpressionParser.parse(cronExpression, { tz: timezone });
|
|
594
|
+
return interval.next().toDate();
|
|
595
|
+
} catch (error: any) {
|
|
596
|
+
this.logger.error(`Failed to calculate next execution: ${error.message}`);
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Module, DynamicModule, forwardRef } from '@nestjs/common';
|
|
2
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
3
|
+
import { BullModule } from '@nestjs/bull';
|
|
4
|
+
import { ScheduledWorkflow } from './entities/scheduled-workflow.entity';
|
|
5
|
+
import { WorkflowExecutionLog } from './entities/workflow-execution-log.entity';
|
|
6
|
+
import { WorkflowScheduleService } from './service/workflow-schedule.service';
|
|
7
|
+
import { WorkflowScheduleController } from './controller/workflow-schedule.controller';
|
|
8
|
+
import { ScheduleProcessor } from './processors/schedule.processor';
|
|
9
|
+
import { WORKFLOW_SCHEDULE_QUEUE } from './constants/schedule.constants';
|
|
10
|
+
import { WorkflowAutomationModule } from '../workflow-automation/workflow-automation.module';
|
|
11
|
+
import { WorkflowScheduleModuleOptions } from './interfaces/workflow-schedule-options.interface';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Workflow Schedule Module
|
|
15
|
+
* Manages scheduled workflows and their execution using Bull + Redis
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* - To enable cron processor: WorkflowScheduleModule.forRoot({ is_workflow: true })
|
|
19
|
+
* - To disable cron processor: WorkflowScheduleModule.forRoot({ is_workflow: false })
|
|
20
|
+
* - Default (no options): WorkflowScheduleModule.forRoot() - processor DISABLED (safe default)
|
|
21
|
+
*/
|
|
22
|
+
@Module({})
|
|
23
|
+
export class WorkflowScheduleModule {
|
|
24
|
+
/**
|
|
25
|
+
* Configure the WorkflowScheduleModule with options
|
|
26
|
+
* Use this method in CoreModule/AppModule to control whether the cron processor runs
|
|
27
|
+
*
|
|
28
|
+
* @param options - Configuration options
|
|
29
|
+
* @param options.is_workflow - Set to true to enable scheduler on this server
|
|
30
|
+
* @returns DynamicModule
|
|
31
|
+
*/
|
|
32
|
+
static forRoot(options: WorkflowScheduleModuleOptions = {}): DynamicModule {
|
|
33
|
+
const { is_workflow = false } = options;
|
|
34
|
+
|
|
35
|
+
// Base providers that are always included
|
|
36
|
+
const providers: any[] = [WorkflowScheduleService];
|
|
37
|
+
|
|
38
|
+
// Conditionally add the processor based on is_workflow flag
|
|
39
|
+
if (is_workflow) {
|
|
40
|
+
providers.push(ScheduleProcessor);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
module: WorkflowScheduleModule,
|
|
45
|
+
global: true, // Make this module global so WorkflowAutomationModule can access exports
|
|
46
|
+
imports: [
|
|
47
|
+
// Register TypeORM entities
|
|
48
|
+
TypeOrmModule.forFeature([ScheduledWorkflow, WorkflowExecutionLog]),
|
|
49
|
+
|
|
50
|
+
// Register Bull queue for workflow scheduling
|
|
51
|
+
BullModule.registerQueue({
|
|
52
|
+
name: WORKFLOW_SCHEDULE_QUEUE,
|
|
53
|
+
}),
|
|
54
|
+
forwardRef(() => WorkflowAutomationModule), // 👈 handle circular dependency
|
|
55
|
+
],
|
|
56
|
+
providers,
|
|
57
|
+
controllers: [
|
|
58
|
+
// REST API controller
|
|
59
|
+
WorkflowScheduleController,
|
|
60
|
+
],
|
|
61
|
+
exports: [
|
|
62
|
+
// Export service for use in other modules
|
|
63
|
+
WorkflowScheduleService,
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|