Reset step output schema when step is removed (#10762)

When a step is deleted in a draft version, its variable are still
available in the following steps. This is because step output schema was
not reset. We needed either to refresh or to change version so output
schema gets updated.

This PR:
- migrates to a family state global + context not linked to a component
- add a reset step output schema function
- reset when a step is removed
This commit is contained in:
Thomas Trompette
2025-03-11 17:38:47 +01:00
committed by GitHub
parent 680935e605
commit ddeba39a2c
24 changed files with 242 additions and 215 deletions

View File

@ -1,76 +0,0 @@
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { stepsOutputSchemaComponentFamilyState } from '@/workflow/states/stepsOutputSchemaFamilyState';
import { WorkflowVersion } from '@/workflow/types/Workflow';
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
import {
OutputSchema,
StepOutputSchema,
} from '@/workflow/workflow-variables/types/StepOutputSchema';
import { getTriggerStepName } from '@/workflow/workflow-variables/utils/getTriggerStepName';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared';
export const usePopulateStepsOutputSchema = ({
workflowVersionId,
}: {
workflowVersionId: string;
}) => {
const stepsOutputSchemaFamilyState = useRecoilComponentCallbackStateV2(
stepsOutputSchemaComponentFamilyState,
workflowVersionId,
);
const populateStepsOutputSchema = useRecoilCallback(
({ set }) =>
(workflowVersion: WorkflowVersion) => {
workflowVersion?.steps?.forEach((step) => {
const stepOutputSchema: StepOutputSchema = {
id: step.id,
name: step.name,
icon: getActionIcon(step.type),
outputSchema: step.settings.outputSchema as OutputSchema,
};
set(stepsOutputSchemaFamilyState(step.id), stepOutputSchema);
});
const trigger = workflowVersion.trigger;
if (isDefined(trigger)) {
const triggerIconKey =
trigger.type === 'DATABASE_EVENT'
? getTriggerIcon({
type: trigger.type,
eventName: splitWorkflowTriggerEventName(
trigger.settings?.eventName,
).event,
})
: getTriggerIcon({
type: trigger.type,
});
const triggerOutputSchema: StepOutputSchema = {
id: TRIGGER_STEP_ID,
name: isDefined(trigger.name)
? trigger.name
: getTriggerStepName(trigger),
icon: triggerIconKey,
outputSchema: trigger.settings.outputSchema as OutputSchema,
};
set(
stepsOutputSchemaFamilyState(TRIGGER_STEP_ID),
triggerOutputSchema,
);
}
},
[stepsOutputSchemaFamilyState],
);
return {
populateStepsOutputSchema,
};
};

View File

@ -1,40 +1,97 @@
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { WorkflowVersionComponentInstanceContext } from '@/workflow/states/context/WorkflowVersionComponentInstanceContext';
import { stepsOutputSchemaComponentFamilyState } from '@/workflow/states/stepsOutputSchemaFamilyState';
import { stepsOutputSchemaFamilyState } from '@/workflow/states/stepsOutputSchemaFamilyState';
import { WorkflowVersion } from '@/workflow/types/Workflow';
import { getStepOutputSchemaFamilyStateKey } from '@/workflow/utils/getStepOutputSchemaFamilyStateKey';
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
import {
OutputSchema,
StepOutputSchema,
} from '@/workflow/workflow-variables/types/StepOutputSchema';
import { getTriggerStepName } from '@/workflow/workflow-variables/utils/getTriggerStepName';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared';
export const useStepsOutputSchema = ({
instanceIdFromProps,
}: {
instanceIdFromProps?: string;
}) => {
const instanceId = useAvailableComponentInstanceIdOrThrow(
WorkflowVersionComponentInstanceContext,
instanceIdFromProps,
);
export const useStepsOutputSchema = () => {
const populateStepsOutputSchema = useRecoilCallback(
({ set }) =>
(workflowVersion: WorkflowVersion) => {
workflowVersion.steps?.forEach((step) => {
const stepOutputSchema: StepOutputSchema = {
id: step.id,
name: step.name,
icon: getActionIcon(step.type),
outputSchema: step.settings?.outputSchema as OutputSchema,
};
const stepsOutputSchemaFamilyState = useRecoilComponentCallbackStateV2(
stepsOutputSchemaComponentFamilyState,
instanceId,
);
set(
stepsOutputSchemaFamilyState(
getStepOutputSchemaFamilyStateKey(workflowVersion.id, step.id),
),
stepOutputSchema,
);
});
const getStepsOutputSchema = useRecoilCallback(
({ snapshot }) =>
(stepIds: string[]) => {
const stepsOutputSchema = stepIds
.map((stepId) =>
snapshot
.getLoadable(stepsOutputSchemaFamilyState(stepId))
.getValue(),
)
.filter(isDefined);
const trigger = workflowVersion.trigger;
return stepsOutputSchema;
if (isDefined(trigger)) {
const triggerIconKey =
trigger.type === 'DATABASE_EVENT'
? getTriggerIcon({
type: trigger.type,
eventName: splitWorkflowTriggerEventName(
trigger.settings?.eventName,
).event,
})
: getTriggerIcon({
type: trigger.type,
});
const triggerOutputSchema: StepOutputSchema = {
id: TRIGGER_STEP_ID,
name: isDefined(trigger.name)
? trigger.name
: getTriggerStepName(trigger),
icon: triggerIconKey,
outputSchema: trigger.settings?.outputSchema as OutputSchema,
};
set(
stepsOutputSchemaFamilyState(
getStepOutputSchemaFamilyStateKey(
workflowVersion.id,
TRIGGER_STEP_ID,
),
),
triggerOutputSchema,
);
}
},
[stepsOutputSchemaFamilyState],
[],
);
return { getStepsOutputSchema };
const deleteStepOutputSchema = useRecoilCallback(
({ set }) =>
({
stepId,
workflowVersionId,
}: {
stepId: string;
workflowVersionId: string;
}) => {
set(
stepsOutputSchemaFamilyState(
getStepOutputSchemaFamilyStateKey(workflowVersionId, stepId),
),
null,
);
},
[],
);
return {
populateStepsOutputSchema,
deleteStepOutputSchema,
};
};