Add workflow statuses (#6765)
Following figma updates https://www.figma.com/design/PNBfTgOVraw557OXChYagk/Explo?node-id=21872-7929&t=DOUzd6rzwr6lprcs-0 - No activity targets for workflow entities for now - Adding a direct relation between workflow run et workflow - Adding a status on the version (draft, active, deactivated) - Adding a list of statuses on workflow - publishedVersionId => lastPublishedVersionId Also adding: - the endpoint to deactivate a version
This commit is contained in:
@ -19,6 +19,7 @@ export class WorkflowTriggerGraphqlApiExceptionFilter
|
||||
case WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION:
|
||||
case WorkflowTriggerExceptionCode.INVALID_ACTION_TYPE:
|
||||
case WorkflowTriggerExceptionCode.INVALID_WORKFLOW_TRIGGER:
|
||||
case WorkflowTriggerExceptionCode.FORBIDDEN:
|
||||
throw new UserInputError(exception.message);
|
||||
default:
|
||||
throw new InternalServerError(exception.message);
|
||||
|
||||
@ -27,6 +27,15 @@ export class WorkflowTriggerResolver {
|
||||
);
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async disableWorkflowTrigger(
|
||||
@Args('workflowVersionId') workflowVersionId: string,
|
||||
) {
|
||||
return await this.workflowTriggerWorkspaceService.disableWorkflowTrigger(
|
||||
workflowVersionId,
|
||||
);
|
||||
}
|
||||
|
||||
@Mutation(() => WorkflowRunDTO)
|
||||
async runWorkflowVersion(
|
||||
@AuthWorkspaceMemberId() workspaceMemberId: string,
|
||||
|
||||
@ -27,7 +27,7 @@ export const workflowsAllView = async (
|
||||
{
|
||||
fieldMetadataId:
|
||||
objectMetadataMap[STANDARD_OBJECT_IDS.workflow].fields[
|
||||
WORKFLOW_STANDARD_FIELD_IDS.publishedVersionId
|
||||
WORKFLOW_STANDARD_FIELD_IDS.lastPublishedVersionId
|
||||
],
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
|
||||
@ -10,7 +10,6 @@ export const ACTIVITY_TARGET_STANDARD_FIELD_IDS = {
|
||||
person: '20202020-4afd-4ae7-99c2-de57d795a93f',
|
||||
company: '20202020-7cc0-44a1-8068-f11171fdd02e',
|
||||
opportunity: '20202020-1fc2-4af1-8c91-7901ee0fd38b',
|
||||
workflow: '20202020-a63d-40d0-b24d-ddcc1347d583',
|
||||
custom: '20202020-7f21-442f-94be-32462281b1ca',
|
||||
};
|
||||
|
||||
@ -45,7 +44,6 @@ export const ATTACHMENT_STANDARD_FIELD_IDS = {
|
||||
person: '20202020-0158-4aa2-965c-5cdafe21ffa2',
|
||||
company: '20202020-ceab-4a28-b546-73b06b4c08d5',
|
||||
opportunity: '20202020-7374-499d-bea3-9354890755b5',
|
||||
workflow: '20202020-0906-4dc3-b26f-edc951c7ab82',
|
||||
custom: '20202020-302d-43b3-9aea-aa4f89282a9f',
|
||||
};
|
||||
|
||||
@ -190,7 +188,6 @@ export const TIMELINE_ACTIVITY_STANDARD_FIELD_IDS = {
|
||||
opportunity: '20202020-7664-4a35-a3df-580d389fd527',
|
||||
task: '20202020-b2f5-415c-9135-a31dfe49501b',
|
||||
note: '20202020-ec55-4135-8da5-3a20badc0156',
|
||||
workflow: '20202020-9e59-4030-aa27-55abd676c3c8',
|
||||
custom: '20202020-4a71-41b0-9f83-9cdcca3f8b14',
|
||||
linkedRecordCachedName: '20202020-cfdb-4bef-bbce-a29f41230934',
|
||||
linkedRecordId: '20202020-2e0e-48c0-b445-ee6c1e61687d',
|
||||
@ -284,7 +281,6 @@ export const NOTE_TARGET_STANDARD_FIELD_IDS = {
|
||||
company: 'c500fbc0-d6f2-4982-a959-5a755431696c',
|
||||
opportunity: '20202020-4e42-417a-a705-76581c9ade79',
|
||||
custom: '20202020-3d12-4579-94ee-7117c1bad492',
|
||||
workflow: '20202020-eb46-47c5-8f3f-f3f93e7aec20',
|
||||
};
|
||||
|
||||
export const OPPORTUNITY_STANDARD_FIELD_IDS = {
|
||||
@ -348,7 +344,6 @@ export const TASK_TARGET_STANDARD_FIELD_IDS = {
|
||||
company: '20202020-4703-4a4e-948c-487b0c60a92c',
|
||||
opportunity: '20202020-6cb2-4c01-a9a5-aca3dbc11d41',
|
||||
custom: '20202020-41c1-4c9a-8c75-be0971ef89af',
|
||||
workflow: '20202020-a16c-47a3-b21c-c41c9bcac659',
|
||||
};
|
||||
|
||||
export const VIEW_FIELD_STANDARD_FIELD_IDS = {
|
||||
@ -400,20 +395,18 @@ export const WORKFLOW_EVENT_LISTENER_STANDARD_FIELD_IDS = {
|
||||
|
||||
export const WORKFLOW_STANDARD_FIELD_IDS = {
|
||||
name: '20202020-b3d3-478f-acc0-5d901e725b20',
|
||||
publishedVersionId: '20202020-326a-4fba-8639-3456c0a169e8',
|
||||
versions: '20202020-9432-416e-8f3c-27ee3153d099',
|
||||
eventListeners: '20202020-0229-4c66-832e-035c67579a38',
|
||||
lastPublishedVersionId: '20202020-326a-4fba-8639-3456c0a169e8',
|
||||
statuses: '20202020-357c-4432-8c50-8c31b4a552d9',
|
||||
position: '20202020-39b0-4d8c-8c5f-33c2326deb5f',
|
||||
versions: '20202020-9432-416e-8f3c-27ee3153d099',
|
||||
runs: '20202020-759b-4340-b58b-e73595c4df4f',
|
||||
eventListeners: '20202020-0229-4c66-832e-035c67579a38',
|
||||
favorites: '20202020-c554-4c41-be7a-cf9cd4b0d512',
|
||||
activityTargets: '20202020-9d65-445a-899d-1c6f1cf3a9ab',
|
||||
attachments: '20202020-ea95-4d4d-81cd-9921740316b8',
|
||||
timelineActivities: '20202020-dd79-492a-9d11-58333ed0f71a',
|
||||
taskTargets: '20202020-0094-4e79-b934-03eaa8ab949c',
|
||||
noteTargets: '20202020-40aa-4839-965e-972a2f72e08d',
|
||||
};
|
||||
|
||||
export const WORKFLOW_RUN_STANDARD_FIELD_IDS = {
|
||||
workflowVersion: '20202020-2f52-4ba8-8dc4-d0d6adb9578d',
|
||||
workflow: '20202020-8c57-4e7f-84f5-f373f68e1b82',
|
||||
startedAt: '20202020-a234-4e2d-bd15-85bcea6bb183',
|
||||
endedAt: '20202020-e1c1-4b6b-bbbd-b2beaf2e159e',
|
||||
status: '20202020-6b3e-4f9c-8c2b-2e5b8e6d6f3b',
|
||||
@ -424,6 +417,7 @@ export const WORKFLOW_VERSION_STANDARD_FIELD_IDS = {
|
||||
name: '20202020-a12f-4cca-9937-a2e40cc65509',
|
||||
workflow: '20202020-afa3-46c3-91b0-0631ca6aa1c8',
|
||||
trigger: '20202020-4eae-43e7-86e0-212b41a30b48',
|
||||
status: '20202020-5a34-440e-8a25-39d8c3d1d4cf',
|
||||
runs: '20202020-1d08-46df-901a-85045f18099a',
|
||||
steps: '20202020-5988-4a64-b94a-1f9b7b989039',
|
||||
};
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
||||
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
@ -17,7 +15,6 @@ import { ActivityWorkspaceEntity } from 'src/modules/activity/standard-objects/a
|
||||
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.activityTarget,
|
||||
@ -89,27 +86,6 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceJoinColumn('opportunity')
|
||||
opportunityId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.workflow,
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
label: 'Workflow',
|
||||
description: 'ActivityTarget workflow',
|
||||
icon: 'IconSettingsAutomation',
|
||||
inverseSideTarget: () => WorkflowWorkspaceEntity,
|
||||
inverseSideFieldKey: 'activityTargets',
|
||||
})
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workflow: Relation<WorkflowWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('workflow')
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
workflowId: string | null;
|
||||
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
@ -8,7 +7,6 @@ import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-en
|
||||
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
|
||||
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
|
||||
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
@ -22,7 +20,6 @@ import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.work
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@WorkspaceEntity({
|
||||
@ -168,27 +165,6 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceJoinColumn('opportunity')
|
||||
opportunityId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ATTACHMENT_STANDARD_FIELD_IDS.workflow,
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
label: 'Workflow',
|
||||
description: 'Attachment workflow',
|
||||
icon: 'IconSettingsAutomation',
|
||||
inverseSideTarget: () => WorkflowWorkspaceEntity,
|
||||
inverseSideFieldKey: 'attachments',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
workflow: Relation<WorkflowWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('workflow')
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
workflowId: string | null;
|
||||
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
||||
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
@ -17,7 +15,6 @@ import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/com
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.noteTarget,
|
||||
@ -104,25 +101,4 @@ export class NoteTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'noteTargets',
|
||||
})
|
||||
custom: Relation<CustomWorkspaceEntity>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: NOTE_TARGET_STANDARD_FIELD_IDS.workflow,
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
label: 'Workflow',
|
||||
description: 'Note workflow',
|
||||
icon: 'IconTargetArrow',
|
||||
inverseSideTarget: () => WorkflowWorkspaceEntity,
|
||||
inverseSideFieldKey: 'noteTargets',
|
||||
})
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workflow: Relation<WorkflowWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('workflow')
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
workflowId: string | null;
|
||||
}
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
||||
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
@ -17,7 +15,6 @@ import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/com
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.taskTarget,
|
||||
@ -104,25 +101,4 @@ export class TaskTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'taskTargets',
|
||||
})
|
||||
custom: Relation<CustomWorkspaceEntity>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TASK_TARGET_STANDARD_FIELD_IDS.workflow,
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
label: 'Workflow',
|
||||
description: 'Task workflow',
|
||||
icon: 'IconTargetArrow',
|
||||
inverseSideTarget: () => WorkflowWorkspaceEntity,
|
||||
inverseSideFieldKey: 'taskTargets',
|
||||
})
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workflow: Relation<WorkflowWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('workflow')
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
workflowId: string | null;
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
@ -8,7 +7,6 @@ import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-en
|
||||
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
|
||||
import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.decorator';
|
||||
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
@ -21,7 +19,6 @@ import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.work
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@WorkspaceEntity({
|
||||
@ -185,27 +182,6 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceJoinColumn('task')
|
||||
taskId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.workflow,
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
label: 'Workflow',
|
||||
description: 'Event workflow',
|
||||
icon: 'IconTargetArrow',
|
||||
inverseSideTarget: () => WorkflowWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
})
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workflow: Relation<WorkflowWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('workflow')
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
})
|
||||
workflowId: string | null;
|
||||
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
|
||||
@ -18,6 +18,7 @@ import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-re
|
||||
import { WORKFLOW_RUN_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
|
||||
export enum WorkflowRunStatus {
|
||||
NOT_STARTED = 'NOT_STARTED',
|
||||
@ -111,8 +112,8 @@ export class WorkflowRunWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.workflowVersion,
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
label: 'Workflow',
|
||||
description: 'WorkflowVersion workflow',
|
||||
label: 'Workflow version',
|
||||
description: 'Workflow version linked to the run.',
|
||||
icon: 'IconVersions',
|
||||
inverseSideTarget: () => WorkflowVersionWorkspaceEntity,
|
||||
inverseSideFieldKey: 'runs',
|
||||
@ -121,4 +122,18 @@ export class WorkflowRunWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
|
||||
@WorkspaceJoinColumn('workflowVersion')
|
||||
workflowVersionId: string;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.workflow,
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
label: 'Workflow',
|
||||
description: 'Workflow linked to the run.',
|
||||
icon: 'IconSettingsAutomation',
|
||||
inverseSideTarget: () => WorkflowWorkspaceEntity,
|
||||
inverseSideFieldKey: 'runs',
|
||||
})
|
||||
workflow: Relation<WorkflowWorkspaceEntity>;
|
||||
|
||||
@WorkspaceJoinColumn('workflow')
|
||||
workflowId: string;
|
||||
}
|
||||
|
||||
@ -24,6 +24,33 @@ import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-ob
|
||||
import { WorkflowStep } from 'src/modules/workflow/common/types/workflow-step.type';
|
||||
import { WorkflowTrigger } from 'src/modules/workflow/common/types/workflow-trigger.type';
|
||||
|
||||
export enum WorkflowVersionStatus {
|
||||
DRAFT = 'DRAFT',
|
||||
ACTIVE = 'ACTIVE',
|
||||
DEACTIVATED = 'DEACTIVATED',
|
||||
}
|
||||
|
||||
export const WorkflowVersionStatusOptions = [
|
||||
{
|
||||
value: WorkflowVersionStatus.DRAFT,
|
||||
label: 'Draft',
|
||||
position: 0,
|
||||
color: 'yellow',
|
||||
},
|
||||
{
|
||||
value: WorkflowVersionStatus.ACTIVE,
|
||||
label: 'Active',
|
||||
position: 1,
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
value: WorkflowVersionStatus.DEACTIVATED,
|
||||
label: 'Deactivated',
|
||||
position: 2,
|
||||
color: 'gray',
|
||||
},
|
||||
];
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.workflowVersion,
|
||||
namePlural: 'workflowVersions',
|
||||
@ -65,6 +92,16 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceIsNullable()
|
||||
steps: WorkflowStep[] | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.status,
|
||||
type: FieldMetadataType.SELECT,
|
||||
label: 'Version status',
|
||||
description: 'The workflow version status',
|
||||
options: WorkflowVersionStatusOptions,
|
||||
defaultValue: "'DRAFT'",
|
||||
})
|
||||
status: WorkflowVersionStatus;
|
||||
|
||||
// Relations
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.workflow,
|
||||
|
||||
@ -15,14 +15,14 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { WORKFLOW_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ActivityTargetWorkspaceEntity } from 'src/modules/activity/standard-objects/activity-target.workspace-entity';
|
||||
import { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objects/attachment.workspace-entity';
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
import { NoteTargetWorkspaceEntity } from 'src/modules/note/standard-objects/note-target.workspace-entity';
|
||||
import { TaskTargetWorkspaceEntity } from 'src/modules/task/standard-objects/task-target.workspace-entity';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
import { WorkflowEventListenerWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-event-listener.workspace-entity';
|
||||
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
|
||||
import {
|
||||
WorkflowVersionStatus,
|
||||
WorkflowVersionStatusOptions,
|
||||
WorkflowVersionWorkspaceEntity,
|
||||
} from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.workflow,
|
||||
@ -47,14 +47,24 @@ export class WorkflowWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
name: string;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.publishedVersionId,
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.lastPublishedVersionId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Published Version Id',
|
||||
description: 'The workflow published version id',
|
||||
label: 'Last published Version Id',
|
||||
description: 'The workflow last published version id',
|
||||
icon: 'IconVersions',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
publishedVersionId: string | null;
|
||||
lastPublishedVersionId: string | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.statuses,
|
||||
type: FieldMetadataType.MULTI_SELECT,
|
||||
label: 'Statuses',
|
||||
description: 'The current statuses of the workflow versions',
|
||||
options: WorkflowVersionStatusOptions,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
statuses: WorkflowVersionStatus[] | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.position,
|
||||
@ -80,6 +90,18 @@ export class WorkflowWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceIsNullable()
|
||||
versions: Relation<WorkflowVersionWorkspaceEntity[]>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.runs,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
label: 'Runs',
|
||||
description: 'Workflow runs linked to the workflow.',
|
||||
icon: 'IconVersions',
|
||||
inverseSideTarget: () => WorkflowRunWorkspaceEntity,
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
runs: Relation<WorkflowRunWorkspaceEntity>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.eventListeners,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
@ -92,17 +114,6 @@ export class WorkflowWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceIsNullable()
|
||||
eventListeners: Relation<WorkflowEventListenerWorkspaceEntity[]>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.activityTargets,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
label: 'Activities',
|
||||
description: 'Activities tied to the contact',
|
||||
icon: 'IconCheckbox',
|
||||
inverseSideTarget: () => ActivityTargetWorkspaceEntity,
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
activityTargets: Relation<ActivityTargetWorkspaceEntity[]>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.favorites,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
@ -114,50 +125,4 @@ export class WorkflowWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
favorites: Relation<FavoriteWorkspaceEntity[]>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.attachments,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
label: 'Attachments',
|
||||
description: 'Attachments linked to the contact.',
|
||||
icon: 'IconFileImport',
|
||||
inverseSideTarget: () => AttachmentWorkspaceEntity,
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
attachments: Relation<AttachmentWorkspaceEntity[]>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.timelineActivities,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
label: 'Events',
|
||||
description: 'Events linked to the workflow',
|
||||
icon: 'IconTimelineEvent',
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
@WorkspaceIsSystem()
|
||||
timelineActivities: Relation<TimelineActivityWorkspaceEntity[]>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.taskTargets,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
label: 'Tasks',
|
||||
description: 'Tasks tied to the workflow',
|
||||
icon: 'IconCheckbox',
|
||||
inverseSideTarget: () => TaskTargetWorkspaceEntity,
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
taskTargets: Relation<TaskTargetWorkspaceEntity[]>;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: WORKFLOW_STANDARD_FIELD_IDS.noteTargets,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
label: 'Notes',
|
||||
description: 'Notes tied to the workflow',
|
||||
icon: 'IconNotes',
|
||||
inverseSideTarget: () => NoteTargetWorkspaceEntity,
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
noteTargets: Relation<NoteTargetWorkspaceEntity[]>;
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
export class WorkflowCommonWorkspaceService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
|
||||
async getWorkflowVersion(workflowVersionId: string): Promise<
|
||||
async getWorkflowVersionOrFail(workflowVersionId: string): Promise<
|
||||
Omit<WorkflowVersionWorkspaceEntity, 'trigger'> & {
|
||||
trigger: WorkflowTrigger;
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ export class RunWorkflowJob {
|
||||
await this.workflowRunWorkspaceService.startWorkflowRun(workflowRunId);
|
||||
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersion(
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail(
|
||||
workflowVersionId,
|
||||
);
|
||||
|
||||
|
||||
@ -10,4 +10,5 @@ export class WorkflowRunException extends CustomException {
|
||||
export enum WorkflowRunExceptionCode {
|
||||
WORKFLOW_RUN_NOT_FOUND = 'WORKFLOW_RUN_NOT_FOUND',
|
||||
INVALID_OPERATION = 'INVALID_OPERATION',
|
||||
INVALID_INPUT = 'INVALID_INPUT',
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service';
|
||||
|
||||
@Module({
|
||||
imports: [WorkflowCommonModule],
|
||||
providers: [WorkflowRunWorkspaceService],
|
||||
exports: [WorkflowRunWorkspaceService],
|
||||
})
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
WorkflowRunStatus,
|
||||
WorkflowRunWorkspaceEntity,
|
||||
} from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workflow-common.workspace-service';
|
||||
import {
|
||||
WorkflowRunException,
|
||||
WorkflowRunExceptionCode,
|
||||
@ -13,7 +14,10 @@ import {
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowRunWorkspaceService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
) {}
|
||||
|
||||
async createWorkflowRun(workflowVersionId: string, createdBy: ActorMetadata) {
|
||||
const workflowRunRepository =
|
||||
@ -21,10 +25,16 @@ export class WorkflowRunWorkspaceService {
|
||||
'workflowRun',
|
||||
);
|
||||
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail(
|
||||
workflowVersionId,
|
||||
);
|
||||
|
||||
return (
|
||||
await workflowRunRepository.save({
|
||||
workflowVersionId,
|
||||
createdBy,
|
||||
workflowId: workflowVersion.workflowId,
|
||||
status: WorkflowRunStatus.NOT_STARTED,
|
||||
})
|
||||
).id;
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { DatabaseEventTriggerService } from 'src/modules/workflow/workflow-trigger/database-event-trigger/database-event-trigger.service';
|
||||
import { DatabaseEventTriggerListener } from 'src/modules/workflow/workflow-trigger/database-event-trigger/listeners/database-event-trigger.listener';
|
||||
|
||||
@Module({
|
||||
imports: [FeatureFlagModule],
|
||||
providers: [DatabaseEventTriggerService, DatabaseEventTriggerListener],
|
||||
exports: [DatabaseEventTriggerService],
|
||||
})
|
||||
export class DatabaseEventTriggerModule {}
|
||||
@ -0,0 +1,50 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { WorkflowEventListenerWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-event-listener.workspace-entity';
|
||||
import { WorkflowDatabaseEventTrigger } from 'src/modules/workflow/common/types/workflow-trigger.type';
|
||||
|
||||
@Injectable()
|
||||
export class DatabaseEventTriggerService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
|
||||
async createEventListener(
|
||||
workflowId: string,
|
||||
trigger: WorkflowDatabaseEventTrigger,
|
||||
manager: EntityManager,
|
||||
) {
|
||||
const eventName = trigger.settings.eventName;
|
||||
|
||||
const workflowEventListenerRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowEventListenerWorkspaceEntity>(
|
||||
'workflowEventListener',
|
||||
);
|
||||
|
||||
const workflowEventListener = await workflowEventListenerRepository.create({
|
||||
workflowId,
|
||||
eventName,
|
||||
});
|
||||
|
||||
await workflowEventListenerRepository.save(
|
||||
workflowEventListener,
|
||||
{},
|
||||
manager,
|
||||
);
|
||||
}
|
||||
|
||||
async deleteEventListener(workflowId: string, manager: EntityManager) {
|
||||
const workflowEventListenerRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowEventListenerWorkspaceEntity>(
|
||||
'workflowEventListener',
|
||||
);
|
||||
|
||||
await workflowEventListenerRepository.delete(
|
||||
{
|
||||
workflowId,
|
||||
},
|
||||
manager,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,10 @@ import { Processor } from 'src/engine/integrations/message-queue/decorators/proc
|
||||
import { MessageQueue } from 'src/engine/integrations/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/workflow-runner.workspace-service';
|
||||
import {
|
||||
@ -36,16 +40,32 @@ export class WorkflowEventTriggerJob {
|
||||
id: data.workflowId,
|
||||
});
|
||||
|
||||
if (!workflow.publishedVersionId) {
|
||||
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.publishedVersionId,
|
||||
workflow.lastPublishedVersionId,
|
||||
data.payload,
|
||||
{
|
||||
source: FieldActorSource.WORKFLOW,
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
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 {
|
||||
WorkflowTrigger,
|
||||
WorkflowTriggerType,
|
||||
@ -8,7 +12,32 @@ import {
|
||||
WorkflowTriggerExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-trigger/workflow-trigger.exception';
|
||||
|
||||
export function assertWorkflowVersionIsValid(
|
||||
export function assertVersionCanBeActivated(
|
||||
workflowVersion: Omit<WorkflowVersionWorkspaceEntity, 'trigger'> & {
|
||||
trigger: WorkflowTrigger;
|
||||
},
|
||||
workflow: WorkflowWorkspaceEntity,
|
||||
) {
|
||||
assertVersionIsValid(workflowVersion);
|
||||
|
||||
const isLastPublishedVersion =
|
||||
workflow.lastPublishedVersionId === workflowVersion.id;
|
||||
|
||||
const isDraft = workflowVersion.status === WorkflowVersionStatus.DRAFT;
|
||||
|
||||
const isLastPublishedVersionDeactivated =
|
||||
workflowVersion.status === WorkflowVersionStatus.DEACTIVATED &&
|
||||
isLastPublishedVersion;
|
||||
|
||||
if (!isDraft && !isLastPublishedVersionDeactivated) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Cannot activate non-draft or non-last-published version',
|
||||
WorkflowTriggerExceptionCode.INVALID_INPUT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function assertVersionIsValid(
|
||||
workflowVersion: Omit<WorkflowVersionWorkspaceEntity, 'trigger'> & {
|
||||
trigger: WorkflowTrigger;
|
||||
},
|
||||
@ -12,5 +12,6 @@ export enum WorkflowTriggerExceptionCode {
|
||||
INVALID_WORKFLOW_TRIGGER = 'INVALID_WORKFLOW_TRIGGER',
|
||||
INVALID_WORKFLOW_VERSION = 'INVALID_WORKFLOW_VERSION',
|
||||
INVALID_ACTION_TYPE = 'INVALID_ACTION_TYPE',
|
||||
FORBIDDEN = 'FORBIDDEN',
|
||||
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
||||
}
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
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 { DatabaseEventTriggerListener } from 'src/modules/workflow/workflow-trigger/listeners/database-event-trigger.listener';
|
||||
import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workflow-trigger.workspace-service';
|
||||
|
||||
@Module({
|
||||
imports: [WorkflowCommonModule, WorkflowRunnerModule, FeatureFlagModule],
|
||||
imports: [
|
||||
WorkflowCommonModule,
|
||||
WorkflowRunnerModule,
|
||||
DatabaseEventTriggerModule,
|
||||
],
|
||||
providers: [
|
||||
WorkflowTriggerWorkspaceService,
|
||||
ScopedWorkspaceContextFactory,
|
||||
DatabaseEventTriggerListener,
|
||||
WorkflowEventTriggerJob,
|
||||
],
|
||||
exports: [WorkflowTriggerWorkspaceService],
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { buildCreatedByFromWorkspaceMember } from 'src/engine/core-modules/actor/utils/build-created-by-from-workspace-member.util';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { WorkflowEventListenerWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-event-listener.workspace-entity';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
import {
|
||||
WorkflowDatabaseEventTrigger,
|
||||
WorkflowTriggerType,
|
||||
} from 'src/modules/workflow/common/types/workflow-trigger.type';
|
||||
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 { WorkflowTriggerType } from 'src/modules/workflow/common/types/workflow-trigger.type';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workflow-common.workspace-service';
|
||||
import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-runner.workspace-service';
|
||||
import { assertWorkflowVersionIsValid } from 'src/modules/workflow/workflow-trigger/utils/assert-workflow-version-is-valid';
|
||||
import { DatabaseEventTriggerService } from 'src/modules/workflow/workflow-trigger/database-event-trigger/database-event-trigger.service';
|
||||
import { assertVersionCanBeActivated } from 'src/modules/workflow/workflow-trigger/utils/assert-version-can-be-activated.util';
|
||||
import {
|
||||
WorkflowTriggerException,
|
||||
WorkflowTriggerExceptionCode,
|
||||
@ -25,6 +28,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly workflowRunnerWorkspaceService: WorkflowRunnerWorkspaceService,
|
||||
private readonly databaseEventTriggerService: DatabaseEventTriggerService,
|
||||
) {}
|
||||
|
||||
async runWorkflowVersion(
|
||||
@ -42,17 +46,9 @@ export class WorkflowTriggerWorkspaceService {
|
||||
);
|
||||
}
|
||||
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersion(
|
||||
workflowVersionId,
|
||||
);
|
||||
|
||||
if (!workflowVersion) {
|
||||
throw new WorkflowTriggerException(
|
||||
'No workflow version found',
|
||||
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION,
|
||||
);
|
||||
}
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail(
|
||||
workflowVersionId,
|
||||
);
|
||||
|
||||
return await this.workflowRunnerWorkspaceService.run(
|
||||
workspaceId,
|
||||
@ -64,72 +60,178 @@ export class WorkflowTriggerWorkspaceService {
|
||||
|
||||
async enableWorkflowTrigger(workflowVersionId: string) {
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersion(
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail(
|
||||
workflowVersionId,
|
||||
);
|
||||
|
||||
assertWorkflowVersionIsValid(workflowVersion);
|
||||
|
||||
switch (workflowVersion.trigger.type) {
|
||||
case WorkflowTriggerType.DATABASE_EVENT:
|
||||
await this.upsertEventListenerAndPublishVersion(
|
||||
workflowVersion.workflowId,
|
||||
workflowVersionId,
|
||||
workflowVersion.trigger,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async upsertEventListenerAndPublishVersion(
|
||||
workflowId: string,
|
||||
workflowVersionId: string,
|
||||
trigger: WorkflowDatabaseEventTrigger,
|
||||
) {
|
||||
const eventName = trigger.settings.eventName;
|
||||
|
||||
const workflowEventListenerRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowEventListenerWorkspaceEntity>(
|
||||
'workflowEventListener',
|
||||
);
|
||||
|
||||
const workflowEventListener = await workflowEventListenerRepository.create({
|
||||
workflowId,
|
||||
eventName,
|
||||
});
|
||||
|
||||
const workspaceDataSource = await this.twentyORMManager.getDatasource();
|
||||
|
||||
const workflowRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowWorkspaceEntity>(
|
||||
'workflow',
|
||||
);
|
||||
|
||||
await workspaceDataSource?.transaction(async (transactionManager) => {
|
||||
// TODO: Use upsert when available for workspace entities
|
||||
await workflowEventListenerRepository.delete(
|
||||
{
|
||||
workflowId,
|
||||
eventName,
|
||||
},
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
await workflowEventListenerRepository.save(
|
||||
workflowEventListener,
|
||||
{},
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
await workflowRepository.update(
|
||||
{ id: workflowId },
|
||||
{ publishedVersionId: workflowVersionId },
|
||||
transactionManager,
|
||||
);
|
||||
const workflow = await workflowRepository.findOne({
|
||||
where: { id: workflowVersion.workflowId },
|
||||
});
|
||||
|
||||
if (!workflow) {
|
||||
throw new WorkflowTriggerException(
|
||||
'No workflow found',
|
||||
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION,
|
||||
);
|
||||
}
|
||||
|
||||
assertVersionCanBeActivated(workflowVersion, workflow);
|
||||
|
||||
const workspaceDataSource = await this.twentyORMManager.getDatasource();
|
||||
const queryRunner = workspaceDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
const manager = queryRunner.manager;
|
||||
|
||||
try {
|
||||
if (
|
||||
workflow.lastPublishedVersionId &&
|
||||
workflowVersionId !== workflow.lastPublishedVersionId
|
||||
) {
|
||||
await this.disableWorkflowTriggerWithManager(
|
||||
workflow.lastPublishedVersionId,
|
||||
manager,
|
||||
);
|
||||
}
|
||||
|
||||
await this.activateWorkflowVersion(
|
||||
workflowVersion.workflowId,
|
||||
workflowVersionId,
|
||||
manager,
|
||||
);
|
||||
await workflowRepository.update(
|
||||
{ id: workflow.id },
|
||||
{ lastPublishedVersionId: workflowVersionId },
|
||||
manager,
|
||||
);
|
||||
|
||||
switch (workflowVersion.trigger.type) {
|
||||
case WorkflowTriggerType.DATABASE_EVENT:
|
||||
await this.databaseEventTriggerService.createEventListener(
|
||||
workflowVersion.workflowId,
|
||||
workflowVersion.trigger,
|
||||
manager,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
async disableWorkflowTrigger(workflowVersionId: string) {
|
||||
const workspaceDataSource = await this.twentyORMManager.getDatasource();
|
||||
const queryRunner = workspaceDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
await this.disableWorkflowTriggerWithManager(
|
||||
workflowVersionId,
|
||||
queryRunner.manager,
|
||||
);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
private async disableWorkflowTriggerWithManager(
|
||||
workflowVersionId: string,
|
||||
manager: EntityManager,
|
||||
) {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
'workflowVersion',
|
||||
);
|
||||
|
||||
const workflowVersion = await workflowVersionRepository.findOne({
|
||||
where: { id: workflowVersionId },
|
||||
});
|
||||
|
||||
if (!workflowVersion) {
|
||||
throw new WorkflowTriggerException(
|
||||
'No workflow version found',
|
||||
WorkflowTriggerExceptionCode.INVALID_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
if (workflowVersion.status !== WorkflowVersionStatus.ACTIVE) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Cannot disable non-active workflow version',
|
||||
WorkflowTriggerExceptionCode.INVALID_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflowVersionId },
|
||||
{ status: WorkflowVersionStatus.DEACTIVATED },
|
||||
manager,
|
||||
);
|
||||
|
||||
switch (workflowVersion?.trigger?.type) {
|
||||
case WorkflowTriggerType.DATABASE_EVENT:
|
||||
await this.databaseEventTriggerService.deleteEventListener(
|
||||
workflowVersion.workflowId,
|
||||
manager,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async activateWorkflowVersion(
|
||||
workflowId: string,
|
||||
workflowVersionId: string,
|
||||
manager: EntityManager,
|
||||
) {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
'workflowVersion',
|
||||
);
|
||||
|
||||
const activeWorkflowVersions = await workflowVersionRepository.find(
|
||||
{
|
||||
where: { workflowId, status: WorkflowVersionStatus.ACTIVE },
|
||||
},
|
||||
manager,
|
||||
);
|
||||
|
||||
if (activeWorkflowVersions.length > 0) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Cannot have more than one active workflow version',
|
||||
WorkflowTriggerExceptionCode.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflowVersionId },
|
||||
{ status: WorkflowVersionStatus.ACTIVE },
|
||||
manager,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user