Delete filter on branch deletion (#13834)
When the target is not in the direct next step ids of the source (step or trigger), check if there is a filter between both. If yes, delete the filter and remove it from source next step ids. Also refactored the existing.
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { type WorkflowRunStepStatus } from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
export const getIsInputTabDisabled = ({
|
||||
stepExecutionStatus,
|
||||
|
||||
+1
-1
@@ -10,10 +10,10 @@ import { RightDrawerStepListContainer } from '@/workflow/workflow-steps/componen
|
||||
import { RightDrawerWorkflowSelectStepTitle } from '@/workflow/workflow-steps/components/RightDrawerWorkflowSelectStepTitle';
|
||||
import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
|
||||
import { OTHER_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/OtherTriggerTypes';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger';
|
||||
import { getTriggerDefaultDefinition } from '@/workflow/workflow-trigger/utils/getTriggerDefaultDefinition';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
@@ -2,7 +2,6 @@ import { stepsOutputSchemaFamilyState } from '@/workflow/states/stepsOutputSchem
|
||||
import { type WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
import { getStepOutputSchemaFamilyStateKey } from '@/workflow/utils/getStepOutputSchemaFamilyStateKey';
|
||||
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerDefaultLabel';
|
||||
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
|
||||
import {
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
} from '@/workflow/workflow-variables/types/StepOutputSchema';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
export const useStepsOutputSchema = () => {
|
||||
const populateStepsOutputSchema = useRecoilCallback(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type WorkflowStep } from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { findStepPosition } from '../findStepPosition';
|
||||
|
||||
describe('findStepPosition', () => {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import {
|
||||
type WorkflowAction,
|
||||
type WorkflowTrigger,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { getStepDefinitionOrThrow } from '../getStepDefinitionOrThrow';
|
||||
|
||||
describe('getStepDefinitionOrThrow', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type WorkflowStep } from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
/**
|
||||
* This function returns the reference of the array where the step should be positioned
|
||||
|
||||
@@ -3,8 +3,8 @@ import {
|
||||
type WorkflowTrigger,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { findStepPosition } from '@/workflow/utils/findStepPosition';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
export const getStepDefinitionOrThrow = ({
|
||||
stepId,
|
||||
|
||||
+1
-1
@@ -7,12 +7,12 @@ import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/wo
|
||||
import { workflowVisualizerWorkflowVersionIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowVersionIdComponentState';
|
||||
import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase';
|
||||
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useContext } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
const StyledStepNodeLabelIconContainer = styled.div`
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { type WorkflowDiagramEmptyTriggerNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { type Node } from '@xyflow/react';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
export const WORKFLOW_DIAGRAM_EMPTY_TRIGGER_NODE_DEFINITION = {
|
||||
id: TRIGGER_STEP_ID,
|
||||
|
||||
+1
-1
@@ -15,9 +15,9 @@ import {
|
||||
import { getWorkflowDiagramTriggerNode } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramTriggerNode';
|
||||
|
||||
import { WORKFLOW_DIAGRAM_EMPTY_TRIGGER_NODE_DEFINITION } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEmptyTriggerNodeDefinition';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { getRootStepIds } from '@/workflow/workflow-trigger/utils/getRootStepIds';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const generateWorkflowDiagram = ({
|
||||
|
||||
+1
-1
@@ -2,10 +2,10 @@ import { type WorkflowTrigger } from '@/workflow/types/Workflow';
|
||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
||||
import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
|
||||
import { type Node } from '@xyflow/react';
|
||||
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
export const getWorkflowDiagramTriggerNode = ({
|
||||
trigger,
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { type WorkflowStep } from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { getWorkflowPreviousStepId } from '../getWorkflowPreviousStepId';
|
||||
|
||||
describe('getWorkflowPreviousStepId', () => {
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { type WorkflowRunFlow } from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { StepStatus, TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { getWorkflowRunStepContext } from '../getWorkflowRunStepContext';
|
||||
import { StepStatus } from 'twenty-shared/workflow';
|
||||
|
||||
describe('getWorkflowRunStepContext', () => {
|
||||
it('should return an empty array for trigger step', () => {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { type WorkflowStep } from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
export const getWorkflowPreviousStepId = ({
|
||||
stepId,
|
||||
|
||||
+1
-1
@@ -1,8 +1,8 @@
|
||||
import { type WorkflowRunFlow } from '@/workflow/types/Workflow';
|
||||
import { getPreviousSteps } from '@/workflow/workflow-steps/utils/getWorkflowPreviousSteps';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import {
|
||||
getWorkflowRunContext,
|
||||
TRIGGER_STEP_ID,
|
||||
type WorkflowRunStepInfos,
|
||||
} from 'twenty-shared/workflow';
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export const TRIGGER_STEP_ID = 'trigger';
|
||||
+1
-1
@@ -4,7 +4,6 @@ import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/ste
|
||||
import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema';
|
||||
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
|
||||
import { getPreviousSteps } from '@/workflow/workflow-steps/utils/getWorkflowPreviousSteps';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import {
|
||||
type OutputSchema,
|
||||
type StepOutputSchema,
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
import { filterOutputSchema } from '@/workflow/workflow-variables/utils/filterOutputSchema';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { isEmptyObject } from '~/utils/isEmptyObject';
|
||||
|
||||
export const useAvailableVariablesInWorkflowStep = ({
|
||||
|
||||
+233
-8
@@ -1,14 +1,17 @@
|
||||
import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service';
|
||||
import {
|
||||
type WorkflowAction,
|
||||
WorkflowActionType,
|
||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
import { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service';
|
||||
|
||||
type MockWorkspaceRepository = Partial<
|
||||
WorkspaceRepository<WorkflowVersionWorkspaceEntity>
|
||||
@@ -71,6 +74,7 @@ const mockWorkflowVersion = {
|
||||
|
||||
describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
let twentyORMGlobalManager: jest.Mocked<TwentyORMGlobalManager>;
|
||||
let workflowCommonWorkspaceService: jest.Mocked<WorkflowCommonWorkspaceService>;
|
||||
let service: WorkflowVersionEdgeWorkspaceService;
|
||||
let mockWorkflowVersionWorkspaceRepository: MockWorkspaceRepository;
|
||||
|
||||
@@ -90,6 +94,12 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
.mockResolvedValue(mockWorkflowVersionWorkspaceRepository),
|
||||
} as unknown as jest.Mocked<TwentyORMGlobalManager>;
|
||||
|
||||
workflowCommonWorkspaceService = {
|
||||
getWorkflowVersionOrFail: jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockWorkflowVersion),
|
||||
} as unknown as jest.Mocked<WorkflowCommonWorkspaceService>;
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
WorkflowVersionEdgeWorkspaceService,
|
||||
@@ -97,6 +107,10 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: twentyORMGlobalManager,
|
||||
},
|
||||
{
|
||||
provide: WorkflowCommonWorkspaceService,
|
||||
useValue: workflowCommonWorkspaceService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
@@ -107,7 +121,7 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
it('should throw if target does not exists', async () => {
|
||||
const call = async () =>
|
||||
await service.createWorkflowVersionEdge({
|
||||
source: 'trigger',
|
||||
source: TRIGGER_STEP_ID,
|
||||
target: 'not-existing-step',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
@@ -121,12 +135,19 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
describe('with source is the trigger', () => {
|
||||
it('should create an edge between trigger and step-1', async () => {
|
||||
const result = await service.createWorkflowVersionEdge({
|
||||
source: 'trigger',
|
||||
source: TRIGGER_STEP_ID,
|
||||
target: 'step-3',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
workflowCommonWorkspaceService.getWorkflowVersionOrFail,
|
||||
).toHaveBeenCalledWith({
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
mockWorkflowVersionWorkspaceRepository.update,
|
||||
).toHaveBeenCalledWith(mockWorkflowVersionId, {
|
||||
@@ -148,7 +169,7 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
|
||||
it('should not duplicate stepIds if edge already exists', async () => {
|
||||
const result = await service.createWorkflowVersionEdge({
|
||||
source: 'trigger',
|
||||
source: TRIGGER_STEP_ID,
|
||||
target: 'step-1',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
@@ -245,7 +266,7 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
it('should throw if target does not exists', async () => {
|
||||
const call = async () =>
|
||||
await service.deleteWorkflowVersionEdge({
|
||||
source: 'trigger',
|
||||
source: TRIGGER_STEP_ID,
|
||||
target: 'not-existing-step',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
@@ -259,12 +280,19 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
describe('with source is the trigger', () => {
|
||||
it('should delete an edge between trigger and step-1', async () => {
|
||||
const result = await service.deleteWorkflowVersionEdge({
|
||||
source: 'trigger',
|
||||
source: TRIGGER_STEP_ID,
|
||||
target: 'step-1',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
workflowCommonWorkspaceService.getWorkflowVersionOrFail,
|
||||
).toHaveBeenCalledWith({
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
mockWorkflowVersionWorkspaceRepository.update,
|
||||
).toHaveBeenCalledWith(mockWorkflowVersionId, {
|
||||
@@ -286,12 +314,19 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
|
||||
it('should not delete if edge does not exists', async () => {
|
||||
const result = await service.deleteWorkflowVersionEdge({
|
||||
source: 'trigger',
|
||||
source: TRIGGER_STEP_ID,
|
||||
target: 'step-2',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
workflowCommonWorkspaceService.getWorkflowVersionOrFail,
|
||||
).toHaveBeenCalledWith({
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
mockWorkflowVersionWorkspaceRepository.update,
|
||||
).not.toHaveBeenCalled();
|
||||
@@ -377,5 +412,195 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with filter steps', () => {
|
||||
it('should delete the filter step when deleting edge from trigger to target through filter', async () => {
|
||||
const mockStepsWithFilter = [
|
||||
{
|
||||
id: 'step-1',
|
||||
type: WorkflowActionType.FORM,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
nextStepIds: ['step-2'],
|
||||
},
|
||||
{
|
||||
id: 'step-2',
|
||||
type: WorkflowActionType.SEND_EMAIL,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
nextStepIds: [],
|
||||
},
|
||||
{
|
||||
id: 'filter-step',
|
||||
type: WorkflowActionType.FILTER,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
nextStepIds: ['step-2'],
|
||||
},
|
||||
] as WorkflowAction[];
|
||||
|
||||
const mockTriggerWithFilter = {
|
||||
type: WorkflowTriggerType.MANUAL,
|
||||
settings: {},
|
||||
nextStepIds: ['step-1', 'filter-step'],
|
||||
};
|
||||
|
||||
const mockWorkflowVersionWithFilter = {
|
||||
id: mockWorkflowVersionId,
|
||||
trigger: mockTriggerWithFilter,
|
||||
steps: mockStepsWithFilter,
|
||||
status: 'DRAFT',
|
||||
} as WorkflowVersionWorkspaceEntity;
|
||||
|
||||
workflowCommonWorkspaceService.getWorkflowVersionOrFail.mockResolvedValue(
|
||||
mockWorkflowVersionWithFilter,
|
||||
);
|
||||
|
||||
const result = await service.deleteWorkflowVersionEdge({
|
||||
source: TRIGGER_STEP_ID,
|
||||
target: 'step-2',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
workflowCommonWorkspaceService.getWorkflowVersionOrFail,
|
||||
).toHaveBeenCalledWith({
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
mockWorkflowVersionWorkspaceRepository.update,
|
||||
).toHaveBeenCalledWith(mockWorkflowVersionId, {
|
||||
trigger: {
|
||||
...mockTriggerWithFilter,
|
||||
nextStepIds: ['step-1'],
|
||||
},
|
||||
steps: mockStepsWithFilter.filter(
|
||||
(step) => step.id !== 'filter-step',
|
||||
),
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
},
|
||||
});
|
||||
});
|
||||
it('should delete the filter step when deleting edge from step to target through filter', async () => {
|
||||
const mockStepsWithFilter = [
|
||||
{
|
||||
id: 'step-1',
|
||||
type: WorkflowActionType.FORM,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
nextStepIds: ['step-2', 'filter-step'],
|
||||
},
|
||||
{
|
||||
id: 'step-2',
|
||||
type: WorkflowActionType.SEND_EMAIL,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
nextStepIds: [],
|
||||
},
|
||||
{
|
||||
id: 'step-3',
|
||||
type: WorkflowActionType.SEND_EMAIL,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
nextStepIds: [],
|
||||
},
|
||||
{
|
||||
id: 'filter-step',
|
||||
type: WorkflowActionType.FILTER,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
nextStepIds: ['step-3'],
|
||||
},
|
||||
] as WorkflowAction[];
|
||||
|
||||
const mockWorkflowVersionWithFilter = {
|
||||
id: mockWorkflowVersionId,
|
||||
trigger: mockTrigger,
|
||||
steps: mockStepsWithFilter,
|
||||
status: 'DRAFT',
|
||||
} as WorkflowVersionWorkspaceEntity;
|
||||
|
||||
workflowCommonWorkspaceService.getWorkflowVersionOrFail.mockResolvedValue(
|
||||
mockWorkflowVersionWithFilter,
|
||||
);
|
||||
|
||||
const result = await service.deleteWorkflowVersionEdge({
|
||||
source: 'step-1',
|
||||
target: 'step-3',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
workflowCommonWorkspaceService.getWorkflowVersionOrFail,
|
||||
).toHaveBeenCalledWith({
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
mockWorkflowVersionWorkspaceRepository.update,
|
||||
).toHaveBeenCalledWith(mockWorkflowVersionId, {
|
||||
steps: mockStepsWithFilter
|
||||
.map((step) => {
|
||||
if (step.id === 'step-1') {
|
||||
return {
|
||||
...step,
|
||||
nextStepIds: ['step-2'],
|
||||
};
|
||||
}
|
||||
|
||||
return step;
|
||||
})
|
||||
.filter((step) => step.id !== 'filter-step'),
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+2
@@ -1,8 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service';
|
||||
|
||||
@Module({
|
||||
imports: [WorkflowCommonModule],
|
||||
providers: [WorkflowVersionEdgeWorkspaceService],
|
||||
exports: [WorkflowVersionEdgeWorkspaceService],
|
||||
})
|
||||
|
||||
+328
-113
@@ -1,21 +1,30 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { type WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto';
|
||||
import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
WorkflowVersionEdgeException,
|
||||
WorkflowVersionEdgeExceptionCode,
|
||||
} from 'src/modules/workflow/common/exceptions/workflow-version-edge.exception';
|
||||
import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { assertWorkflowVersionIsDraft } from 'src/modules/workflow/common/utils/assert-workflow-version-is-draft.util';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
type WorkflowAction,
|
||||
WorkflowActionType,
|
||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { type WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowVersionEdgeWorkspaceService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
) {}
|
||||
|
||||
async createWorkflowVersionEdge({
|
||||
@@ -36,26 +45,16 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const workflowVersion = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
id: workflowVersionId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(workflowVersion)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
'WorkflowVersion not found',
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const steps = workflowVersion.steps || [];
|
||||
|
||||
const trigger = workflowVersion.trigger;
|
||||
|
||||
const isSourceTrigger = source === 'trigger';
|
||||
const steps = workflowVersion.steps || [];
|
||||
|
||||
const targetStep = steps.find((step) => step.id === target);
|
||||
|
||||
@@ -66,41 +65,150 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
);
|
||||
}
|
||||
|
||||
const isSourceTrigger = source === TRIGGER_STEP_ID;
|
||||
|
||||
if (isSourceTrigger) {
|
||||
if (!isDefined(trigger)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Trigger not found in workflowVersion '${workflowVersionId}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return this.createTriggerEdge({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
} else {
|
||||
return this.createStepEdge({
|
||||
trigger,
|
||||
steps,
|
||||
source,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (trigger.nextStepIds?.includes(target)) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
async deleteWorkflowVersionEdge({
|
||||
source,
|
||||
target,
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
}: {
|
||||
source: string;
|
||||
target: string;
|
||||
workflowVersionId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const updatedTrigger = {
|
||||
...trigger,
|
||||
nextStepIds: [...(trigger.nextStepIds ?? []), target],
|
||||
};
|
||||
|
||||
await workflowVersionRepository.update(workflowVersion.id, {
|
||||
trigger: updatedTrigger,
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const trigger = workflowVersion.trigger;
|
||||
const steps = workflowVersion.steps || [];
|
||||
|
||||
const targetStep = steps.find((step) => step.id === target);
|
||||
|
||||
if (!isDefined(targetStep)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Target step '${target}' not found in workflowVersion '${workflowVersionId}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const isSourceTrigger = source === TRIGGER_STEP_ID;
|
||||
|
||||
if (isSourceTrigger) {
|
||||
return this.deleteTriggerEdge({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
} else {
|
||||
return this.deleteStepEdge({
|
||||
trigger,
|
||||
steps,
|
||||
source,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async createTriggerEdge({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
}: {
|
||||
trigger: WorkflowTrigger | null;
|
||||
steps: WorkflowAction[];
|
||||
target: string;
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity;
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
if (!isDefined(trigger)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Trigger not found in workflowVersion '${workflowVersion.id}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (trigger.nextStepIds?.includes(target)) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger: updatedTrigger,
|
||||
trigger,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedTrigger = {
|
||||
...trigger,
|
||||
nextStepIds: [...(trigger.nextStepIds ?? []), target],
|
||||
};
|
||||
|
||||
await workflowVersionRepository.update(workflowVersion.id, {
|
||||
trigger: updatedTrigger,
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger: updatedTrigger,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
|
||||
private async createStepEdge({
|
||||
trigger,
|
||||
steps,
|
||||
source,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
}: {
|
||||
trigger: WorkflowTrigger | null;
|
||||
steps: WorkflowAction[];
|
||||
source: string;
|
||||
target: string;
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity;
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
const sourceStep = steps.find((step) => step.id === source);
|
||||
|
||||
if (!isDefined(sourceStep)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Source step '${source}' not found in workflowVersion '${workflowVersionId}'`,
|
||||
`Source step '${source}' not found in workflowVersion '${workflowVersion.id}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
@@ -135,106 +243,92 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
});
|
||||
}
|
||||
|
||||
async deleteWorkflowVersionEdge({
|
||||
source,
|
||||
private async deleteTriggerEdge({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
}: {
|
||||
source: string;
|
||||
trigger: WorkflowTrigger | null;
|
||||
steps: WorkflowAction[];
|
||||
target: string;
|
||||
workflowVersionId: string;
|
||||
workspaceId: string;
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity;
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
if (!isDefined(trigger)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Trigger not found in workflowVersion '${workflowVersion.id}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const workflowVersion = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
id: workflowVersionId,
|
||||
},
|
||||
if (!trigger.nextStepIds?.includes(target)) {
|
||||
return this.handleFilterBetweenTriggerAndTarget({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersionId: workflowVersion.id,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedTrigger = {
|
||||
...trigger,
|
||||
nextStepIds: trigger.nextStepIds?.filter(
|
||||
(nextStepId: string) => nextStepId !== target,
|
||||
),
|
||||
};
|
||||
|
||||
await workflowVersionRepository.update(workflowVersion.id, {
|
||||
trigger: updatedTrigger,
|
||||
});
|
||||
|
||||
if (!isDefined(workflowVersion)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
'WorkflowVersion not found',
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const steps = workflowVersion.steps || [];
|
||||
|
||||
const trigger = workflowVersion.trigger;
|
||||
|
||||
const isSourceTrigger = source === 'trigger';
|
||||
|
||||
const targetStep = steps.find((step) => step.id === target);
|
||||
|
||||
if (!isDefined(targetStep)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Target step '${target}' not found in workflowVersion '${workflowVersionId}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (isSourceTrigger) {
|
||||
if (!isDefined(trigger)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Trigger not found in workflowVersion '${workflowVersionId}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (!trigger.nextStepIds?.includes(target)) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedTrigger = {
|
||||
...trigger,
|
||||
nextStepIds: trigger.nextStepIds?.filter(
|
||||
(nextStepId) => nextStepId !== target,
|
||||
),
|
||||
};
|
||||
|
||||
await workflowVersionRepository.update(workflowVersion.id, {
|
||||
trigger: updatedTrigger,
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger: updatedTrigger,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger: updatedTrigger,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
|
||||
private async deleteStepEdge({
|
||||
trigger,
|
||||
steps,
|
||||
source,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
}: {
|
||||
trigger: WorkflowTrigger | null;
|
||||
steps: WorkflowAction[];
|
||||
source: string;
|
||||
target: string;
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity;
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
const sourceStep = steps.find((step) => step.id === source);
|
||||
|
||||
if (!isDefined(sourceStep)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Source step '${source}' not found in workflowVersion '${workflowVersionId}'`,
|
||||
`Source step '${source}' not found in workflowVersion '${workflowVersion.id}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (!sourceStep.nextStepIds?.includes(target)) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
return await this.handleFilterBetweenSourceAndTarget({
|
||||
trigger,
|
||||
steps,
|
||||
sourceStep,
|
||||
target,
|
||||
workflowVersionId: workflowVersion.id,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedSourceStep = {
|
||||
...sourceStep,
|
||||
nextStepIds: sourceStep.nextStepIds?.filter(
|
||||
(nextStepId) => nextStepId !== target,
|
||||
(nextStepId: string) => nextStepId !== target,
|
||||
),
|
||||
};
|
||||
|
||||
@@ -255,4 +349,125 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
steps: updatedSteps,
|
||||
});
|
||||
}
|
||||
|
||||
private async handleFilterBetweenTriggerAndTarget({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersionId,
|
||||
workflowVersionRepository,
|
||||
}: {
|
||||
trigger: WorkflowTrigger;
|
||||
steps: WorkflowAction[];
|
||||
target: string;
|
||||
workflowVersionId: string;
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
const filterBetweenTriggerAndTarget = this.findFilterBetweenNodes({
|
||||
steps,
|
||||
sourceNextStepIds: trigger.nextStepIds,
|
||||
target,
|
||||
});
|
||||
|
||||
if (!isDefined(filterBetweenTriggerAndTarget)) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedTrigger = {
|
||||
...trigger,
|
||||
nextStepIds: trigger.nextStepIds?.filter(
|
||||
(nextStepId: string) => nextStepId !== filterBetweenTriggerAndTarget.id,
|
||||
),
|
||||
};
|
||||
|
||||
const updatedSteps = steps.filter(
|
||||
(step) => step.id !== filterBetweenTriggerAndTarget.id,
|
||||
);
|
||||
|
||||
await workflowVersionRepository.update(workflowVersionId, {
|
||||
trigger: updatedTrigger,
|
||||
steps: updatedSteps,
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger: updatedTrigger,
|
||||
steps: updatedSteps,
|
||||
});
|
||||
}
|
||||
|
||||
private async handleFilterBetweenSourceAndTarget({
|
||||
trigger,
|
||||
steps,
|
||||
sourceStep,
|
||||
target,
|
||||
workflowVersionRepository,
|
||||
workflowVersionId,
|
||||
}: {
|
||||
trigger: WorkflowTrigger | null;
|
||||
steps: WorkflowAction[];
|
||||
sourceStep: WorkflowAction;
|
||||
target: string;
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>;
|
||||
workflowVersionId: string;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
const filterBetweenSourceAndTarget = this.findFilterBetweenNodes({
|
||||
steps,
|
||||
sourceNextStepIds: sourceStep.nextStepIds,
|
||||
target,
|
||||
});
|
||||
|
||||
if (!isDefined(filterBetweenSourceAndTarget)) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedSourceStep = {
|
||||
...sourceStep,
|
||||
nextStepIds: sourceStep.nextStepIds?.filter(
|
||||
(nextStepId: string) => nextStepId !== filterBetweenSourceAndTarget.id,
|
||||
),
|
||||
};
|
||||
|
||||
const updatedSteps = steps
|
||||
.map((step) => {
|
||||
if (step.id === sourceStep.id) {
|
||||
return updatedSourceStep;
|
||||
}
|
||||
|
||||
return step;
|
||||
})
|
||||
.filter((step) => step.id !== filterBetweenSourceAndTarget.id);
|
||||
|
||||
await workflowVersionRepository.update(workflowVersionId, {
|
||||
steps: updatedSteps,
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps: updatedSteps,
|
||||
});
|
||||
}
|
||||
|
||||
private findFilterBetweenNodes({
|
||||
steps,
|
||||
sourceNextStepIds,
|
||||
target,
|
||||
}: {
|
||||
steps: WorkflowAction[];
|
||||
sourceNextStepIds: string[] | undefined;
|
||||
target: string;
|
||||
}) {
|
||||
const nextStepFilters = steps.filter(
|
||||
(step) =>
|
||||
sourceNextStepIds?.includes(step.id) &&
|
||||
step.type === WorkflowActionType.FILTER,
|
||||
);
|
||||
|
||||
return nextStepFilters.find((step) => step.nextStepIds?.includes(target));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -1,6 +1,8 @@
|
||||
import { Test, type TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { AgentService } from 'src/engine/metadata-modules/agent/agent.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
@@ -135,7 +137,7 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
const result = await service.createWorkflowVersionStep({
|
||||
input: {
|
||||
stepType: WorkflowActionType.FORM,
|
||||
parentStepId: 'trigger',
|
||||
parentStepId: TRIGGER_STEP_ID,
|
||||
nextStepId: undefined,
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
},
|
||||
@@ -160,7 +162,7 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
const result = await service.createWorkflowVersionStep({
|
||||
input: {
|
||||
stepType: WorkflowActionType.FORM,
|
||||
parentStepId: 'trigger',
|
||||
parentStepId: TRIGGER_STEP_ID,
|
||||
nextStepId: 'step-1',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
},
|
||||
@@ -259,7 +261,7 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
|
||||
it('should delete trigger', async () => {
|
||||
const result = await service.deleteWorkflowVersionStep({
|
||||
stepIdToDelete: 'trigger',
|
||||
stepIdToDelete: TRIGGER_STEP_ID,
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
@@ -277,7 +279,7 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
deletedStepIds: ['trigger'],
|
||||
deletedStepIds: [TRIGGER_STEP_ID],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { insertStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/insert-step';
|
||||
import {
|
||||
type WorkflowAction,
|
||||
@@ -146,7 +148,7 @@ describe('insertStep', () => {
|
||||
existingTrigger,
|
||||
existingSteps: [step1],
|
||||
insertedStep: newStep,
|
||||
parentStepId: 'trigger',
|
||||
parentStepId: TRIGGER_STEP_ID,
|
||||
nextStepId: undefined,
|
||||
});
|
||||
|
||||
|
||||
+4
-2
@@ -1,3 +1,5 @@
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { removeStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step';
|
||||
import {
|
||||
type WorkflowAction,
|
||||
@@ -187,7 +189,7 @@ describe('removeStep', () => {
|
||||
const result = removeStep({
|
||||
existingTrigger: mockTrigger,
|
||||
existingSteps: [step1, step2, step3],
|
||||
stepIdToDelete: 'trigger',
|
||||
stepIdToDelete: TRIGGER_STEP_ID,
|
||||
stepToDeleteChildrenIds: ['1'],
|
||||
});
|
||||
|
||||
@@ -247,7 +249,7 @@ describe('removeStep', () => {
|
||||
const result = removeStep({
|
||||
existingTrigger: { ...mockTrigger, nextStepIds: [] },
|
||||
existingSteps: null,
|
||||
stepIdToDelete: 'trigger',
|
||||
stepIdToDelete: TRIGGER_STEP_ID,
|
||||
});
|
||||
|
||||
expect(result.updatedTrigger).toEqual(null);
|
||||
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { type WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
|
||||
@@ -22,7 +24,7 @@ export const insertStep = ({
|
||||
|
||||
let updatedExistingSteps = existingSteps;
|
||||
|
||||
if (parentStepId === 'trigger') {
|
||||
if (parentStepId === TRIGGER_STEP_ID) {
|
||||
if (!existingTrigger) {
|
||||
throw new Error('Cannot insert step from undefined trigger');
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import {
|
||||
type WorkflowAction,
|
||||
@@ -181,7 +182,7 @@ const removeTrigger = ({
|
||||
return {
|
||||
updatedSteps,
|
||||
updatedTrigger: null,
|
||||
removedStepIds: ['trigger', ...stepIdsToRemove],
|
||||
removedStepIds: [TRIGGER_STEP_ID, ...stepIdsToRemove],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -196,7 +197,7 @@ export const removeStep = ({
|
||||
stepIdToDelete: string;
|
||||
stepToDeleteChildrenIds?: string[];
|
||||
}) => {
|
||||
if (stepIdToDelete === 'trigger') {
|
||||
if (stepIdToDelete === TRIGGER_STEP_ID) {
|
||||
return removeTrigger({
|
||||
existingSteps,
|
||||
triggerChildrenIds: stepToDeleteChildrenIds,
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
||||
import { StepStatus } from 'twenty-shared/workflow';
|
||||
import { StepStatus, TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
@@ -221,7 +221,7 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
const existingTrigger = workflowVersion.trigger;
|
||||
|
||||
const isDeletingTrigger =
|
||||
stepIdToDelete === 'trigger' && isDefined(existingTrigger);
|
||||
stepIdToDelete === TRIGGER_STEP_ID && isDefined(existingTrigger);
|
||||
|
||||
if (!isDeletingTrigger && !isDefined(workflowVersion.steps)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
|
||||
+3
-2
@@ -1,8 +1,10 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { type WorkflowStepPositionUpdateInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position-update-input.dto';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
WorkflowVersionStepException,
|
||||
@@ -17,7 +19,6 @@ import { assertWorkflowVersionIsDraft } from 'src/modules/workflow/common/utils/
|
||||
import { assertWorkflowVersionTriggerIsDefined } from 'src/modules/workflow/common/utils/assert-workflow-version-trigger-is-defined.util';
|
||||
import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service';
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { type WorkflowStepPositionUpdateInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position-update-input.dto';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowVersionWorkspaceService {
|
||||
@@ -139,7 +140,7 @@ export class WorkflowVersionWorkspaceService {
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const triggerPosition = positions.find(
|
||||
(position) => position.id === 'trigger',
|
||||
(position) => position.id === TRIGGER_STEP_ID,
|
||||
);
|
||||
|
||||
const updatedTrigger =
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const TRIGGER_STEP_ID = 'trigger';
|
||||
@@ -7,6 +7,7 @@
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export { TRIGGER_STEP_ID } from './constants/TriggerStepId';
|
||||
export type {
|
||||
WorkflowRunStepInfo,
|
||||
WorkflowRunStepInfos,
|
||||
|
||||
Reference in New Issue
Block a user