Set steps output schema in a recoil family state (#10688)
- Create a workflow version component family state for each workflow version : `stepId` => `StepOutputSchema` - Populate this state when reaching the workflow visualizer of the workflow version - Wrap the right drawer when in edit mode with the context. It is the only one who needs this schema Next step: - read this state from the variables
This commit is contained in:
@ -0,0 +1,76 @@
|
|||||||
|
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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
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 { useRecoilCallback } from 'recoil';
|
||||||
|
import { isDefined } from 'twenty-shared';
|
||||||
|
|
||||||
|
export const useStepsOutputSchema = ({
|
||||||
|
instanceIdFromProps,
|
||||||
|
}: {
|
||||||
|
instanceIdFromProps?: string;
|
||||||
|
}) => {
|
||||||
|
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||||
|
WorkflowVersionComponentInstanceContext,
|
||||||
|
instanceIdFromProps,
|
||||||
|
);
|
||||||
|
|
||||||
|
const stepsOutputSchemaFamilyState = useRecoilComponentCallbackStateV2(
|
||||||
|
stepsOutputSchemaComponentFamilyState,
|
||||||
|
instanceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const getStepsOutputSchema = useRecoilCallback(
|
||||||
|
({ snapshot }) =>
|
||||||
|
(stepIds: string[]) => {
|
||||||
|
const stepsOutputSchema = stepIds
|
||||||
|
.map((stepId) =>
|
||||||
|
snapshot
|
||||||
|
.getLoadable(stepsOutputSchemaFamilyState(stepId))
|
||||||
|
.getValue(),
|
||||||
|
)
|
||||||
|
.filter(isDefined);
|
||||||
|
|
||||||
|
return stepsOutputSchema;
|
||||||
|
},
|
||||||
|
[stepsOutputSchemaFamilyState],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { getStepsOutputSchema };
|
||||||
|
};
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||||
|
|
||||||
|
export const WorkflowVersionComponentInstanceContext =
|
||||||
|
createComponentInstanceContext();
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
|
||||||
|
import { WorkflowVersionComponentInstanceContext } from '@/workflow/states/context/WorkflowVersionComponentInstanceContext';
|
||||||
|
import { StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
|
||||||
|
|
||||||
|
export const stepsOutputSchemaComponentFamilyState =
|
||||||
|
createComponentFamilyStateV2<StepOutputSchema | null, string>({
|
||||||
|
key: 'stepsOutputSchemaComponentFamilyState',
|
||||||
|
defaultValue: null,
|
||||||
|
componentInstanceContext: WorkflowVersionComponentInstanceContext,
|
||||||
|
});
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { usePopulateStepsOutputSchema } from '@/workflow/hooks/usePopulateStepsOutputSchema';
|
||||||
|
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
export const WorkflowVersionOutputSchemaEffect = ({
|
||||||
|
workflowVersion,
|
||||||
|
}: {
|
||||||
|
workflowVersion: WorkflowVersion;
|
||||||
|
}) => {
|
||||||
|
const { populateStepsOutputSchema } = usePopulateStepsOutputSchema({
|
||||||
|
workflowVersionId: workflowVersion.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
populateStepsOutputSchema(workflowVersion);
|
||||||
|
}, [populateStepsOutputSchema, workflowVersion]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@ -1,17 +1,22 @@
|
|||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { WorkflowDiagramCanvasEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable';
|
import { WorkflowDiagramCanvasEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable';
|
||||||
import { WorkflowDiagramEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramEffect';
|
import { WorkflowDiagramEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramEffect';
|
||||||
|
import { WorkflowVersionOutputSchemaEffect } from '@/workflow/workflow-diagram/components/WorkflowVersionOutputSchemaEffect';
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
|
|
||||||
export const WorkflowVisualizer = ({ workflowId }: { workflowId: string }) => {
|
export const WorkflowVisualizer = ({ workflowId }: { workflowId: string }) => {
|
||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
const workflowVersion = workflowWithCurrentVersion?.currentVersion;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WorkflowDiagramEffect
|
<WorkflowDiagramEffect
|
||||||
workflowWithCurrentVersion={workflowWithCurrentVersion}
|
workflowWithCurrentVersion={workflowWithCurrentVersion}
|
||||||
/>
|
/>
|
||||||
|
{isDefined(workflowVersion) && (
|
||||||
|
<WorkflowVersionOutputSchemaEffect workflowVersion={workflowVersion} />
|
||||||
|
)}
|
||||||
|
|
||||||
{isDefined(workflowWithCurrentVersion) ? (
|
{isDefined(workflowWithCurrentVersion) ? (
|
||||||
<WorkflowDiagramCanvasEditable
|
<WorkflowDiagramCanvasEditable
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { WorkflowVersionComponentInstanceContext } from '@/workflow/states/context/WorkflowVersionComponentInstanceContext';
|
||||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||||
import { RightDrawerWorkflowEditStepContent } from '@/workflow/workflow-steps/components/RightDrawerWorkflowEditStepContent';
|
import { RightDrawerWorkflowEditStepContent } from '@/workflow/workflow-steps/components/RightDrawerWorkflowEditStepContent';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
@ -12,5 +13,11 @@ export const RightDrawerWorkflowEditStep = () => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <RightDrawerWorkflowEditStepContent workflow={workflow} />;
|
return (
|
||||||
|
<WorkflowVersionComponentInstanceContext.Provider
|
||||||
|
value={{ instanceId: workflow.currentVersion.id }}
|
||||||
|
>
|
||||||
|
<RightDrawerWorkflowEditStepContent workflow={workflow} />
|
||||||
|
</WorkflowVersionComponentInstanceContext.Provider>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,111 +1,64 @@
|
|||||||
import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow';
|
import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema';
|
||||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
|
||||||
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
|
|
||||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
|
||||||
import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow';
|
import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow';
|
||||||
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 {
|
import {
|
||||||
OutputSchema,
|
OutputSchema,
|
||||||
StepOutputSchema,
|
StepOutputSchema,
|
||||||
} from '@/workflow/workflow-variables/types/StepOutputSchema';
|
} from '@/workflow/workflow-variables/types/StepOutputSchema';
|
||||||
import { filterOutputSchema } from '@/workflow/workflow-variables/utils/filterOutputSchema';
|
import { filterOutputSchema } from '@/workflow/workflow-variables/utils/filterOutputSchema';
|
||||||
import { getTriggerStepName } from '@/workflow/workflow-variables/utils/getTriggerStepName';
|
import { isEmptyObject } from '@tiptap/core';
|
||||||
import isEmpty from 'lodash.isempty';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
import { isDefined } from 'twenty-shared';
|
import { isDefined } from 'twenty-shared';
|
||||||
import { isEmptyObject } from '~/utils/isEmptyObject';
|
|
||||||
|
|
||||||
export const useAvailableVariablesInWorkflowStep = ({
|
export const useAvailableVariablesInWorkflowStep = ({
|
||||||
objectNameSingularToSelect,
|
objectNameSingularToSelect,
|
||||||
}: {
|
}: {
|
||||||
objectNameSingularToSelect?: string;
|
objectNameSingularToSelect?: string;
|
||||||
}): StepOutputSchema[] => {
|
}): StepOutputSchema[] => {
|
||||||
const workflowId = useRecoilValue(workflowIdState);
|
|
||||||
const workflow = useWorkflowWithCurrentVersion(workflowId);
|
|
||||||
const workflowSelectedNode = useWorkflowSelectedNodeOrThrow();
|
const workflowSelectedNode = useWorkflowSelectedNodeOrThrow();
|
||||||
const flow = useFlowOrThrow();
|
const flow = useFlowOrThrow();
|
||||||
|
const { getStepsOutputSchema } = useStepsOutputSchema({});
|
||||||
|
|
||||||
if (!isDefined(workflow)) {
|
const steps = flow.steps ?? [];
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const trigger = flow.trigger;
|
const previousStepIds: string[] = [];
|
||||||
const steps = flow.steps;
|
|
||||||
|
|
||||||
const stepDefinition = getStepDefinitionOrThrow({
|
|
||||||
stepId: workflowSelectedNode,
|
|
||||||
trigger,
|
|
||||||
steps,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
!isDefined(stepDefinition) ||
|
|
||||||
stepDefinition.type === 'trigger' ||
|
|
||||||
!isDefined(steps)
|
|
||||||
) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const previousSteps = [];
|
|
||||||
|
|
||||||
for (const step of steps) {
|
for (const step of steps) {
|
||||||
if (step.id === workflowSelectedNode) {
|
if (step.id === workflowSelectedNode) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
previousSteps.push(step);
|
previousStepIds.push(step.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = [];
|
const availableStepsOutputSchema: StepOutputSchema[] =
|
||||||
|
getStepsOutputSchema(previousStepIds).filter(isDefined);
|
||||||
|
|
||||||
const filteredTriggerOutputSchema = filterOutputSchema(
|
const triggersOutputSchema: StepOutputSchema[] = getStepsOutputSchema([
|
||||||
trigger?.settings?.outputSchema as OutputSchema | undefined,
|
TRIGGER_STEP_ID,
|
||||||
objectNameSingularToSelect,
|
]).filter(isDefined);
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
const availableVariablesInWorkflowStep = [
|
||||||
isDefined(trigger) &&
|
...availableStepsOutputSchema,
|
||||||
isDefined(filteredTriggerOutputSchema) &&
|
...triggersOutputSchema,
|
||||||
!isEmptyObject(filteredTriggerOutputSchema)
|
]
|
||||||
) {
|
.map((stepOutputSchema) => {
|
||||||
const triggerIconKey =
|
const outputSchema = filterOutputSchema(
|
||||||
trigger.type === 'DATABASE_EVENT'
|
stepOutputSchema.outputSchema,
|
||||||
? getTriggerIcon({
|
objectNameSingularToSelect,
|
||||||
type: trigger.type,
|
) as OutputSchema;
|
||||||
eventName: splitWorkflowTriggerEventName(
|
|
||||||
trigger.settings?.eventName,
|
|
||||||
).event,
|
|
||||||
})
|
|
||||||
: getTriggerIcon({
|
|
||||||
type: trigger.type,
|
|
||||||
});
|
|
||||||
|
|
||||||
result.push({
|
if (!isDefined(outputSchema) || isEmptyObject(outputSchema)) {
|
||||||
id: 'trigger',
|
return undefined;
|
||||||
name: isDefined(trigger.name)
|
}
|
||||||
? trigger.name
|
|
||||||
: getTriggerStepName(trigger),
|
|
||||||
icon: triggerIconKey,
|
|
||||||
outputSchema: filteredTriggerOutputSchema,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
previousSteps.forEach((previousStep) => {
|
return {
|
||||||
const filteredOutputSchema = filterOutputSchema(
|
id: stepOutputSchema.id,
|
||||||
previousStep.settings.outputSchema as OutputSchema,
|
name: stepOutputSchema.name,
|
||||||
objectNameSingularToSelect,
|
icon: stepOutputSchema.icon,
|
||||||
);
|
outputSchema,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(isDefined);
|
||||||
|
|
||||||
if (isDefined(filteredOutputSchema) && !isEmpty(filteredOutputSchema)) {
|
return availableVariablesInWorkflowStep;
|
||||||
result.push({
|
|
||||||
id: previousStep.id,
|
|
||||||
name: previousStep.name,
|
|
||||||
icon: getActionIcon(previousStep.type),
|
|
||||||
outputSchema: filteredOutputSchema,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user