From 07cf1ed71d6f1ba2995ee4bddb855f2be3760906 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Thu, 19 Jun 2025 11:33:21 +0200 Subject: [PATCH] Variables not coming from a Record step should be available in Record Picker (#12708) We want code and webhook variables available in Record Picker since those can contains uuid. This PR: - update `WorkflowVariablesDropdownObjectItems.tsx` so it manages fields properly - factorise both dropdown into a commun hook - update filterOutputSchema.ts so it does not filter fields that are not FieldMetadata types - set relation fields as record object in variable schema so those can be selected as full record Before https://github.com/user-attachments/assets/f4f85402-c056-4fd8-8474-d86bef9d4bc3 After https://github.com/user-attachments/assets/c6589e18-7dfa-4fc8-a525-3a580e265896 --- .../WorkflowVariablesDropdownFieldItems.tsx | 107 +++------------ .../WorkflowVariablesDropdownObjectItems.tsx | 75 +++++------ .../hooks/useVariableDropdown.ts | 123 ++++++++++++++++++ .../types/StepOutputSchema.ts | 1 + .../__tests__/filterOutputSchema.test.ts | 6 +- .../utils/filterOutputSchema.ts | 9 ++ .../isFieldTypeCompatibleWithRecordId.ts | 7 + .../searchVariableThroughOutputSchema.ts | 13 +- .../types/output-schema.type.ts | 1 + .../__tests__/generate-fake-field.spec.ts | 2 + .../generate-fake-object-record.spec.ts | 7 +- .../utils/generate-fake-field.ts | 1 + .../utils/generate-fake-form-response.ts | 10 +- .../utils/generate-fake-object-record.ts | 17 ++- .../utils/generate-object-record-fields.ts | 5 +- .../workflow-schema.workspace-service.ts | 4 +- 16 files changed, 229 insertions(+), 159 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-variables/utils/isFieldTypeCompatibleWithRecordId.ts diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx index 0fcd0ebcf..9c8f8f9d3 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx @@ -2,32 +2,19 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { - BaseOutputSchema, - LinkOutputSchema, - StepOutputSchema, -} from '@/workflow/workflow-variables/types/StepOutputSchema'; -import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema'; -import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema'; +import { StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; -import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { workflowDiagramTriggerNodeSelectionComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState'; -import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; -import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath'; +import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel'; -import { isLinkOutputSchema } from '@/workflow/workflow-variables/utils/isLinkOutputSchema'; -import { useState } from 'react'; -import { isDefined } from 'twenty-shared/utils'; import { IconChevronLeft, OverflowingTextWithTooltip, useIcons, } from 'twenty-ui/display'; import { MenuItemSelect } from 'twenty-ui/navigation'; -import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { useVariableDropdown } from '../hooks/useVariableDropdown'; type WorkflowVariablesDropdownFieldItemsProps = { step: StepOutputSchema; @@ -40,83 +27,19 @@ export const WorkflowVariablesDropdownFieldItems = ({ onSelect, onBack, }: WorkflowVariablesDropdownFieldItemsProps) => { - const [currentPath, setCurrentPath] = useState([]); - const [searchInputValue, setSearchInputValue] = useState(''); const { getIcon } = useIcons(); - const setWorkflowSelectedNode = useSetRecoilComponentStateV2( - workflowSelectedNodeComponentState, - ); - const setActiveTabId = useSetRecoilComponentStateV2( - activeTabIdComponentState, - 'workflow-serverless-function-tab-list-component-id', - ); - const setWorkflowDiagramTriggerNodeSelection = useSetRecoilComponentStateV2( - workflowDiagramTriggerNodeSelectionComponentState, - ); - - const getDisplayedSubStepFields = () => { - const currentSubStep = getCurrentSubStepFromPath(step, currentPath); - - if (isLinkOutputSchema(currentSubStep)) { - return { link: currentSubStep.link }; - } else if (isRecordOutputSchema(currentSubStep)) { - return currentSubStep.fields; - } else if (isBaseOutputSchema(currentSubStep)) { - return currentSubStep; - } - }; - - const handleSelectField = (key: string) => { - const currentSubStep = getCurrentSubStepFromPath(step, currentPath); - - const handleSelectBaseOutputSchema = ( - baseOutputSchema: BaseOutputSchema, - ) => { - if (!baseOutputSchema[key]?.isLeaf) { - setCurrentPath([...currentPath, key]); - setSearchInputValue(''); - } else { - onSelect(`{{${step.id}.${[...currentPath, key].join('.')}}}`); - } - }; - - const handleSelectLinkOutputSchema = ( - linkOutputSchema: LinkOutputSchema, - ) => { - setWorkflowSelectedNode(step.id); - setWorkflowDiagramTriggerNodeSelection(step.id); - if (isDefined(linkOutputSchema.link.tab)) { - setActiveTabId(linkOutputSchema.link.tab); - } - }; - - if (isLinkOutputSchema(currentSubStep)) { - handleSelectLinkOutputSchema(currentSubStep); - } else if (isRecordOutputSchema(currentSubStep)) { - handleSelectBaseOutputSchema(currentSubStep.fields); - } else if (isBaseOutputSchema(currentSubStep)) { - handleSelectBaseOutputSchema(currentSubStep); - } - }; - - const goBack = () => { - if (currentPath.length === 0) { - onBack(); - } else { - setCurrentPath(currentPath.slice(0, -1)); - } - }; - - const displayedObject = getDisplayedSubStepFields(); - const options = displayedObject ? Object.entries(displayedObject) : []; - - const filteredOptions = searchInputValue - ? options.filter( - ([_, value]) => - value.label && - value.label.toLowerCase().includes(searchInputValue.toLowerCase()), - ) - : options; + const { + searchInputValue, + setSearchInputValue, + handleSelectField, + goBack, + filteredOptions, + currentPath, + } = useVariableDropdown({ + step, + onSelect, + onBack, + }); return ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems.tsx index a7aaeeb1f..6f0a42724 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems.tsx @@ -5,19 +5,19 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM import { StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath'; import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel'; -import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema'; import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; -import { useState } from 'react'; +import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { t } from '@lingui/core/macro'; import { IconChevronLeft, OverflowingTextWithTooltip, useIcons, } from 'twenty-ui/display'; import { MenuItemSelect } from 'twenty-ui/navigation'; -import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { useVariableDropdown } from '../hooks/useVariableDropdown'; type WorkflowVariablesDropdownObjectItemsProps = { step: StepOutputSchema; @@ -30,19 +30,19 @@ export const WorkflowVariablesDropdownObjectItems = ({ onSelect, onBack, }: WorkflowVariablesDropdownObjectItemsProps) => { - const [currentPath, setCurrentPath] = useState([]); - const [searchInputValue, setSearchInputValue] = useState(''); const { getIcon } = useIcons(); - - const getDisplayedSubStepFields = () => { - const currentSubStep = getCurrentSubStepFromPath(step, currentPath); - - if (isRecordOutputSchema(currentSubStep)) { - return currentSubStep.fields; - } else if (isBaseOutputSchema(currentSubStep)) { - return currentSubStep; - } - }; + const { + currentPath, + filteredOptions, + searchInputValue, + setSearchInputValue, + handleSelectField, + goBack, + } = useVariableDropdown({ + step, + onSelect, + onBack, + }); const getDisplayedSubStepObject = () => { const currentSubStep = getCurrentSubStepFromPath(step, currentPath); @@ -66,19 +66,6 @@ export const WorkflowVariablesDropdownObjectItems = ({ ); }; - const handleSelectField = (key: string) => { - setCurrentPath([...currentPath, key]); - setSearchInputValue(''); - }; - - const goBack = () => { - if (currentPath.length === 0) { - onBack(); - } else { - setCurrentPath(currentPath.slice(0, -1)); - } - }; - const displayedSubStepObject = getDisplayedSubStepObject(); const shouldDisplaySubStepObject = searchInputValue @@ -88,16 +75,9 @@ export const WorkflowVariablesDropdownObjectItems = ({ .includes(searchInputValue.toLowerCase()) : true; - const displayedFields = getDisplayedSubStepFields(); - const options = displayedFields ? Object.entries(displayedFields) : []; - - const filteredOptions = searchInputValue - ? options.filter( - ([_, value]) => - value.label && - value.label.toLowerCase().includes(searchInputValue.toLowerCase()), - ) - : options; + const shouldDisplayObject = + shouldDisplaySubStepObject && displayedSubStepObject?.label; + const nameSingular = displayedSubStepObject?.nameSingular; return ( @@ -120,29 +100,36 @@ export const WorkflowVariablesDropdownObjectItems = ({ /> - {shouldDisplaySubStepObject && displayedSubStepObject?.label && ( + {shouldDisplayObject && ( )} - {filteredOptions.map(([key, value]) => ( + {filteredOptions.length > 0 && shouldDisplayObject && ( + + )} + {filteredOptions.map(([key, option]) => ( handleSelectField(key)} - text={value.label || key} - hasSubMenu={!value.isLeaf} - LeftIcon={value.icon ? getIcon(value.icon) : undefined} + text={option.label || key} + hasSubMenu={!option.isLeaf} + LeftIcon={option.icon ? getIcon(option.icon) : undefined} + contextualText={ + option.isLeaf ? option?.value?.toString() : undefined + } /> ))} diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts new file mode 100644 index 000000000..2c54b485b --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts @@ -0,0 +1,123 @@ +import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { workflowDiagramTriggerNodeSelectionComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; +import { useState } from 'react'; +import { isDefined } from 'twenty-shared/utils'; +import { + BaseOutputSchema, + LinkOutputSchema, + StepOutputSchema, +} from '../types/StepOutputSchema'; +import { getCurrentSubStepFromPath } from '../utils/getCurrentSubStepFromPath'; +import { isBaseOutputSchema } from '../utils/isBaseOutputSchema'; +import { isLinkOutputSchema } from '../utils/isLinkOutputSchema'; +import { isRecordOutputSchema } from '../utils/isRecordOutputSchema'; + +type UseVariableDropdownProps = { + step: StepOutputSchema; + onSelect: (value: string) => void; + onBack: () => void; +}; + +type UseVariableDropdownReturn = { + currentPath: string[]; + searchInputValue: string; + setSearchInputValue: (value: string) => void; + handleSelectField: (key: string) => void; + goBack: () => void; + filteredOptions: [string, any][]; +}; + +export const useVariableDropdown = ({ + step, + onSelect, + onBack, +}: UseVariableDropdownProps): UseVariableDropdownReturn => { + const [currentPath, setCurrentPath] = useState([]); + const [searchInputValue, setSearchInputValue] = useState(''); + + const setWorkflowSelectedNode = useSetRecoilComponentStateV2( + workflowSelectedNodeComponentState, + ); + const setActiveTabId = useSetRecoilComponentStateV2( + activeTabIdComponentState, + 'workflow-serverless-function-tab-list-component-id', + ); + const setWorkflowDiagramTriggerNodeSelection = useSetRecoilComponentStateV2( + workflowDiagramTriggerNodeSelectionComponentState, + ); + + const getDisplayedSubStepFields = () => { + const currentSubStep = getCurrentSubStepFromPath(step, currentPath); + + if (isLinkOutputSchema(currentSubStep)) { + return { link: currentSubStep.link }; + } else if (isRecordOutputSchema(currentSubStep)) { + return currentSubStep.fields; + } else if (isBaseOutputSchema(currentSubStep)) { + return currentSubStep; + } + }; + + const handleSelectField = (key: string) => { + const currentSubStep = getCurrentSubStepFromPath(step, currentPath); + + const handleSelectBaseOutputSchema = ( + baseOutputSchema: BaseOutputSchema, + ) => { + if (!baseOutputSchema[key]?.isLeaf) { + setCurrentPath([...currentPath, key]); + setSearchInputValue(''); + } else { + onSelect(`{{${step.id}.${[...currentPath, key].join('.')}}}`); + } + }; + + const handleSelectLinkOutputSchema = ( + linkOutputSchema: LinkOutputSchema, + ) => { + setWorkflowSelectedNode(step.id); + setWorkflowDiagramTriggerNodeSelection(step.id); + if (isDefined(linkOutputSchema.link.tab)) { + setActiveTabId(linkOutputSchema.link.tab); + } + }; + + if (isLinkOutputSchema(currentSubStep)) { + handleSelectLinkOutputSchema(currentSubStep); + } else if (isRecordOutputSchema(currentSubStep)) { + handleSelectBaseOutputSchema(currentSubStep.fields); + } else if (isBaseOutputSchema(currentSubStep)) { + handleSelectBaseOutputSchema(currentSubStep); + } + }; + + const goBack = () => { + if (currentPath.length === 0) { + onBack(); + } else { + setCurrentPath(currentPath.slice(0, -1)); + } + }; + + const displayedFields = getDisplayedSubStepFields(); + const options = displayedFields ? Object.entries(displayedFields) : []; + + const filteredOptions = searchInputValue + ? options.filter( + ([_, value]) => + value.label && + value.label.toLowerCase().includes(searchInputValue.toLowerCase()), + ) + : options; + + return { + currentPath, + searchInputValue, + setSearchInputValue, + handleSelectField, + goBack, + filteredOptions, + }; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts index ddf98169b..fe3fee273 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts @@ -10,6 +10,7 @@ type Leaf = { type Node = { isLeaf: false; + type?: InputSchemaPropertyType; icon?: string; label?: string; value: OutputSchema; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/filterOutputSchema.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/filterOutputSchema.test.ts index d5295b331..848b0a8f7 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/filterOutputSchema.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/filterOutputSchema.test.ts @@ -1,4 +1,5 @@ import { OutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; +import { FieldMetadataType } from 'twenty-shared/types'; import { filterOutputSchema } from '../filterOutputSchema'; describe('filterOutputSchema', () => { @@ -85,9 +86,10 @@ describe('filterOutputSchema', () => { expect(filterOutputSchema(inputSchema, 'person')).toEqual(expectedSchema); }); - it('should ignore leaf fields', () => { + it('should ignore leaf fields that are field metadata types', () => { const inputSchema = createRecordSchema('company', { name: { isLeaf: true, value: 'string' }, + id: { isLeaf: true, type: FieldMetadataType.UUID }, employee: { isLeaf: false, value: createRecordSchema('person'), @@ -97,6 +99,7 @@ describe('filterOutputSchema', () => { const expectedSchema = { _outputSchemaType: 'RECORD', fields: { + name: { isLeaf: true, value: 'string' }, employee: { isLeaf: false, value: createRecordSchema('person'), @@ -117,6 +120,7 @@ describe('filterOutputSchema', () => { const inputSchema = createBaseSchema({ field1: { isLeaf: true, + type: FieldMetadataType.TEXT, value: 'string', }, }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts index 46fbcc5af..c4d128cc6 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts @@ -4,6 +4,7 @@ import { RecordOutputSchema, } from '@/workflow/workflow-variables/types/StepOutputSchema'; import { isBaseOutputSchema } from '@/workflow/workflow-variables/utils/isBaseOutputSchema'; +import { isFieldTypeCompatibleWithRecordId } from '@/workflow/workflow-variables/utils/isFieldTypeCompatibleWithRecordId'; import { isLinkOutputSchema } from '@/workflow/workflow-variables/utils/isLinkOutputSchema'; import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema'; import { isDefined } from 'twenty-shared/utils'; @@ -33,6 +34,10 @@ const filterRecordOutputSchema = ( const field = outputSchema.fields[key]; if (field.isLeaf) { + if (isFieldTypeCompatibleWithRecordId(field.type)) { + filteredFields[key] = field; + hasValidFields = true; + } continue; } @@ -75,6 +80,10 @@ const filterBaseOutputSchema = ( const field = outputSchema[key]; if (field.isLeaf) { + if (isFieldTypeCompatibleWithRecordId(field.type)) { + filteredSchema[key] = field; + hasValidFields = true; + } continue; } diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isFieldTypeCompatibleWithRecordId.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isFieldTypeCompatibleWithRecordId.ts new file mode 100644 index 000000000..929782559 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/isFieldTypeCompatibleWithRecordId.ts @@ -0,0 +1,7 @@ +import { InputSchemaPropertyType } from '@/workflow/types/InputSchema'; + +export const isFieldTypeCompatibleWithRecordId = ( + type?: InputSchemaPropertyType, +): boolean => { + return !type || type === 'string' || type === 'unknown'; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchema.ts index 7f4385d91..016303693 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/searchVariableThroughOutputSchema.ts @@ -94,12 +94,13 @@ const searchCurrentStepOutputSchema = ({ } return { - variableLabel: isFullRecord - ? getDisplayedSubStepObjectLabel(currentSubStep) - : getDisplayedSubStepFieldLabel( - isSelectedFieldInNextKey ? nextKey : selectedField, - currentSubStep, - ), + variableLabel: + isFullRecord && isRecordOutputSchema(currentSubStep) + ? getDisplayedSubStepObjectLabel(currentSubStep) + : getDisplayedSubStepFieldLabel( + isSelectedFieldInNextKey ? nextKey : selectedField, + currentSubStep, + ), variablePathLabel, }; }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts index e5f365df3..75706b148 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts @@ -11,6 +11,7 @@ export type Leaf = { export type Node = { isLeaf: false; + type?: InputSchemaPropertyType; icon?: string; label?: string; value: OutputSchema; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts index c8ca09615..04de74980 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts @@ -128,6 +128,7 @@ describe('generateFakeField', () => { isLeaf: false, icon: undefined, label: 'Links Field', + type: FieldMetadataType.LINKS, value: { label: { isLeaf: true, @@ -166,6 +167,7 @@ describe('generateFakeField', () => { isLeaf: false, icon: 'IconCurrency', label: 'Currency Field', + type: FieldMetadataType.CURRENCY, value: { amount: { isLeaf: true, diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-object-record.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-object-record.spec.ts index 64b654e1f..fe6240a4a 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-object-record.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-object-record.spec.ts @@ -1,6 +1,6 @@ +import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps'; import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record'; import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields'; -import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps'; jest.mock( 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields', @@ -33,7 +33,7 @@ const objectMetadataInfo = { describe('generateFakeObjectRecord', () => { it('should generate a record with correct object metadata', () => { - const result = generateFakeObjectRecord(objectMetadataInfo); + const result = generateFakeObjectRecord({ objectMetadataInfo }); expect(result).toEqual({ object: { @@ -53,10 +53,11 @@ describe('generateFakeObjectRecord', () => { }); it('should call generateObjectRecordFields with the object metadata', () => { - generateFakeObjectRecord(objectMetadataInfo); + generateFakeObjectRecord({ objectMetadataInfo }); expect(generateObjectRecordFields).toHaveBeenCalledWith({ objectMetadataInfo, + depth: 0, }); }); }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts index e345b3369..76e907106 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts @@ -22,6 +22,7 @@ export const generateFakeField = ({ if (compositeType) { return { isLeaf: false, + type: type, icon: icon, label: label, value: compositeType.properties.reduce((acc, property) => { diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts index 29bbc41d4..40c6fa440 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts @@ -1,5 +1,7 @@ import { isDefined } from 'twenty-shared/utils'; +import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; +import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; import { Leaf, Node, @@ -7,8 +9,6 @@ import { import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field'; import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record'; import { FormFieldMetadata } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type'; -import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; -import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; export const generateFakeFormResponse = async ({ formMetadata, @@ -40,8 +40,10 @@ export const generateFakeFormResponse = async ({ isLeaf: false, label: formFieldMetadata.label, value: generateFakeObjectRecord({ - objectMetadataMaps, - objectMetadataItemWithFieldsMaps, + objectMetadataInfo: { + objectMetadataItemWithFieldsMaps, + objectMetadataMaps, + }, }), }, }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record.ts index e14ae581e..242fa755b 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record.ts @@ -1,10 +1,14 @@ +import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { RecordOutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields'; -import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; -export const generateFakeObjectRecord = ( - objectMetadataInfo: ObjectMetadataInfo, -): RecordOutputSchema => { +export const generateFakeObjectRecord = ({ + objectMetadataInfo, + depth = 0, +}: { + objectMetadataInfo: ObjectMetadataInfo; + depth?: number; +}): RecordOutputSchema => { return { object: { isLeaf: true, @@ -15,7 +19,10 @@ export const generateFakeObjectRecord = ( objectMetadataInfo.objectMetadataItemWithFieldsMaps.nameSingular, fieldIdName: 'id', }, - fields: generateObjectRecordFields({ objectMetadataInfo }), + fields: generateObjectRecordFields({ + objectMetadataInfo, + depth, + }), _outputSchemaType: 'RECORD', }; }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields.ts index a7dc58311..a20536f0e 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields.ts @@ -1,10 +1,11 @@ import { FieldMetadataType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { BaseOutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field'; +import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record'; import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value'; -import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; const MAXIMUM_DEPTH = 1; @@ -45,7 +46,7 @@ export const generateObjectRecordFields = ({ isLeaf: false, icon: field.icon, label: field.label, - value: generateObjectRecordFields({ + value: generateFakeObjectRecord({ objectMetadataInfo: { objectMetadataItemWithFieldsMaps: relationTargetObjectMetadata, objectMetadataMaps: objectMetadataInfo.objectMetadataMaps, diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts index 48d97e6b4..24e09cfc0 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts @@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action'; import { generateFakeValue } from 'src/engine/utils/generate-fake-value'; +import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; import { generateFakeFormResponse } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response'; import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record'; @@ -16,7 +17,6 @@ import { WorkflowTrigger, WorkflowTriggerType, } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; -import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; @Injectable() export class WorkflowSchemaWorkspaceService { @@ -148,7 +148,7 @@ export class WorkflowSchemaWorkspaceService { workspaceId, ); - return generateFakeObjectRecord(objectMetadataInfo); + return generateFakeObjectRecord({ objectMetadataInfo }); } private computeSendEmailActionOutputSchema(): OutputSchema {