Update workflow nodes configuration (#6861)
- Improve the design of the right drawer - Allow to update the trigger of the workflow: the object and the event listened to - Allow to update the selected serverless function that a code action should execute - Change how we determine which workflow version to display in the visualizer. We fetch the selected workflow's data, including whether it has a draft or a published version. If the workflow has a draft version, it gets displayed; otherwise, we display the last published version. - I used the type `WorkflowWithCurrentVersion` to forward the currently edited workflow with its _current_ version embedded across the app. - I created single-responsibility hooks like `useFindWorkflowWithCurrentVersion`, `useFindShowPageWorkflow`, `useUpdateWorkflowVersionTrigger` or `useUpdateWorkflowVersionStep`. - I updated the types for workflow related objects, like `Workflow` and `WorkflowVersion`. See `packages/twenty-front/src/modules/workflow/types/Workflow.ts`. - This introduced the possibility to have `null` values for triggers and steps. I made the according changes in the codebase and in the tests. - I created a utility function to extract both parts of object-event format (`company.created`): `packages/twenty-front/src/modules/workflow/utils/splitWorkflowTriggerEventName.ts`
This commit is contained in:
committed by
GitHub
parent
c55dfbde6e
commit
a2b1062db6
@ -18,7 +18,7 @@ describe('generateWorkflowDiagram', () => {
|
||||
|
||||
expect(result.nodes[0]).toMatchObject({
|
||||
data: {
|
||||
label: trigger.settings.eventName,
|
||||
label: 'Company is Created',
|
||||
nodeType: 'trigger',
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,79 +0,0 @@
|
||||
import { Workflow } from '@/workflow/types/Workflow';
|
||||
import { getWorkflowLastDiagramVersion } from '../getWorkflowLastDiagramVersion';
|
||||
|
||||
describe('getWorkflowLastDiagramVersion', () => {
|
||||
it('returns an empty diagram if the provided workflow is undefined', () => {
|
||||
const result = getWorkflowLastDiagramVersion(undefined);
|
||||
|
||||
expect(result).toEqual({ nodes: [], edges: [] });
|
||||
});
|
||||
|
||||
it('returns an empty diagram if the provided workflow has no versions', () => {
|
||||
const result = getWorkflowLastDiagramVersion({
|
||||
__typename: 'Workflow',
|
||||
id: 'aa',
|
||||
name: 'aa',
|
||||
publishedVersionId: '',
|
||||
versions: [],
|
||||
});
|
||||
|
||||
expect(result).toEqual({ nodes: [], edges: [] });
|
||||
});
|
||||
|
||||
it('returns the diagram for the last version', () => {
|
||||
const workflow: Workflow = {
|
||||
__typename: 'Workflow',
|
||||
id: 'aa',
|
||||
name: 'aa',
|
||||
publishedVersionId: '',
|
||||
versions: [
|
||||
{
|
||||
__typename: 'WorkflowVersion',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
steps: [],
|
||||
trigger: {
|
||||
settings: { eventName: 'company.created' },
|
||||
type: 'DATABASE_EVENT',
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
},
|
||||
{
|
||||
__typename: 'WorkflowVersion',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
steps: [
|
||||
{
|
||||
id: 'step-1',
|
||||
name: '',
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: true },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
serverlessFunctionId: 'a5434be2-c10b-465c-acec-46492782a997',
|
||||
},
|
||||
type: 'CODE_ACTION',
|
||||
valid: true,
|
||||
},
|
||||
],
|
||||
trigger: {
|
||||
settings: { eventName: 'company.created' },
|
||||
type: 'DATABASE_EVENT',
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = getWorkflowLastDiagramVersion(workflow);
|
||||
|
||||
// Corresponds to the trigger + 1 step
|
||||
expect(result.nodes).toHaveLength(2);
|
||||
expect(result.edges).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,79 @@
|
||||
import { getWorkflowVersionDiagram } from '../getWorkflowVersionDiagram';
|
||||
|
||||
describe('getWorkflowVersionDiagram', () => {
|
||||
it('returns an empty diagram if the provided workflow version', () => {
|
||||
const result = getWorkflowVersionDiagram(undefined);
|
||||
|
||||
expect(result).toEqual({ nodes: [], edges: [] });
|
||||
});
|
||||
|
||||
it('returns an empty diagram if the provided workflow version has no trigger', () => {
|
||||
const result = getWorkflowVersionDiagram({
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
steps: [],
|
||||
trigger: null,
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
});
|
||||
|
||||
expect(result).toEqual({ nodes: [], edges: [] });
|
||||
});
|
||||
|
||||
it('returns an empty diagram if the provided workflow version has no steps', () => {
|
||||
const result = getWorkflowVersionDiagram({
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
steps: null,
|
||||
trigger: {
|
||||
settings: { eventName: 'company.created' },
|
||||
type: 'DATABASE_EVENT',
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
});
|
||||
|
||||
expect(result).toEqual({ nodes: [], edges: [] });
|
||||
});
|
||||
|
||||
it('returns the diagram for the last version', () => {
|
||||
const result = getWorkflowVersionDiagram({
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
steps: [
|
||||
{
|
||||
id: 'step-1',
|
||||
name: '',
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: true },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
serverlessFunctionId: 'a5434be2-c10b-465c-acec-46492782a997',
|
||||
},
|
||||
type: 'CODE_ACTION',
|
||||
valid: true,
|
||||
},
|
||||
],
|
||||
trigger: {
|
||||
settings: { eventName: 'company.created' },
|
||||
type: 'DATABASE_EVENT',
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
});
|
||||
|
||||
// Corresponds to the trigger + 1 step
|
||||
expect(result.nodes).toHaveLength(2);
|
||||
expect(result.edges).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@ -3,8 +3,9 @@ import { insertStep } from '../insertStep';
|
||||
|
||||
describe('insertStep', () => {
|
||||
it('returns a deep copy of the provided steps array instead of mutating it', () => {
|
||||
const workflowVersionInitial: WorkflowVersion = {
|
||||
const workflowVersionInitial = {
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
@ -15,7 +16,7 @@ describe('insertStep', () => {
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
};
|
||||
} satisfies WorkflowVersion;
|
||||
const stepToAdd: WorkflowStep = {
|
||||
id: 'step-1',
|
||||
name: '',
|
||||
@ -40,8 +41,9 @@ describe('insertStep', () => {
|
||||
});
|
||||
|
||||
it('adds the step when the steps array is empty', () => {
|
||||
const workflowVersionInitial: WorkflowVersion = {
|
||||
const workflowVersionInitial = {
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
@ -52,7 +54,7 @@ describe('insertStep', () => {
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
};
|
||||
} satisfies WorkflowVersion;
|
||||
const stepToAdd: WorkflowStep = {
|
||||
id: 'step-1',
|
||||
name: '',
|
||||
@ -78,8 +80,9 @@ describe('insertStep', () => {
|
||||
});
|
||||
|
||||
it('adds the step at the end of a non-empty steps array', () => {
|
||||
const workflowVersionInitial: WorkflowVersion = {
|
||||
const workflowVersionInitial = {
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
@ -117,7 +120,7 @@ describe('insertStep', () => {
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
};
|
||||
} satisfies WorkflowVersion;
|
||||
const stepToAdd: WorkflowStep = {
|
||||
id: 'step-3',
|
||||
name: '',
|
||||
@ -147,8 +150,9 @@ describe('insertStep', () => {
|
||||
});
|
||||
|
||||
it('adds the step in the middle of a non-empty steps array', () => {
|
||||
const workflowVersionInitial: WorkflowVersion = {
|
||||
const workflowVersionInitial = {
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
@ -186,7 +190,7 @@ describe('insertStep', () => {
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
};
|
||||
} satisfies WorkflowVersion;
|
||||
const stepToAdd: WorkflowStep = {
|
||||
id: 'step-3',
|
||||
name: '',
|
||||
|
||||
@ -0,0 +1,127 @@
|
||||
import { WorkflowStep, WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
import { replaceStep } from '../replaceStep';
|
||||
|
||||
describe('replaceStep', () => {
|
||||
it('returns a deep copy of the provided steps array instead of mutating it', () => {
|
||||
const stepToBeReplaced = {
|
||||
id: 'step-1',
|
||||
name: '',
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: true },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
serverlessFunctionId: 'first',
|
||||
},
|
||||
type: 'CODE_ACTION',
|
||||
valid: true,
|
||||
} satisfies WorkflowStep;
|
||||
const workflowVersionInitial = {
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
steps: [stepToBeReplaced],
|
||||
trigger: {
|
||||
settings: { eventName: 'company.created' },
|
||||
type: 'DATABASE_EVENT',
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
} satisfies WorkflowVersion;
|
||||
|
||||
const stepsUpdated = replaceStep({
|
||||
steps: workflowVersionInitial.steps,
|
||||
stepToReplace: {
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: true },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
serverlessFunctionId: 'second',
|
||||
},
|
||||
},
|
||||
stepId: stepToBeReplaced.id,
|
||||
});
|
||||
|
||||
expect(workflowVersionInitial.steps).not.toBe(stepsUpdated);
|
||||
});
|
||||
|
||||
it('replaces a step in a non-empty steps array', () => {
|
||||
const stepToBeReplaced: WorkflowStep = {
|
||||
id: 'step-2',
|
||||
name: '',
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: true },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
serverlessFunctionId: 'a5434be2-c10b-465c-acec-46492782a997',
|
||||
},
|
||||
type: 'CODE_ACTION',
|
||||
valid: true,
|
||||
};
|
||||
const workflowVersionInitial = {
|
||||
__typename: 'WorkflowVersion',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '',
|
||||
id: '1',
|
||||
name: '',
|
||||
steps: [
|
||||
{
|
||||
id: 'step-1',
|
||||
name: '',
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: true },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
serverlessFunctionId: 'a5434be2-c10b-465c-acec-46492782a997',
|
||||
},
|
||||
type: 'CODE_ACTION',
|
||||
valid: true,
|
||||
},
|
||||
stepToBeReplaced,
|
||||
{
|
||||
id: 'step-3',
|
||||
name: '',
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: true },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
serverlessFunctionId: 'a5434be2-c10b-465c-acec-46492782a997',
|
||||
},
|
||||
type: 'CODE_ACTION',
|
||||
valid: true,
|
||||
},
|
||||
],
|
||||
trigger: {
|
||||
settings: { eventName: 'company.created' },
|
||||
type: 'DATABASE_EVENT',
|
||||
},
|
||||
updatedAt: '',
|
||||
workflowId: '',
|
||||
} satisfies WorkflowVersion;
|
||||
|
||||
const updatedStepName = "that's another name";
|
||||
const stepsUpdated = replaceStep({
|
||||
stepId: stepToBeReplaced.id,
|
||||
steps: workflowVersionInitial.steps,
|
||||
stepToReplace: {
|
||||
name: updatedStepName,
|
||||
},
|
||||
});
|
||||
|
||||
const expectedUpdatedSteps: Array<WorkflowStep> = [
|
||||
workflowVersionInitial.steps[0],
|
||||
{
|
||||
...stepToBeReplaced,
|
||||
name: updatedStepName,
|
||||
},
|
||||
workflowVersionInitial.steps[2],
|
||||
];
|
||||
expect(stepsUpdated).toEqual(expectedUpdatedSteps);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,41 @@
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
|
||||
import { WorkflowStep } from '@/workflow/types/Workflow';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
/**
|
||||
* This function returns the reference of the array where the step should be positioned
|
||||
* and at which index.
|
||||
*/
|
||||
export const findStepPositionOrThrow = ({
|
||||
steps,
|
||||
stepId,
|
||||
}: {
|
||||
steps: Array<WorkflowStep>;
|
||||
stepId: string | undefined;
|
||||
}): { steps: Array<WorkflowStep>; index: number } => {
|
||||
if (!isDefined(stepId) || stepId === TRIGGER_STEP_ID) {
|
||||
return {
|
||||
steps,
|
||||
index: 0,
|
||||
};
|
||||
}
|
||||
|
||||
for (const [index, step] of steps.entries()) {
|
||||
if (step.id === stepId) {
|
||||
return {
|
||||
steps,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: When condition will have been implemented, put recursivity here.
|
||||
// if (step.type === "CONDITION") {
|
||||
// return findNodePosition({
|
||||
// workflowSteps: step.conditions,
|
||||
// stepId,
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
throw new Error(`Couldn't locate the step. Unreachable step id: ${stepId}.`);
|
||||
};
|
||||
@ -1,11 +1,14 @@
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
|
||||
import { WorkflowStep, WorkflowTrigger } from '@/workflow/types/Workflow';
|
||||
import {
|
||||
WorkflowDiagram,
|
||||
WorkflowDiagramEdge,
|
||||
WorkflowDiagramNode,
|
||||
} from '@/workflow/types/WorkflowDiagram';
|
||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
||||
import { MarkerType } from '@xyflow/react';
|
||||
import { v4 } from 'uuid';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const generateWorkflowDiagram = ({
|
||||
trigger,
|
||||
@ -58,12 +61,15 @@ export const generateWorkflowDiagram = ({
|
||||
};
|
||||
|
||||
// Start with the trigger node
|
||||
const triggerNodeId = 'trigger';
|
||||
const triggerNodeId = TRIGGER_STEP_ID;
|
||||
const triggerEvent = splitWorkflowTriggerEventName(
|
||||
trigger.settings.eventName,
|
||||
);
|
||||
nodes.push({
|
||||
id: triggerNodeId,
|
||||
data: {
|
||||
nodeType: 'trigger',
|
||||
label: trigger.settings.eventName,
|
||||
label: `${capitalize(triggerEvent.objectType)} is ${capitalize(triggerEvent.event)}`,
|
||||
},
|
||||
position: {
|
||||
x: 0,
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
import { WorkflowStep, WorkflowStepType } from '@/workflow/types/Workflow';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const getStepDefaultDefinition = (
|
||||
type: WorkflowStepType,
|
||||
): WorkflowStep => {
|
||||
const newStepId = v4();
|
||||
|
||||
switch (type) {
|
||||
case 'CODE_ACTION': {
|
||||
return {
|
||||
id: newStepId,
|
||||
name: 'Code',
|
||||
type: 'CODE_ACTION',
|
||||
valid: false,
|
||||
settings: {
|
||||
serverlessFunctionId: '',
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown type: ${type}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,28 +0,0 @@
|
||||
import { Workflow } from '@/workflow/types/Workflow';
|
||||
import { WorkflowDiagram } from '@/workflow/types/WorkflowDiagram';
|
||||
import { generateWorkflowDiagram } from '@/workflow/utils/generateWorkflowDiagram';
|
||||
import { getWorkflowLastVersion } from '@/workflow/utils/getWorkflowLastVersion';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
const EMPTY_DIAGRAM: WorkflowDiagram = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
};
|
||||
|
||||
export const getWorkflowLastDiagramVersion = (
|
||||
workflow: Workflow | undefined,
|
||||
): WorkflowDiagram => {
|
||||
if (!isDefined(workflow)) {
|
||||
return EMPTY_DIAGRAM;
|
||||
}
|
||||
|
||||
const lastVersion = getWorkflowLastVersion(workflow);
|
||||
if (!isDefined(lastVersion) || !isDefined(lastVersion.trigger)) {
|
||||
return EMPTY_DIAGRAM;
|
||||
}
|
||||
|
||||
return generateWorkflowDiagram({
|
||||
trigger: lastVersion.trigger,
|
||||
steps: lastVersion.steps,
|
||||
});
|
||||
};
|
||||
@ -1,10 +0,0 @@
|
||||
import { Workflow, WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
|
||||
export const getWorkflowLastVersion = (
|
||||
workflow: Workflow,
|
||||
): WorkflowVersion | undefined => {
|
||||
return workflow.versions
|
||||
.slice()
|
||||
.sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1))
|
||||
.at(-1);
|
||||
};
|
||||
@ -0,0 +1,28 @@
|
||||
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
import { WorkflowDiagram } from '@/workflow/types/WorkflowDiagram';
|
||||
import { generateWorkflowDiagram } from '@/workflow/utils/generateWorkflowDiagram';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
const EMPTY_DIAGRAM: WorkflowDiagram = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
};
|
||||
|
||||
export const getWorkflowVersionDiagram = (
|
||||
workflowVersion: WorkflowVersion | undefined,
|
||||
): WorkflowDiagram => {
|
||||
if (
|
||||
!(
|
||||
isDefined(workflowVersion) &&
|
||||
isDefined(workflowVersion.trigger) &&
|
||||
isDefined(workflowVersion.steps)
|
||||
)
|
||||
) {
|
||||
return EMPTY_DIAGRAM;
|
||||
}
|
||||
|
||||
return generateWorkflowDiagram({
|
||||
trigger: workflowVersion.trigger,
|
||||
steps: workflowVersion.steps,
|
||||
});
|
||||
};
|
||||
@ -1,38 +1,5 @@
|
||||
import { WorkflowStep } from '@/workflow/types/Workflow';
|
||||
|
||||
const findStepPositionOrThrow = ({
|
||||
steps,
|
||||
stepId,
|
||||
}: {
|
||||
steps: Array<WorkflowStep>;
|
||||
stepId: string | undefined;
|
||||
}): { steps: Array<WorkflowStep>; index: number } => {
|
||||
if (stepId === undefined) {
|
||||
return {
|
||||
steps,
|
||||
index: 0,
|
||||
};
|
||||
}
|
||||
|
||||
for (const [index, step] of steps.entries()) {
|
||||
if (step.id === stepId) {
|
||||
return {
|
||||
steps,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: When condition will have been implemented, put recursivity here.
|
||||
// if (step.type === "CONDITION") {
|
||||
// return findNodePosition({
|
||||
// workflowSteps: step.conditions,
|
||||
// stepId,
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
throw new Error(`Couldn't locate the step. Unreachable step id: ${stepId}.`);
|
||||
};
|
||||
import { findStepPositionOrThrow } from '@/workflow/utils/findStepPositionOrThrow';
|
||||
|
||||
export const insertStep = ({
|
||||
steps: stepsInitial,
|
||||
@ -43,11 +10,10 @@ export const insertStep = ({
|
||||
parentStepId: string | undefined;
|
||||
stepToAdd: WorkflowStep;
|
||||
}): Array<WorkflowStep> => {
|
||||
// Make a deep copy of the nested object to prevent unwanted side effects.
|
||||
const steps = structuredClone(stepsInitial);
|
||||
|
||||
const parentStepPosition = findStepPositionOrThrow({
|
||||
steps: steps,
|
||||
steps,
|
||||
stepId: parentStepId,
|
||||
});
|
||||
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
import { WorkflowStep } from '@/workflow/types/Workflow';
|
||||
import { findStepPositionOrThrow } from '@/workflow/utils/findStepPositionOrThrow';
|
||||
|
||||
export const replaceStep = ({
|
||||
steps: stepsInitial,
|
||||
stepId,
|
||||
stepToReplace,
|
||||
}: {
|
||||
steps: Array<WorkflowStep>;
|
||||
stepId: string;
|
||||
stepToReplace: Partial<Omit<WorkflowStep, 'id'>>;
|
||||
}) => {
|
||||
const steps = structuredClone(stepsInitial);
|
||||
|
||||
const parentStepPosition = findStepPositionOrThrow({
|
||||
steps,
|
||||
stepId,
|
||||
});
|
||||
|
||||
parentStepPosition.steps[parentStepPosition.index] = {
|
||||
...parentStepPosition.steps[parentStepPosition.index],
|
||||
...stepToReplace,
|
||||
};
|
||||
|
||||
return steps;
|
||||
};
|
||||
@ -0,0 +1,8 @@
|
||||
export const splitWorkflowTriggerEventName = (eventName: string) => {
|
||||
const [objectType, event] = eventName.split('.');
|
||||
|
||||
return {
|
||||
objectType,
|
||||
event,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user