Handle relations for filters (#13654)

- Refacto variable dropdown so it can display both objects and fields
- we do not filter on object name anymore to simplify the code
- add relation handler in filters



https://github.com/user-attachments/assets/e4f03f11-45cb-4d3f-b628-e996129dd996
This commit is contained in:
Thomas Trompette
2025-08-06 14:57:10 +02:00
committed by GitHub
parent d74bcbe62b
commit d9dbfc4f7e
21 changed files with 705 additions and 307 deletions
@@ -24,6 +24,8 @@ export const WorkflowStepFilterFieldSelect = ({
stepFilter,
}: WorkflowStepFilterFieldSelectProps) => {
const { readonly } = useContext(WorkflowStepFilterContext);
const shouldDisplayRecordFields = true;
const shouldDisplayRecordObjects = true;
const { upsertStepFilterSettings } = useUpsertStepFilterSettings();
@@ -44,9 +46,10 @@ export const WorkflowStepFilterFieldSelect = ({
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep(
{},
);
const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
});
const noAvailableVariables = availableVariablesInWorkflowStep.length === 0;
@@ -157,6 +160,9 @@ export const WorkflowStepFilterFieldSelect = ({
textAccent={isSelectedFieldNotFound ? 'placeholder' : 'default'}
/>
}
shouldDisplayRecordFields={shouldDisplayRecordFields}
shouldDisplayRecordObjects={shouldDisplayRecordObjects}
shouldEnableSelectRelationObject={true}
/>
);
};
@@ -43,6 +43,7 @@ const isFilterableFieldMetadataType = (
FieldMetadataType.RICH_TEXT_V2,
FieldMetadataType.ARRAY,
FieldMetadataType.UUID,
FieldMetadataType.RELATION,
...COMPOSITE_FIELD_METADATA_TYPES,
].includes(type as FieldMetadataType);
};
@@ -142,6 +143,9 @@ export const WorkflowStepFilterValueInput = ({
metadata: {
fieldName: selectedFieldMetadataItem?.name ?? '',
options: selectedFieldMetadataItem?.options ?? [],
relationObjectMetadataNameSingular:
selectedFieldMetadataItem?.relation?.targetObjectMetadata?.nameSingular,
relationType: selectedFieldMetadataItem?.relation?.type,
} as FieldMetadata,
};
@@ -41,7 +41,8 @@ export const WorkflowVariablePicker: VariablePickerComponent = ({
disabled,
multiline,
onVariableSelect,
objectNameSingularToSelect,
shouldDisplayRecordObjects = false,
shouldDisplayRecordFields = true,
}) => {
return (
<StyledSearchVariablesDropdownContainer
@@ -52,7 +53,8 @@ export const WorkflowVariablePicker: VariablePickerComponent = ({
instanceId={instanceId}
onVariableSelect={onVariableSelect}
disabled={disabled}
objectNameSingularToSelect={objectNameSingularToSelect}
shouldDisplayRecordObjects={shouldDisplayRecordObjects}
shouldDisplayRecordFields={shouldDisplayRecordFields}
multiline={multiline}
/>
</StyledSearchVariablesDropdownContainer>
@@ -3,8 +3,8 @@ import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/S
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { WorkflowVariablesDropdownAllItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownAllItems';
import { WorkflowVariablesDropdownFieldItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems';
import { WorkflowVariablesDropdownObjectItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems';
import { WorkflowVariablesDropdownWorkflowStepItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems';
import { SEARCH_VARIABLES_DROPDOWN_ID } from '@/workflow/workflow-variables/constants/SearchVariablesDropdownId';
@@ -35,14 +35,18 @@ export const WorkflowVariablesDropdown = ({
instanceId,
onVariableSelect,
disabled,
objectNameSingularToSelect,
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
shouldEnableSelectRelationObject,
multiline,
clickableComponent,
}: {
instanceId: string;
onVariableSelect: (variableName: string) => void;
shouldDisplayRecordFields: boolean;
shouldDisplayRecordObjects: boolean;
shouldEnableSelectRelationObject?: boolean;
disabled?: boolean;
objectNameSingularToSelect?: string;
multiline?: boolean;
clickableComponent?: React.ReactNode;
}) => {
@@ -55,7 +59,8 @@ export const WorkflowVariablesDropdown = ({
);
const { closeDropdown } = useCloseDropdown();
const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({
objectNameSingularToSelect,
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
});
const noAvailableVariables = availableVariablesInWorkflowStep.length === 0;
@@ -120,11 +125,12 @@ export const WorkflowVariablesDropdown = ({
steps={availableVariablesInWorkflowStep}
onSelect={handleStepSelect}
/>
) : isDefined(objectNameSingularToSelect) ? (
<WorkflowVariablesDropdownObjectItems
) : shouldDisplayRecordObjects ? (
<WorkflowVariablesDropdownAllItems
step={selectedStep}
onSelect={handleSubItemSelect}
onBack={handleBack}
shouldEnableSelectRelationObject={shouldEnableSelectRelationObject}
/>
) : (
<WorkflowVariablesDropdownFieldItems
@@ -3,14 +3,15 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
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 { 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 { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { t } from '@lingui/core/macro';
import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath';
import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel';
import { getVariableTemplateFromPath } from '@/workflow/workflow-variables/utils/getVariableTemplateFromPath';
import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema';
import { useLingui } from '@lingui/react/macro';
import {
IconChevronLeft,
OverflowingTextWithTooltip,
@@ -19,25 +20,28 @@ import {
import { MenuItemSelect } from 'twenty-ui/navigation';
import { useVariableDropdown } from '../hooks/useVariableDropdown';
type WorkflowVariablesDropdownObjectItemsProps = {
type WorkflowVariablesDropdownAllItemsProps = {
step: StepOutputSchema;
onSelect: (value: string) => void;
onBack: () => void;
shouldEnableSelectRelationObject?: boolean;
};
export const WorkflowVariablesDropdownObjectItems = ({
export const WorkflowVariablesDropdownAllItems = ({
step,
onSelect,
onBack,
}: WorkflowVariablesDropdownObjectItemsProps) => {
shouldEnableSelectRelationObject,
}: WorkflowVariablesDropdownAllItemsProps) => {
const { t } = useLingui();
const { getIcon } = useIcons();
const {
currentPath,
filteredOptions,
searchInputValue,
setSearchInputValue,
handleSelectField,
goBack,
filteredOptions,
currentPath,
} = useVariableDropdown({
step,
onSelect,
@@ -61,9 +65,25 @@ export const WorkflowVariablesDropdownObjectItems = ({
return;
}
onSelect(
`{{${step.id}.${[...currentPath, currentSubStep.object.fieldIdName].join('.')}}}`,
);
const isRelationField = currentSubStep.object.isRelationField ?? false;
const isRelationObjectSelectable =
shouldEnableSelectRelationObject ?? false;
if (isRelationField && isRelationObjectSelectable) {
onSelect(
getVariableTemplateFromPath({
stepId: step.id,
path: currentPath,
}),
);
} else {
onSelect(
getVariableTemplateFromPath({
stepId: step.id,
path: [...currentPath, currentSubStep.object.fieldIdName],
}),
);
}
};
const displayedSubStepObject = getDisplayedSubStepObject();
@@ -118,17 +138,17 @@ export const WorkflowVariablesDropdownObjectItems = ({
{filteredOptions.length > 0 && shouldDisplayObject && (
<DropdownMenuSeparator />
)}
{filteredOptions.map(([key, option]) => (
{filteredOptions.map(([key, subStep]) => (
<MenuItemSelect
key={key}
selected={false}
focused={false}
onClick={() => handleSelectField(key)}
text={option.label || key}
hasSubMenu={!option.isLeaf}
LeftIcon={option.icon ? getIcon(option.icon) : undefined}
text={subStep.label || key}
hasSubMenu={!subStep.isLeaf}
LeftIcon={subStep.icon ? getIcon(subStep.icon) : undefined}
contextualText={
option.isLeaf ? option?.value?.toString() : undefined
subStep.isLeaf ? subStep?.value?.toString() : undefined
}
/>
))}
@@ -50,7 +50,6 @@ export const WorkflowVariablesDropdownFieldItems = ({
Icon={IconChevronLeft}
/>
}
style={{ position: 'fixed' }}
>
<OverflowingTextWithTooltip
text={getStepHeaderLabel(step, currentPath)}
@@ -13,9 +13,11 @@ import { isDefined } from 'twenty-shared/utils';
import { isEmptyObject } from '~/utils/isEmptyObject';
export const useAvailableVariablesInWorkflowStep = ({
objectNameSingularToSelect,
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
}: {
objectNameSingularToSelect?: string;
shouldDisplayRecordFields: boolean;
shouldDisplayRecordObjects: boolean;
}): StepOutputSchema[] => {
const workflowSelectedNode = useWorkflowSelectedNodeOrThrow();
const flow = useFlowOrThrow();
@@ -35,10 +37,11 @@ export const useAvailableVariablesInWorkflowStep = ({
const availableVariablesInWorkflowStep = availableStepsOutputSchema
.map((stepOutputSchema) => {
const outputSchema = filterOutputSchema(
stepOutputSchema.outputSchema,
objectNameSingularToSelect,
) as OutputSchema;
const outputSchema = filterOutputSchema({
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
outputSchema: stepOutputSchema.outputSchema,
}) as OutputSchema;
if (!isDefined(outputSchema) || isEmptyObject(outputSchema)) {
return undefined;
@@ -2,6 +2,7 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { workflowDiagramTriggerNodeSelectionComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState';
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import { getVariableTemplateFromPath } from '@/workflow/workflow-variables/utils/getVariableTemplateFromPath';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import {
@@ -70,7 +71,12 @@ export const useVariableDropdown = ({
setCurrentPath([...currentPath, key]);
setSearchInputValue('');
} else {
onSelect(`{{${step.id}.${[...currentPath, key].join('.')}}}`);
onSelect(
getVariableTemplateFromPath({
stepId: step.id,
path: [...currentPath, key],
}),
);
}
};
@@ -36,6 +36,7 @@ export type RecordOutputSchema = {
nameSingular: string;
fieldIdName: string;
objectMetadataId: string;
isRelationField?: boolean;
} & Leaf;
fields: BaseOutputSchema;
_outputSchemaType: 'RECORD';
@@ -3,189 +3,226 @@ import { FieldMetadataType } from 'twenty-shared/types';
import { filterOutputSchema } from '../filterOutputSchema';
describe('filterOutputSchema', () => {
describe('edge cases', () => {
it('should return the input schema when objectNameSingularToSelect is undefined', () => {
const inputSchema: OutputSchema = {
_outputSchemaType: 'RECORD',
object: {
nameSingular: 'person',
fieldIdName: 'id',
isLeaf: true,
value: 'Fake value',
objectMetadataId: '123',
},
fields: {},
};
const createRecordSchema = (
nameSingular: string,
fields = {},
): OutputSchema => ({
_outputSchemaType: 'RECORD',
object: {
nameSingular,
fieldIdName: 'id',
isLeaf: true,
value: 'Fake value',
objectMetadataId: '123',
},
fields,
});
expect(filterOutputSchema(inputSchema, undefined)).toBe(inputSchema);
const createBaseSchema = (fields = {}): OutputSchema => ({
...fields,
});
describe('shouldDisplayRecordFields only (true, false)', () => {
describe('record schema', () => {
it('should return the input schema unchanged', () => {
const inputSchema = createRecordSchema('person', {
name: { isLeaf: true, value: 'string' },
id: { isLeaf: true, type: FieldMetadataType.UUID },
});
expect(
filterOutputSchema({
shouldDisplayRecordFields: true,
shouldDisplayRecordObjects: false,
outputSchema: inputSchema,
}),
).toBe(inputSchema);
});
it('should return undefined when input schema is undefined', () => {
expect(
filterOutputSchema({
shouldDisplayRecordFields: true,
shouldDisplayRecordObjects: false,
outputSchema: undefined,
}),
).toBeUndefined();
});
});
it('should return undefined when input schema is undefined', () => {
expect(filterOutputSchema(undefined, 'person')).toBeUndefined();
describe('base schema', () => {
it('should return the input schema unchanged', () => {
const inputSchema = createBaseSchema({
field1: { isLeaf: true, value: 'string' },
field2: { isLeaf: true, type: FieldMetadataType.NUMBER },
});
expect(
filterOutputSchema({
shouldDisplayRecordFields: true,
shouldDisplayRecordObjects: false,
outputSchema: inputSchema,
}),
).toBe(inputSchema);
});
});
});
describe('record output schema', () => {
const createRecordSchema = (
nameSingular: string,
fields = {},
): OutputSchema => ({
_outputSchemaType: 'RECORD',
object: {
nameSingular,
fieldIdName: 'id',
isLeaf: true,
value: 'Fake value',
objectMetadataId: '123',
},
fields,
});
it('should keep a matching record schema', () => {
const inputSchema = createRecordSchema('person');
expect(filterOutputSchema(inputSchema, 'person')).toEqual(inputSchema);
});
it('should filter out a non-matching record schema with no valid fields', () => {
const inputSchema = createRecordSchema('company');
expect(filterOutputSchema(inputSchema, 'person')).toBeUndefined();
});
it('should keep valid nested records while filtering out invalid ones', () => {
const inputSchema = createRecordSchema('company', {
employee: {
isLeaf: false,
value: createRecordSchema('person', {
manager: {
isLeaf: false,
value: createRecordSchema('person'),
},
}),
},
department: {
isLeaf: false,
value: createRecordSchema('department'),
},
});
const expectedSchema = {
_outputSchemaType: 'RECORD',
fields: {
describe('shouldDisplayRecordObjects only (false, true)', () => {
describe('record schema', () => {
it('should keep record schema with object and filter compatible fields', () => {
const inputSchema = createRecordSchema('person', {
name: { isLeaf: true, value: 'string' },
id: { isLeaf: true, type: FieldMetadataType.UUID },
employee: {
isLeaf: false,
value: createRecordSchema('person', {
manager: {
isLeaf: false,
value: createRecordSchema('person'),
},
}),
value: createRecordSchema('employee'),
},
},
};
});
expect(filterOutputSchema(inputSchema, 'person')).toEqual(expectedSchema);
});
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'),
},
});
const expectedSchema = {
_outputSchemaType: 'RECORD',
fields: {
const expectedSchema = createRecordSchema('person', {
name: { isLeaf: true, value: 'string' },
employee: {
isLeaf: false,
value: createRecordSchema('employee'),
},
});
expect(
filterOutputSchema({
shouldDisplayRecordFields: false,
shouldDisplayRecordObjects: true,
outputSchema: inputSchema,
}),
).toEqual(expectedSchema);
});
it('should return undefined for record schema without object and no valid fields', () => {
const inputSchema = {
_outputSchemaType: 'RECORD',
fields: {
invalidField: { isLeaf: true, type: FieldMetadataType.NUMBER },
},
} as any;
expect(
filterOutputSchema({
shouldDisplayRecordFields: false,
shouldDisplayRecordObjects: true,
outputSchema: inputSchema,
}),
).toBeUndefined();
});
});
describe('base schema', () => {
it('should keep base schema with valid nested records', () => {
const inputSchema = createBaseSchema({
field1: {
isLeaf: false,
value: createRecordSchema('person'),
},
},
};
field2: { isLeaf: true, type: FieldMetadataType.NUMBER },
});
expect(filterOutputSchema(inputSchema, 'person')).toEqual(expectedSchema);
const expectedSchema = {
field1: {
isLeaf: false,
value: createRecordSchema('person'),
},
};
expect(
filterOutputSchema({
shouldDisplayRecordFields: false,
shouldDisplayRecordObjects: true,
outputSchema: inputSchema,
}),
).toEqual(expectedSchema);
});
it('should return undefined for base schema with no valid records', () => {
const inputSchema = createBaseSchema({
field1: { isLeaf: true, type: FieldMetadataType.NUMBER },
field2: { isLeaf: true, type: FieldMetadataType.BOOLEAN },
});
expect(
filterOutputSchema({
shouldDisplayRecordFields: false,
shouldDisplayRecordObjects: true,
outputSchema: inputSchema,
}),
).toBeUndefined();
});
});
});
describe('base output schema', () => {
const createBaseSchema = (fields = {}): OutputSchema => ({
...fields,
describe('both shouldDisplayRecordFields and shouldDisplayRecordObjects (true, true)', () => {
it('should return the input schema unchanged for record schema', () => {
const inputSchema = createRecordSchema('person', {
name: { isLeaf: true, value: 'string' },
id: { isLeaf: true, type: FieldMetadataType.UUID },
});
expect(
filterOutputSchema({
shouldDisplayRecordFields: true,
shouldDisplayRecordObjects: true,
outputSchema: inputSchema,
}),
).toBe(inputSchema);
});
it('should filter out base schema with no valid records', () => {
it('should return the input schema unchanged for base schema', () => {
const inputSchema = createBaseSchema({
field1: {
isLeaf: true,
type: FieldMetadataType.TEXT,
value: 'string',
},
field1: { isLeaf: true, value: 'string' },
field2: { isLeaf: true, type: FieldMetadataType.NUMBER },
});
expect(filterOutputSchema(inputSchema, 'person')).toBeUndefined();
expect(
filterOutputSchema({
shouldDisplayRecordFields: true,
shouldDisplayRecordObjects: true,
outputSchema: inputSchema,
}),
).toBe(inputSchema);
});
it('should keep base schema with valid nested records', () => {
const inputSchema = createBaseSchema({
field1: {
isLeaf: false,
value: {
_outputSchemaType: 'RECORD',
object: { nameSingular: 'person' },
fields: {},
},
},
it('should return undefined when input schema is undefined', () => {
expect(
filterOutputSchema({
shouldDisplayRecordFields: true,
shouldDisplayRecordObjects: true,
outputSchema: undefined,
}),
).toBeUndefined();
});
});
describe('both shouldDisplayRecordFields and shouldDisplayRecordObjects false (false, false)', () => {
it('should return the input schema unchanged', () => {
const inputSchema = createRecordSchema('person', {
name: { isLeaf: true, value: 'string' },
});
expect(filterOutputSchema(inputSchema, 'person')).toEqual({
field1: {
isLeaf: false,
value: {
_outputSchemaType: 'RECORD',
object: { nameSingular: 'person' },
fields: {},
},
},
});
expect(
filterOutputSchema({
shouldDisplayRecordFields: false,
shouldDisplayRecordObjects: false,
outputSchema: inputSchema,
}),
).toBe(inputSchema);
});
it('should handle deeply nested valid records', () => {
const inputSchema = createBaseSchema({
level1: {
isLeaf: false,
value: createBaseSchema({
level2: {
isLeaf: false,
value: {
_outputSchemaType: 'RECORD',
object: { nameSingular: 'person' },
fields: {},
},
},
}),
},
});
expect(filterOutputSchema(inputSchema, 'person')).toEqual({
level1: {
isLeaf: false,
value: {
level2: {
isLeaf: false,
value: {
_outputSchemaType: 'RECORD',
object: { nameSingular: 'person' },
fields: {},
},
},
},
},
});
it('should return undefined when input schema is undefined', () => {
expect(
filterOutputSchema({
shouldDisplayRecordFields: false,
shouldDisplayRecordObjects: false,
outputSchema: undefined,
}),
).toBeUndefined();
});
});
});
@@ -0,0 +1,21 @@
import { getVariableTemplateFromPath } from '@/workflow/workflow-variables/utils/getVariableTemplateFromPath';
describe('getVariableTemplateFromPath', () => {
it('should return stepId template when path is empty', () => {
const result = getVariableTemplateFromPath({
stepId: 'step-1',
path: [],
});
expect(result).toBe('{{step-1}}');
});
it('should return stepId with path', () => {
const result = getVariableTemplateFromPath({
stepId: 'step-2',
path: ['company', 'name'],
});
expect(result).toBe('{{step-2.company.name}}');
});
});
@@ -9,24 +9,31 @@ import { isLinkOutputSchema } from '@/workflow/workflow-variables/utils/isLinkOu
import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isRecordOutputSchema';
import { isDefined } from 'twenty-shared/utils';
const isValidRecordOutputSchema = (
outputSchema: RecordOutputSchema,
objectNameSingularToSelect?: string,
): boolean => {
if (isDefined(objectNameSingularToSelect)) {
return (
isDefined(outputSchema.object) &&
outputSchema.object.nameSingular === objectNameSingularToSelect
);
const isValidRecordOutputSchema = ({
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
outputSchema,
}: {
shouldDisplayRecordFields: boolean;
shouldDisplayRecordObjects: boolean;
outputSchema: RecordOutputSchema;
}): boolean => {
if (shouldDisplayRecordObjects && !shouldDisplayRecordFields) {
return isDefined(outputSchema.object);
}
return true;
};
const filterRecordOutputSchema = (
outputSchema: RecordOutputSchema,
objectNameSingularToSelect: string,
): RecordOutputSchema | undefined => {
const filterRecordOutputSchema = ({
outputSchema,
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
}: {
outputSchema: RecordOutputSchema;
shouldDisplayRecordFields: boolean;
shouldDisplayRecordObjects: boolean;
}): RecordOutputSchema | undefined => {
const filteredFields: BaseOutputSchema = {};
let hasValidFields = false;
@@ -41,10 +48,12 @@ const filterRecordOutputSchema = (
continue;
}
const validSubSchema = filterOutputSchema(
field.value,
objectNameSingularToSelect,
);
const validSubSchema = filterOutputSchema({
outputSchema: field.value,
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
});
if (isDefined(validSubSchema)) {
filteredFields[key] = {
...field,
@@ -54,7 +63,13 @@ const filterRecordOutputSchema = (
}
}
if (isValidRecordOutputSchema(outputSchema, objectNameSingularToSelect)) {
if (
isValidRecordOutputSchema({
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
outputSchema,
})
) {
return {
...outputSchema,
fields: filteredFields,
@@ -69,10 +84,15 @@ const filterRecordOutputSchema = (
return undefined;
};
const filterBaseOutputSchema = (
outputSchema: BaseOutputSchema,
objectNameSingularToSelect: string,
): BaseOutputSchema | undefined => {
const filterBaseOutputSchema = ({
outputSchema,
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
}: {
outputSchema: BaseOutputSchema;
shouldDisplayRecordFields: boolean;
shouldDisplayRecordObjects: boolean;
}): BaseOutputSchema | undefined => {
const filteredSchema: BaseOutputSchema = {};
let hasValidFields = false;
@@ -87,10 +107,11 @@ const filterBaseOutputSchema = (
continue;
}
const validSubSchema = filterOutputSchema(
field.value,
objectNameSingularToSelect,
);
const validSubSchema = filterOutputSchema({
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
outputSchema: field.value,
});
if (isDefined(validSubSchema)) {
filteredSchema[key] = {
...field,
@@ -107,20 +128,37 @@ const filterBaseOutputSchema = (
return undefined;
};
export const filterOutputSchema = (
outputSchema?: OutputSchema,
objectNameSingularToSelect?: string,
): OutputSchema | undefined => {
if (!objectNameSingularToSelect || !outputSchema) {
export const filterOutputSchema = ({
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
outputSchema,
}: {
shouldDisplayRecordFields: boolean;
shouldDisplayRecordObjects: boolean;
outputSchema?: OutputSchema;
}): OutputSchema | undefined => {
if (
!shouldDisplayRecordObjects ||
shouldDisplayRecordFields ||
!outputSchema
) {
return outputSchema;
}
if (isLinkOutputSchema(outputSchema)) {
return outputSchema;
} else if (isRecordOutputSchema(outputSchema)) {
return filterRecordOutputSchema(outputSchema, objectNameSingularToSelect);
return filterRecordOutputSchema({
outputSchema,
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
});
} else if (isBaseOutputSchema(outputSchema)) {
return filterBaseOutputSchema(outputSchema, objectNameSingularToSelect);
return filterBaseOutputSchema({
outputSchema,
shouldDisplayRecordFields,
shouldDisplayRecordObjects,
});
}
return undefined;
@@ -0,0 +1,13 @@
export const getVariableTemplateFromPath = ({
stepId,
path,
}: {
stepId: string;
path: string[];
}) => {
if (path.length === 0) {
return `{{${stepId}}}`;
}
return `{{${stepId}.${path.join('.')}}}`;
};