Add cron trigger table (#14110)
This Pr begins the extensibility journey - adds a `core.cronTrigger` table - add a oneToMany relation between core.serverlessFunction and `core.cronTrigger` (one serverlessFunction can be triggered by multiple cronTriggers) - add a job to trigger a serverless function - adds a cron to trigger serverlessFunction (via the trigger job) based on the core.cronTrigger.setting.pattern - adds a command to register the cron - add the command in `cron-register-all.command.ts`
This commit is contained in:
+2
-2
@@ -6,7 +6,7 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { AutomatedTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service';
|
||||
import { WorkflowCronTriggerCronCommand } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/commands/workflow-cron-trigger.cron.command';
|
||||
import { CronTriggerCronJob } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/jobs/cron-trigger.cron.job';
|
||||
import { WorkflowCronTriggerCronJob } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/jobs/workflow-cron-trigger-cron.job';
|
||||
import { DatabaseEventTriggerListener } from 'src/modules/workflow/workflow-trigger/automated-trigger/listeners/database-event-trigger.listener';
|
||||
|
||||
@Module({
|
||||
@@ -18,7 +18,7 @@ import { DatabaseEventTriggerListener } from 'src/modules/workflow/workflow-trig
|
||||
providers: [
|
||||
AutomatedTriggerWorkspaceService,
|
||||
DatabaseEventTriggerListener,
|
||||
CronTriggerCronJob,
|
||||
WorkflowCronTriggerCronJob,
|
||||
WorkflowCronTriggerCronCommand,
|
||||
],
|
||||
exports: [AutomatedTriggerWorkspaceService, WorkflowCronTriggerCronCommand],
|
||||
|
||||
+5
-5
@@ -4,9 +4,9 @@ import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decora
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
import {
|
||||
CRON_TRIGGER_CRON_PATTERN,
|
||||
CronTriggerCronJob,
|
||||
} from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/jobs/cron-trigger.cron.job';
|
||||
WORKFLOW_CRON_TRIGGER_CRON_PATTERN,
|
||||
WorkflowCronTriggerCronJob,
|
||||
} from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/jobs/workflow-cron-trigger-cron.job';
|
||||
|
||||
@Command({
|
||||
name: 'cron:workflow:automated-cron-trigger',
|
||||
@@ -22,11 +22,11 @@ export class WorkflowCronTriggerCronCommand extends CommandRunner {
|
||||
|
||||
async run(): Promise<void> {
|
||||
await this.messageQueueService.addCron<undefined>({
|
||||
jobName: CronTriggerCronJob.name,
|
||||
jobName: WorkflowCronTriggerCronJob.name,
|
||||
data: undefined,
|
||||
options: {
|
||||
repeat: {
|
||||
pattern: CRON_TRIGGER_CRON_PATTERN,
|
||||
pattern: WORKFLOW_CRON_TRIGGER_CRON_PATTERN,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
+8
-5
@@ -15,16 +15,16 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
|
||||
import { AutomatedTriggerType } from 'src/modules/workflow/common/standard-objects/workflow-automated-trigger.workspace-entity';
|
||||
import { type CronTriggerSettings } from 'src/modules/workflow/workflow-trigger/automated-trigger/constants/automated-trigger-settings';
|
||||
import { shouldRunNow } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/utils/should-run-now.utils';
|
||||
import {
|
||||
WorkflowTriggerJob,
|
||||
type WorkflowTriggerJobData,
|
||||
} from 'src/modules/workflow/workflow-trigger/jobs/workflow-trigger.job';
|
||||
import { shouldRunNow } from 'src/utils/should-run-now.utils';
|
||||
|
||||
export const CRON_TRIGGER_CRON_PATTERN = '* * * * *';
|
||||
export const WORKFLOW_CRON_TRIGGER_CRON_PATTERN = '* * * * *';
|
||||
|
||||
@Processor(MessageQueue.cronQueue)
|
||||
export class CronTriggerCronJob {
|
||||
export class WorkflowCronTriggerCronJob {
|
||||
constructor(
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
@@ -35,8 +35,11 @@ export class CronTriggerCronJob {
|
||||
private readonly exceptionHandlerService: ExceptionHandlerService,
|
||||
) {}
|
||||
|
||||
@Process(CronTriggerCronJob.name)
|
||||
@SentryCronMonitor(CronTriggerCronJob.name, CRON_TRIGGER_CRON_PATTERN)
|
||||
@Process(WorkflowCronTriggerCronJob.name)
|
||||
@SentryCronMonitor(
|
||||
WorkflowCronTriggerCronJob.name,
|
||||
WORKFLOW_CRON_TRIGGER_CRON_PATTERN,
|
||||
)
|
||||
async handle() {
|
||||
const activeWorkspaces = await this.workspaceRepository.find({
|
||||
where: {
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
export const shouldRunNow = (
|
||||
pattern: string,
|
||||
now: Date,
|
||||
rootCronIntervalMs = 60_000,
|
||||
) => {
|
||||
try {
|
||||
const interval = CronExpressionParser.parse(pattern, {
|
||||
currentDate: now,
|
||||
});
|
||||
|
||||
const prevTriggerDate = interval.prev();
|
||||
const diff = Math.abs(prevTriggerDate.getTime() - now.getTime());
|
||||
|
||||
return diff < rootCronIntervalMs;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
import { shouldRunNow } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/utils/should-run-now.utils';
|
||||
|
||||
const getNowDate = (hour: string) => {
|
||||
return new Date(`2025-01-01T${hour}.100Z`);
|
||||
};
|
||||
|
||||
describe('shouldRunNow', () => {
|
||||
it('returns true when now matches cron pattern */1 * * * *', () => {
|
||||
const cron = '*/1 * * * *';
|
||||
|
||||
expect(shouldRunNow(cron, getNowDate('10:00:00'))).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true with a 50s root cron delay', () => {
|
||||
const cron = '*/1 * * * *';
|
||||
|
||||
expect(shouldRunNow(cron, getNowDate('10:00:50'))).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true 5 times in a row for a */5 pattern', () => {
|
||||
const cron = '*/5 * * * *'; // every 5 minutes
|
||||
|
||||
expect(shouldRunNow(cron, getNowDate('09:59:00'))).toBe(false);
|
||||
expect(shouldRunNow(cron, getNowDate('10:00:00'))).toBe(true);
|
||||
expect(shouldRunNow(cron, getNowDate('10:01:00'))).toBe(false);
|
||||
expect(shouldRunNow(cron, getNowDate('10:02:00'))).toBe(false);
|
||||
expect(shouldRunNow(cron, getNowDate('10:03:00'))).toBe(false);
|
||||
expect(shouldRunNow(cron, getNowDate('10:04:00'))).toBe(false);
|
||||
expect(shouldRunNow(cron, getNowDate('10:05:00'))).toBe(true);
|
||||
expect(shouldRunNow(cron, getNowDate('10:06:00'))).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for invalid cron pattern', () => {
|
||||
const cron = 'invalid-cron';
|
||||
|
||||
expect(shouldRunNow(cron, getNowDate('10:00:00'))).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false if the next run is outside the interval window (2 minutes)', () => {
|
||||
const cron = '*/10 * * * *'; // every 10 minutes
|
||||
const interval2min = 2 * 60_000;
|
||||
|
||||
expect(shouldRunNow(cron, getNowDate('10:06:00'), interval2min)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user