Compute output schema on frontend (#16530)
Fixes https://github.com/twentyhq/core-team-issues/issues/1382 Current issue : all step output schemas are computed and stored on backend side. Which means that, when the database schema is updated - like a field creation - steps needs to be deleted an recreated. Which is invisible to users. Solution : schema generation is moved on frontend side 1. Coming on the page the first time, the schema is populated for all steps except a few ones that are handled differently (Code, Webhook, http node, Agent) 2. A separated state allow to determine if a step needs a recomputation. 3. The user only needs a refresh to see the whole schema re-computed Follow-up: - check if remaining backend steps could be moved to runtime computation. But Code will still require storage. - Clean backend service that is not used anymore
This commit is contained in:
+156
@@ -0,0 +1,156 @@
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { type WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
import { getStepOutputSchemaFamilyStateKey } from '@/workflow/utils/getStepOutputSchemaFamilyStateKey';
|
||||
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
|
||||
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerDefaultLabel';
|
||||
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
|
||||
import { shouldRecomputeOutputSchemaFamilyState } from '@/workflow/workflow-variables/states/shouldRecomputeOutputSchemaFamilyState';
|
||||
import { stepsOutputSchemaFamilyState } from '@/workflow/workflow-variables/states/stepsOutputSchemaFamilyState';
|
||||
import {
|
||||
type OutputSchemaV2,
|
||||
type StepOutputSchemaV2,
|
||||
} from '@/workflow/workflow-variables/types/StepOutputSchemaV2';
|
||||
import {
|
||||
computeStepOutputSchema,
|
||||
shouldComputeOutputSchemaOnFrontend,
|
||||
} from '@/workflow/workflow-variables/utils/generate/computeStepOutputSchema';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
export const useStepsOutputSchema = () => {
|
||||
const populateStepsOutputSchema = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(workflowVersion: WorkflowVersion) => {
|
||||
const objectMetadataItems = snapshot
|
||||
.getLoadable(objectMetadataItemsState)
|
||||
.getValue();
|
||||
|
||||
workflowVersion.steps?.forEach((step) => {
|
||||
const stepKey = getStepOutputSchemaFamilyStateKey(
|
||||
workflowVersion.id,
|
||||
step.id,
|
||||
);
|
||||
|
||||
const shouldRecompute = snapshot
|
||||
.getLoadable(shouldRecomputeOutputSchemaFamilyState(stepKey))
|
||||
.getValue();
|
||||
|
||||
const shouldComputeOnFrontend = shouldComputeOutputSchemaOnFrontend(
|
||||
step.type,
|
||||
);
|
||||
|
||||
if (!shouldRecompute) {
|
||||
return;
|
||||
}
|
||||
|
||||
const outputSchema = shouldComputeOnFrontend
|
||||
? computeStepOutputSchema({
|
||||
step,
|
||||
objectMetadataItems,
|
||||
})
|
||||
: step.settings?.outputSchema;
|
||||
|
||||
const stepOutputSchema: StepOutputSchemaV2 = {
|
||||
id: step.id,
|
||||
name: step.name,
|
||||
type: step.type,
|
||||
icon: getActionIcon(step.type),
|
||||
outputSchema: (outputSchema ?? {}) as OutputSchemaV2,
|
||||
};
|
||||
|
||||
set(stepsOutputSchemaFamilyState(stepKey), stepOutputSchema);
|
||||
set(shouldRecomputeOutputSchemaFamilyState(stepKey), false);
|
||||
});
|
||||
|
||||
const trigger = workflowVersion.trigger;
|
||||
|
||||
if (isDefined(trigger)) {
|
||||
const triggerKey = getStepOutputSchemaFamilyStateKey(
|
||||
workflowVersion.id,
|
||||
TRIGGER_STEP_ID,
|
||||
);
|
||||
|
||||
const shouldRecompute = snapshot
|
||||
.getLoadable(shouldRecomputeOutputSchemaFamilyState(triggerKey))
|
||||
.getValue();
|
||||
|
||||
const shouldComputeOnFrontend = shouldComputeOutputSchemaOnFrontend(
|
||||
trigger.type,
|
||||
);
|
||||
|
||||
if (!shouldRecompute) {
|
||||
return;
|
||||
}
|
||||
|
||||
const triggerIconKey = getTriggerIcon(trigger);
|
||||
|
||||
const outputSchema = shouldComputeOnFrontend
|
||||
? computeStepOutputSchema({
|
||||
step: trigger,
|
||||
objectMetadataItems,
|
||||
})
|
||||
: trigger.settings?.outputSchema;
|
||||
|
||||
const triggerOutputSchema: StepOutputSchemaV2 = {
|
||||
id: TRIGGER_STEP_ID,
|
||||
name: isDefined(trigger.name)
|
||||
? trigger.name
|
||||
: getTriggerDefaultLabel(trigger),
|
||||
type: trigger.type,
|
||||
icon: triggerIconKey,
|
||||
outputSchema: (outputSchema ?? {}) as OutputSchemaV2,
|
||||
};
|
||||
|
||||
set(stepsOutputSchemaFamilyState(triggerKey), triggerOutputSchema);
|
||||
set(shouldRecomputeOutputSchemaFamilyState(triggerKey), false);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const markStepForRecomputation = useRecoilCallback(
|
||||
({ set }) =>
|
||||
({
|
||||
stepId,
|
||||
workflowVersionId,
|
||||
}: {
|
||||
stepId: string;
|
||||
workflowVersionId: string;
|
||||
}) => {
|
||||
const stepKey = getStepOutputSchemaFamilyStateKey(
|
||||
workflowVersionId,
|
||||
stepId,
|
||||
);
|
||||
set(shouldRecomputeOutputSchemaFamilyState(stepKey), true);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const deleteStepsOutputSchema = useRecoilCallback(
|
||||
({ set }) =>
|
||||
({
|
||||
stepIds,
|
||||
workflowVersionId,
|
||||
}: {
|
||||
stepIds: string[];
|
||||
workflowVersionId: string;
|
||||
}) => {
|
||||
stepIds.forEach((stepId) => {
|
||||
const stepKey = getStepOutputSchemaFamilyStateKey(
|
||||
workflowVersionId,
|
||||
stepId,
|
||||
);
|
||||
set(stepsOutputSchemaFamilyState(stepKey), null);
|
||||
set(shouldRecomputeOutputSchemaFamilyState(stepKey), true);
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return {
|
||||
populateStepsOutputSchema,
|
||||
markStepForRecomputation,
|
||||
deleteStepsOutputSchema,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user