335 workflow implement workflow cron triggers backend (#9988)
[Backend side] Add cron triggers to workflow Closes https://github.com/twentyhq/core-team-issues/issues/335
This commit is contained in:
@ -13,9 +13,9 @@ import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.
|
||||
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||
import { WorkflowEventListenerWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-event-listener.workspace-entity';
|
||||
import {
|
||||
WorkflowEventTriggerJob,
|
||||
WorkflowEventTriggerJobData,
|
||||
} from 'src/modules/workflow/workflow-trigger/jobs/workflow-event-trigger.job';
|
||||
WorkflowTriggerJob,
|
||||
WorkflowTriggerJobData,
|
||||
} from 'src/modules/workflow/workflow-trigger/jobs/workflow-trigger.job';
|
||||
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
|
||||
@ -103,8 +103,8 @@ export class DatabaseEventTriggerListener {
|
||||
|
||||
for (const eventListener of eventListeners) {
|
||||
for (const eventPayload of payload.events) {
|
||||
this.messageQueueService.add<WorkflowEventTriggerJobData>(
|
||||
WorkflowEventTriggerJob.name,
|
||||
this.messageQueueService.add<WorkflowTriggerJobData>(
|
||||
WorkflowTriggerJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
workflowId: eventListener.workflowId,
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import {
|
||||
WorkflowVersionStatus,
|
||||
WorkflowVersionWorkspaceEntity,
|
||||
} from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service';
|
||||
import {
|
||||
WorkflowTriggerException,
|
||||
WorkflowTriggerExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception';
|
||||
|
||||
export type WorkflowEventTriggerJobData = {
|
||||
workspaceId: string;
|
||||
workflowId: string;
|
||||
payload: object;
|
||||
};
|
||||
|
||||
@Processor({ queueName: MessageQueue.workflowQueue, scope: Scope.REQUEST })
|
||||
export class WorkflowEventTriggerJob {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly workflowRunnerWorkspaceService: WorkflowRunnerWorkspaceService,
|
||||
) {}
|
||||
|
||||
@Process(WorkflowEventTriggerJob.name)
|
||||
async handle(data: WorkflowEventTriggerJobData): Promise<void> {
|
||||
const workflowRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowWorkspaceEntity>(
|
||||
'workflow',
|
||||
);
|
||||
|
||||
const workflow = await workflowRepository.findOneByOrFail({
|
||||
id: data.workflowId,
|
||||
});
|
||||
|
||||
if (!workflow.lastPublishedVersionId) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Workflow has no published version',
|
||||
WorkflowTriggerExceptionCode.INTERNAL_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
'workflowVersion',
|
||||
);
|
||||
|
||||
const workflowVersion = await workflowVersionRepository.findOneByOrFail({
|
||||
id: workflow.lastPublishedVersionId,
|
||||
});
|
||||
|
||||
if (workflowVersion.status !== WorkflowVersionStatus.ACTIVE) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Workflow version is not active',
|
||||
WorkflowTriggerExceptionCode.INTERNAL_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
await this.workflowRunnerWorkspaceService.run(
|
||||
data.workspaceId,
|
||||
workflow.lastPublishedVersionId,
|
||||
data.payload,
|
||||
{
|
||||
source: FieldActorSource.WORKFLOW,
|
||||
name: workflow.name,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import {
|
||||
WorkflowVersionStatus,
|
||||
WorkflowVersionWorkspaceEntity,
|
||||
} from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service';
|
||||
import {
|
||||
WorkflowTriggerException,
|
||||
WorkflowTriggerExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception';
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
|
||||
export type WorkflowTriggerJobData = {
|
||||
workspaceId: string;
|
||||
workflowId: string;
|
||||
payload: object;
|
||||
};
|
||||
|
||||
@Processor({ queueName: MessageQueue.workflowQueue, scope: Scope.REQUEST })
|
||||
export class WorkflowTriggerJob {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly workflowRunnerWorkspaceService: WorkflowRunnerWorkspaceService,
|
||||
@InjectMessageQueue(MessageQueue.workflowQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
|
||||
@Process(WorkflowTriggerJob.name)
|
||||
async handle(data: WorkflowTriggerJobData): Promise<void> {
|
||||
try {
|
||||
const workflowRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowWorkspaceEntity>(
|
||||
'workflow',
|
||||
);
|
||||
|
||||
const workflow = await workflowRepository.findOneByOrFail({
|
||||
id: data.workflowId,
|
||||
});
|
||||
|
||||
if (!workflow.lastPublishedVersionId) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Workflow has no published version',
|
||||
WorkflowTriggerExceptionCode.INTERNAL_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
'workflowVersion',
|
||||
);
|
||||
|
||||
const workflowVersion = await workflowVersionRepository.findOneByOrFail({
|
||||
id: workflow.lastPublishedVersionId,
|
||||
});
|
||||
|
||||
if (workflowVersion.status !== WorkflowVersionStatus.ACTIVE) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Workflow version is not active',
|
||||
WorkflowTriggerExceptionCode.INTERNAL_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
await this.workflowRunnerWorkspaceService.run(
|
||||
data.workspaceId,
|
||||
workflow.lastPublishedVersionId,
|
||||
data.payload,
|
||||
{
|
||||
source: FieldActorSource.WORKFLOW,
|
||||
name: workflow.name,
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
// We remove cron if it exists when no valid workflowVersion exists
|
||||
await this.messageQueueService.removeCron({
|
||||
jobName: WorkflowTriggerJob.name,
|
||||
jobId: data.workflowId,
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output
|
||||
export enum WorkflowTriggerType {
|
||||
DATABASE_EVENT = 'DATABASE_EVENT',
|
||||
MANUAL = 'MANUAL',
|
||||
CRON = 'CRON',
|
||||
}
|
||||
|
||||
type BaseWorkflowTriggerSettings = {
|
||||
@ -35,8 +36,16 @@ export type WorkflowManualTrigger = BaseTrigger & {
|
||||
};
|
||||
};
|
||||
|
||||
export type WorkflowCronTrigger = BaseTrigger & {
|
||||
type: WorkflowTriggerType.CRON;
|
||||
settings: {
|
||||
pattern: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type WorkflowManualTriggerSettings = WorkflowManualTrigger['settings'];
|
||||
|
||||
export type WorkflowTrigger =
|
||||
| WorkflowDatabaseEventTrigger
|
||||
| WorkflowManualTrigger;
|
||||
| WorkflowManualTrigger
|
||||
| WorkflowCronTrigger;
|
||||
|
||||
@ -6,7 +6,7 @@ import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/s
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowRunnerModule } from 'src/modules/workflow/workflow-runner/workflow-runner.module';
|
||||
import { DatabaseEventTriggerModule } from 'src/modules/workflow/workflow-trigger/database-event-trigger/database-event-trigger.module';
|
||||
import { WorkflowEventTriggerJob } from 'src/modules/workflow/workflow-trigger/jobs/workflow-event-trigger.job';
|
||||
import { WorkflowTriggerJob } from 'src/modules/workflow/workflow-trigger/jobs/workflow-trigger.job';
|
||||
import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
|
||||
@ -20,7 +20,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
providers: [
|
||||
WorkflowTriggerWorkspaceService,
|
||||
ScopedWorkspaceContextFactory,
|
||||
WorkflowEventTriggerJob,
|
||||
WorkflowTriggerJob,
|
||||
],
|
||||
exports: [WorkflowTriggerWorkspaceService],
|
||||
})
|
||||
|
||||
@ -29,6 +29,13 @@ import {
|
||||
import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
import { assertVersionCanBeActivated } from 'src/modules/workflow/workflow-trigger/utils/assert-version-can-be-activated.util';
|
||||
import { assertNever } from 'src/utils/assert';
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
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 {
|
||||
WorkflowTriggerJob,
|
||||
WorkflowTriggerJobData,
|
||||
} from 'src/modules/workflow/workflow-trigger/jobs/workflow-trigger.job';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowTriggerWorkspaceService {
|
||||
@ -41,6 +48,8 @@ export class WorkflowTriggerWorkspaceService {
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectMessageQueue(MessageQueue.workflowQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
|
||||
private getWorkspaceId() {
|
||||
@ -329,6 +338,23 @@ export class WorkflowTriggerWorkspaceService {
|
||||
|
||||
return;
|
||||
case WorkflowTriggerType.MANUAL:
|
||||
return;
|
||||
case WorkflowTriggerType.CRON:
|
||||
await this.messageQueueService.addCron<WorkflowTriggerJobData>({
|
||||
jobName: WorkflowTriggerJob.name,
|
||||
jobId: workflowVersion.workflowId,
|
||||
data: {
|
||||
workspaceId: this.getWorkspaceId(),
|
||||
workflowId: workflowVersion.workflowId,
|
||||
payload: {},
|
||||
},
|
||||
options: {
|
||||
repeat: {
|
||||
pattern: workflowVersion.trigger.settings.pattern,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
default: {
|
||||
assertNever(workflowVersion.trigger);
|
||||
@ -351,6 +377,13 @@ export class WorkflowTriggerWorkspaceService {
|
||||
|
||||
return;
|
||||
case WorkflowTriggerType.MANUAL:
|
||||
return;
|
||||
case WorkflowTriggerType.CRON:
|
||||
await this.messageQueueService.removeCron({
|
||||
jobName: WorkflowTriggerJob.name,
|
||||
jobId: workflowVersion.workflowId,
|
||||
});
|
||||
|
||||
return;
|
||||
default:
|
||||
assertNever(workflowVersion.trigger);
|
||||
|
||||
Reference in New Issue
Block a user