Migrate output schema to V2 (#14311)

Previous refacto was creating output schema V2 which has more specific
schemas based on the step type. Before we were using one common schema,
which was too complex when searching for variable informations.

This PR migrate the deprecated schemas and remove the old code:
- mark previous `BaseOutputSchema` as deprecated
- remove other previous schemas
- use V2 everywhere
- icon should not be stored in schema. Instead it should be generated
based on the fieldmetadata or the item type
This commit is contained in:
Thomas Trompette
2025-09-09 18:37:00 +02:00
committed by GitHub
parent e787dadde8
commit 2b59198a1b
57 changed files with 589 additions and 1256 deletions
@@ -7,7 +7,7 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { type BaseOutputSchemaDeprecated } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil';
@@ -47,7 +47,7 @@ export const WorkflowEditActionAiAgent = ({
});
const { handleOutputSchemaChange, outputFields } = useAiAgentOutputSchema(
action.settings.outputSchema as BaseOutputSchema,
action.settings.outputSchema as BaseOutputSchemaDeprecated,
actionOptions.readonly === true ? undefined : actionOptions.onActionUpdate,
action,
actionOptions.readonly,
@@ -1,7 +1,7 @@
import { OUTPUT_FIELD_TYPE_OPTIONS } from '@/ai/constants/OutputFieldTypeOptions';
import { Select } from '@/ui/input/components/Select';
import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema';
import { t } from '@lingui/core/macro';
import { OUTPUT_FIELD_TYPE_OPTIONS } from '@/ai/constants/OutputFieldTypeOptions';
type WorkflowOutputFieldTypeSelectorProps = {
value?: InputSchemaPropertyType;
@@ -2,9 +2,9 @@ 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 { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
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';
@@ -13,12 +13,13 @@ import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/ste
import { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useUpsertStepFilterSettings';
import { getStepFilterOperands } from '@/workflow/workflow-steps/workflow-actions/filter-action/utils/getStepFilterOperands';
import { useVariableDropdown } from '@/workflow/workflow-variables/hooks/useVariableDropdown';
import { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2';
import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2';
import { extractRawVariableNamePart } from '@/workflow/workflow-variables/utils/extractRawVariableNamePart';
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 { searchVariableThroughOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchema';
import { searchVariableThroughOutputSchemaV2 } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchemaV2';
import { useLingui } from '@lingui/react/macro';
import { useRecoilCallback } from 'recoil';
import { type StepFilter } from 'twenty-shared/types';
@@ -32,7 +33,7 @@ import { MenuItemSelect } from 'twenty-ui/navigation';
type WorkflowDropdownStepOutputItemsProps = {
stepFilter: StepFilter;
step: StepOutputSchema;
step: StepOutputSchemaV2;
onSelect: () => void;
onBack: () => void;
};
@@ -51,6 +52,7 @@ export const WorkflowDropdownStepOutputItems = ({
useGetFieldMetadataItemByIdOrThrow();
const workflowVersionId = useWorkflowVersionIdOrThrow();
const { objectMetadataItems } = useObjectMetadataItems();
const updateStepFilter = useRecoilCallback(
({ snapshot }) =>
@@ -75,8 +77,9 @@ export const WorkflowDropdownStepOutputItems = ({
.getValue();
const { variableType, fieldMetadataId, compositeFieldSubFieldName } =
searchVariableThroughOutputSchema({
searchVariableThroughOutputSchemaV2({
stepOutputSchema: currentStepOutputSchema,
stepType: step.type,
rawVariableName,
isFullRecord: false,
});
@@ -112,6 +115,7 @@ export const WorkflowDropdownStepOutputItems = ({
},
[
workflowVersionId,
step.type,
getFieldMetadataItemByIdOrThrow,
upsertStepFilterSettings,
stepFilter,
@@ -142,7 +146,7 @@ export const WorkflowDropdownStepOutputItems = ({
const getDisplayedSubStepObject = () => {
const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
if (!isRecordOutputSchema(currentSubStep)) {
if (!isRecordOutputSchemaV2(currentSubStep)) {
return;
}
@@ -152,14 +156,14 @@ export const WorkflowDropdownStepOutputItems = ({
const handleSelectObject = () => {
const currentSubStep = getCurrentSubStepFromPath(step, currentPath);
if (!isRecordOutputSchema(currentSubStep)) {
if (!isRecordOutputSchemaV2(currentSubStep)) {
return;
}
updateStepFilter({
rawVariableName: getVariableTemplateFromPath({
stepId: step.id,
path: [...currentPath, currentSubStep.object.fieldIdName],
path: [...currentPath, 'id'],
}),
isFullRecord: true,
});
@@ -168,16 +172,22 @@ export const WorkflowDropdownStepOutputItems = ({
const displayedSubStepObject = getDisplayedSubStepObject();
const subStepObjectMetadataItem = isDefined(
displayedSubStepObject?.objectMetadataId,
)
? objectMetadataItems.find(
(item) => item.id === displayedSubStepObject?.objectMetadataId,
)
: undefined;
const shouldDisplaySubStepObject = searchInputValue
? displayedSubStepObject?.label &&
displayedSubStepObject.label
? isDefined(subStepObjectMetadataItem) &&
subStepObjectMetadataItem.labelSingular
.toLowerCase()
.includes(searchInputValue.toLowerCase())
: true;
const shouldDisplayObject =
shouldDisplaySubStepObject && displayedSubStepObject?.label;
const nameSingular = displayedSubStepObject?.nameSingular;
const objectLabel = subStepObjectMetadataItem?.labelSingular;
return (
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
@@ -200,22 +210,22 @@ export const WorkflowDropdownStepOutputItems = ({
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight>
{shouldDisplayObject && (
{shouldDisplaySubStepObject && (
<MenuItemSelect
selected={false}
focused={false}
onClick={handleSelectObject}
text={displayedSubStepObject?.label || ''}
text={objectLabel || ''}
hasSubMenu={false}
LeftIcon={
displayedSubStepObject.icon
? getIcon(displayedSubStepObject.icon)
subStepObjectMetadataItem?.icon
? getIcon(subStepObjectMetadataItem.icon)
: undefined
}
contextualText={t`Pick a ${nameSingular} record`}
contextualText={t`Pick a ${objectLabel} record`}
/>
)}
{filteredOptions.length > 0 && shouldDisplayObject && (
{filteredOptions.length > 0 && shouldDisplaySubStepObject && (
<DropdownMenuSeparator />
)}
{filteredOptions.map(([key, subStep]) => (
@@ -4,11 +4,11 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { WorkflowDropdownStepOutputItems } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems';
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/context/WorkflowStepFilterContext';
import { WorkflowVariablesDropdownWorkflowStepItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems';
import { WorkflowVariablesDropdownSteps } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownSteps';
import { useAvailableVariablesInWorkflowStep } from '@/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep';
import { useSearchVariable } from '@/workflow/workflow-variables/hooks/useSearchVariable';
import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2';
import { type StepOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { extractRawVariableNamePart } from '@/workflow/workflow-variables/utils/extractRawVariableNamePart';
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
@@ -51,7 +51,7 @@ export const WorkflowStepFilterFieldSelect = ({
: undefined;
const [selectedStep, setSelectedStep] = useState<
StepOutputSchema | undefined
StepOutputSchemaV2 | undefined
>(initialStep);
const stepId = extractRawVariableNamePart({
@@ -137,7 +137,7 @@ export const WorkflowStepFilterFieldSelect = ({
}
dropdownComponents={
!isDefined(selectedStep) ? (
<WorkflowVariablesDropdownWorkflowStepItems
<WorkflowVariablesDropdownSteps
dropdownId={dropdownId}
steps={availableVariablesInWorkflowStep}
onSelect={handleStepSelect}
@@ -1,6 +1,6 @@
import { type WorkflowHttpRequestAction } from '@/workflow/types/Workflow';
import { parseAndValidateVariableFriendlyStringifiedJson } from '@/workflow/utils/parseAndValidateVariableFriendlyStringifiedJson';
import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2';
import { isNonEmptyString } from '@sniptt/guards';
import { useState } from 'react';
import { convertOutputSchemaToJson } from '../utils/convertOutputSchemaToJson';
@@ -21,7 +21,7 @@ export const useHttpRequestOutputSchema = ({
Object.keys(action.settings.outputSchema).length
? JSON.stringify(
convertOutputSchemaToJson(
action.settings.outputSchema as BaseOutputSchema,
action.settings.outputSchema as BaseOutputSchemaV2,
),
null,
2,
@@ -1,29 +1,26 @@
import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2';
import { convertOutputSchemaToJson } from '../convertOutputSchemaToJson';
describe('convertOutputSchemaToJson', () => {
it('should convert simple object schema to JSON', () => {
const schema: BaseOutputSchema = {
const schema: BaseOutputSchemaV2 = {
name: {
isLeaf: true,
type: 'string',
label: 'name',
value: 'John',
icon: 'IconText',
},
age: {
isLeaf: true,
type: 'number',
label: 'age',
value: 25,
icon: 'IconNumber',
},
isActive: {
isLeaf: true,
type: 'boolean',
label: 'isActive',
value: true,
icon: 'IconCheckbox',
},
};
const expected = {
@@ -35,27 +32,24 @@ describe('convertOutputSchemaToJson', () => {
});
it('should convert array schema to JSON array', () => {
const schema: BaseOutputSchema = {
const schema: BaseOutputSchemaV2 = {
'0': {
isLeaf: true,
type: 'string',
label: '0',
value: 'first',
icon: 'IconText',
},
'1': {
isLeaf: true,
type: 'string',
label: '1',
value: 'second',
icon: 'IconText',
},
'2': {
isLeaf: true,
type: 'string',
label: '2',
value: 'third',
icon: 'IconText',
},
};
const expected = ['first', 'second', 'third'];
@@ -63,9 +57,10 @@ describe('convertOutputSchemaToJson', () => {
});
it('should convert nested object schema to JSON', () => {
const schema: BaseOutputSchema = {
const schema: BaseOutputSchemaV2 = {
user: {
isLeaf: false,
type: 'object',
label: 'user',
value: {
name: {
@@ -73,17 +68,16 @@ describe('convertOutputSchemaToJson', () => {
type: 'string',
label: 'name',
value: 'John',
icon: 'IconText',
},
age: {
isLeaf: true,
type: 'number',
label: 'age',
value: 25,
icon: 'IconNumber',
},
address: {
isLeaf: false,
type: 'object',
label: 'address',
value: {
city: {
@@ -91,20 +85,16 @@ describe('convertOutputSchemaToJson', () => {
type: 'string',
label: 'city',
value: 'New York',
icon: 'IconText',
},
country: {
isLeaf: true,
type: 'string',
label: 'country',
value: 'USA',
icon: 'IconText',
},
},
icon: 'IconBox',
},
},
icon: 'IconBox',
},
};
const expected = {
@@ -121,9 +111,10 @@ describe('convertOutputSchemaToJson', () => {
});
it('should convert nested array schema to JSON', () => {
const schema: BaseOutputSchema = {
const schema: BaseOutputSchemaV2 = {
'0': {
isLeaf: false,
type: 'object',
label: '0',
value: {
'0': {
@@ -131,20 +122,18 @@ describe('convertOutputSchemaToJson', () => {
type: 'number',
label: '0',
value: 1,
icon: 'IconNumber',
},
'1': {
isLeaf: true,
type: 'number',
label: '1',
value: 2,
icon: 'IconNumber',
},
},
icon: 'IconBox',
},
'1': {
isLeaf: false,
type: 'object',
label: '1',
value: {
'0': {
@@ -152,17 +141,14 @@ describe('convertOutputSchemaToJson', () => {
type: 'number',
label: '0',
value: 3,
icon: 'IconNumber',
},
'1': {
isLeaf: true,
type: 'number',
label: '1',
value: 4,
icon: 'IconNumber',
},
},
icon: 'IconBox',
},
};
const expected = [
@@ -173,13 +159,15 @@ describe('convertOutputSchemaToJson', () => {
});
it('should handle mixed array and object schema', () => {
const schema: BaseOutputSchema = {
const schema: BaseOutputSchemaV2 = {
users: {
isLeaf: false,
type: 'object',
label: 'users',
value: {
'0': {
isLeaf: false,
type: 'object',
label: '0',
value: {
name: {
@@ -187,20 +175,18 @@ describe('convertOutputSchemaToJson', () => {
type: 'string',
label: 'name',
value: 'John',
icon: 'IconText',
},
age: {
isLeaf: true,
type: 'number',
label: 'age',
value: 25,
icon: 'IconNumber',
},
},
icon: 'IconBox',
},
'1': {
isLeaf: false,
type: 'object',
label: '1',
value: {
name: {
@@ -208,23 +194,20 @@ describe('convertOutputSchemaToJson', () => {
type: 'string',
label: 'name',
value: 'Jane',
icon: 'IconText',
},
age: {
isLeaf: true,
type: 'number',
label: 'age',
value: 30,
icon: 'IconNumber',
},
},
icon: 'IconBox',
},
},
icon: 'IconBox',
},
metadata: {
isLeaf: false,
type: 'object',
label: 'metadata',
value: {
count: {
@@ -232,17 +215,14 @@ describe('convertOutputSchemaToJson', () => {
type: 'number',
label: 'count',
value: 2,
icon: 'IconNumber',
},
active: {
isLeaf: true,
type: 'boolean',
label: 'active',
value: true,
icon: 'IconCheckbox',
},
},
icon: 'IconBox',
},
};
const expected = {
@@ -265,20 +245,18 @@ describe('convertOutputSchemaToJson', () => {
});
it('should handle null values', () => {
const schema: BaseOutputSchema = {
const schema: BaseOutputSchemaV2 = {
name: {
isLeaf: true,
type: 'unknown',
label: 'name',
value: null,
icon: 'IconQuestionMark',
},
age: {
isLeaf: true,
type: 'unknown',
label: 'age',
value: null,
icon: 'IconQuestionMark',
},
};
const expected = {
@@ -289,23 +267,26 @@ describe('convertOutputSchemaToJson', () => {
});
it('should handle empty object schema', () => {
const schema: BaseOutputSchema = {};
const schema: BaseOutputSchemaV2 = {};
const expected = {};
expect(convertOutputSchemaToJson(schema)).toEqual(expected);
});
it('should handle complex nested structure', () => {
const schema: BaseOutputSchema = {
const schema: BaseOutputSchemaV2 = {
data: {
isLeaf: false,
type: 'object',
label: 'data',
value: {
users: {
isLeaf: false,
type: 'object',
label: 'users',
value: {
'0': {
isLeaf: false,
type: 'object',
label: '0',
value: {
id: {
@@ -313,17 +294,16 @@ describe('convertOutputSchemaToJson', () => {
type: 'number',
label: 'id',
value: 1,
icon: 'IconNumber',
},
name: {
isLeaf: true,
type: 'string',
label: 'name',
value: 'John',
icon: 'IconText',
},
roles: {
isLeaf: false,
type: 'object',
label: 'roles',
value: {
'0': {
@@ -331,20 +311,18 @@ describe('convertOutputSchemaToJson', () => {
type: 'string',
label: '0',
value: 'admin',
icon: 'IconText',
},
'1': {
isLeaf: true,
type: 'string',
label: '1',
value: 'user',
icon: 'IconText',
},
},
icon: 'IconBox',
},
settings: {
isLeaf: false,
type: 'object',
label: 'settings',
value: {
theme: {
@@ -352,26 +330,22 @@ describe('convertOutputSchemaToJson', () => {
type: 'string',
label: 'theme',
value: 'dark',
icon: 'IconText',
},
notifications: {
isLeaf: true,
type: 'boolean',
label: 'notifications',
value: true,
icon: 'IconCheckbox',
},
},
icon: 'IconBox',
},
},
icon: 'IconBox',
},
},
icon: 'IconBox',
},
metadata: {
isLeaf: false,
type: 'object',
label: 'metadata',
value: {
total: {
@@ -379,20 +353,16 @@ describe('convertOutputSchemaToJson', () => {
type: 'number',
label: 'total',
value: 1,
icon: 'IconNumber',
},
page: {
isLeaf: true,
type: 'number',
label: 'page',
value: 1,
icon: 'IconNumber',
},
},
icon: 'IconBox',
},
},
icon: 'IconBox',
},
};
const expected = {
@@ -23,14 +23,12 @@ describe('getHttpRequestOutputSchema', () => {
type: 'string',
label: 'name',
value: 'John',
icon: 'IconAbc',
},
email: {
isLeaf: true,
type: 'string',
label: 'email',
value: 'john@example.com',
icon: 'IconAbc',
},
};
expect(getHttpRequestOutputSchema(input)).toEqual(expected);
@@ -44,14 +42,12 @@ describe('getHttpRequestOutputSchema', () => {
type: 'number',
label: 'age',
value: 25,
icon: 'IconText',
},
score: {
isLeaf: true,
type: 'number',
label: 'score',
value: 98.5,
icon: 'IconText',
},
};
expect(getHttpRequestOutputSchema(input)).toEqual(expected);
@@ -65,14 +61,12 @@ describe('getHttpRequestOutputSchema', () => {
type: 'boolean',
label: 'isActive',
value: true,
icon: 'IconCheckbox',
},
isVerified: {
isLeaf: true,
type: 'boolean',
label: 'isVerified',
value: false,
icon: 'IconCheckbox',
},
};
expect(getHttpRequestOutputSchema(input)).toEqual(expected);
@@ -93,44 +87,40 @@ describe('getHttpRequestOutputSchema', () => {
user: {
isLeaf: false,
label: 'user',
type: 'object',
value: {
name: {
isLeaf: true,
type: 'string',
label: 'name',
value: 'John',
icon: 'IconAbc',
},
age: {
isLeaf: true,
type: 'number',
label: 'age',
value: 25,
icon: 'IconText',
},
address: {
isLeaf: false,
label: 'address',
type: 'object',
value: {
city: {
isLeaf: true,
type: 'string',
label: 'city',
value: 'New York',
icon: 'IconAbc',
},
country: {
isLeaf: true,
type: 'string',
label: 'country',
value: 'USA',
icon: 'IconAbc',
},
},
icon: 'IconBox',
},
},
icon: 'IconBox',
},
};
expect(getHttpRequestOutputSchema(input)).toEqual(expected);
@@ -144,74 +134,22 @@ describe('getHttpRequestOutputSchema', () => {
};
const expected = {
tags: {
isLeaf: false,
isLeaf: true,
label: 'tags',
value: {
'0': {
isLeaf: true,
type: 'string',
label: '0',
value: 'tag1',
icon: 'IconAbc',
},
'1': {
isLeaf: true,
type: 'string',
label: '1',
value: 'tag2',
icon: 'IconAbc',
},
},
icon: 'IconBox',
type: 'array',
value: ['tag1', 'tag2'],
},
scores: {
isLeaf: false,
isLeaf: true,
label: 'scores',
value: {
'0': {
isLeaf: true,
type: 'number',
label: '0',
value: 1,
icon: 'IconText',
},
'1': {
isLeaf: true,
type: 'number',
label: '1',
value: 2,
icon: 'IconText',
},
'2': {
isLeaf: true,
type: 'number',
label: '2',
value: 3,
icon: 'IconText',
},
},
icon: 'IconBox',
type: 'array',
value: [1, 2, 3],
},
flags: {
isLeaf: false,
isLeaf: true,
label: 'flags',
value: {
'0': {
isLeaf: true,
type: 'boolean',
label: '0',
value: true,
icon: 'IconCheckbox',
},
'1': {
isLeaf: true,
type: 'boolean',
label: '1',
value: false,
icon: 'IconCheckbox',
},
},
icon: 'IconBox',
type: 'array',
value: [true, false],
},
};
expect(getHttpRequestOutputSchema(input)).toEqual(expected);
@@ -225,14 +163,12 @@ describe('getHttpRequestOutputSchema', () => {
type: 'unknown',
label: 'name',
value: null,
icon: 'IconQuestionMark',
},
age: {
isLeaf: true,
type: 'unknown',
label: 'age',
value: null,
icon: 'IconQuestionMark',
},
};
expect(getHttpRequestOutputSchema(input)).toEqual(expected);
@@ -256,70 +192,49 @@ describe('getHttpRequestOutputSchema', () => {
type: 'string',
label: 'name',
value: 'John',
icon: 'IconAbc',
},
age: {
isLeaf: true,
type: 'number',
label: 'age',
value: 25,
icon: 'IconText',
},
isActive: {
isLeaf: true,
type: 'boolean',
label: 'isActive',
value: true,
icon: 'IconCheckbox',
},
tags: {
isLeaf: false,
isLeaf: true,
label: 'tags',
value: {
'0': {
isLeaf: true,
type: 'string',
label: '0',
value: 'tag1',
icon: 'IconAbc',
},
'1': {
isLeaf: true,
type: 'string',
label: '1',
value: 'tag2',
icon: 'IconAbc',
},
},
icon: 'IconBox',
type: 'array',
value: ['tag1', 'tag2'],
},
address: {
isLeaf: false,
label: 'address',
type: 'object',
value: {
city: {
isLeaf: true,
type: 'string',
label: 'city',
value: 'New York',
icon: 'IconAbc',
},
zip: {
isLeaf: true,
type: 'number',
label: 'zip',
value: 10001,
icon: 'IconText',
},
},
icon: 'IconBox',
},
metadata: {
isLeaf: true,
type: 'unknown',
label: 'metadata',
value: null,
icon: 'IconQuestionMark',
},
};
expect(getHttpRequestOutputSchema(input)).toEqual(expected);
@@ -1,7 +1,7 @@
import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2';
export const convertOutputSchemaToJson = (
schema: BaseOutputSchema,
schema: BaseOutputSchemaV2,
): Record<string, unknown> | unknown[] => {
const keys = Object.keys(schema);
@@ -17,7 +17,7 @@ export const convertOutputSchemaToJson = (
if (entry.isLeaf) {
return entry.value;
}
return convertOutputSchemaToJson(entry.value as BaseOutputSchema);
return convertOutputSchemaToJson(entry.value as BaseOutputSchemaV2);
});
}
@@ -27,7 +27,9 @@ export const convertOutputSchemaToJson = (
if (entry.isLeaf) {
result[key] = entry.value;
} else {
result[key] = convertOutputSchemaToJson(entry.value as BaseOutputSchema);
result[key] = convertOutputSchemaToJson(
entry.value as BaseOutputSchemaV2,
);
}
});
@@ -1,5 +1,5 @@
import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema';
import { type BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
import { type BaseOutputSchemaV2 } from '@/workflow/workflow-variables/types/BaseOutputSchemaV2';
const getValueType = (value: unknown): InputSchemaPropertyType => {
if (value === null || value === undefined) {
@@ -25,12 +25,12 @@ const getValueType = (value: unknown): InputSchemaPropertyType => {
export const getHttpRequestOutputSchema = (
responseData: unknown,
): BaseOutputSchema => {
): BaseOutputSchemaV2 => {
if (typeof responseData !== 'object' || responseData === null) {
return {};
}
const schema: BaseOutputSchema = {};
const schema: BaseOutputSchemaV2 = {};
Object.entries(responseData).forEach(([key, value]) => {
const type = getValueType(value);
@@ -41,7 +41,6 @@ export const getHttpRequestOutputSchema = (
type: 'string',
label: key,
value,
icon: 'IconAbc',
};
break;
case 'number':
@@ -50,7 +49,6 @@ export const getHttpRequestOutputSchema = (
type: 'number',
label: key,
value,
icon: 'IconText',
};
break;
case 'boolean':
@@ -59,16 +57,22 @@ export const getHttpRequestOutputSchema = (
type: 'boolean',
label: key,
value,
icon: 'IconCheckbox',
};
break;
case 'array':
schema[key] = {
isLeaf: true,
label: key,
type: 'array',
value,
};
break;
case 'object':
schema[key] = {
isLeaf: false,
label: key,
value: getHttpRequestOutputSchema(value),
icon: 'IconBox',
type: 'object',
};
break;
case 'unknown':
@@ -78,7 +82,6 @@ export const getHttpRequestOutputSchema = (
type: 'unknown',
label: key,
value: value === null ? null : String(value),
icon: 'IconQuestionMark',
};
break;
}